From 8d636fb61e24065818043fbf567fd6d874132d9e Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Thu, 25 Oct 2018 11:32:32 -0700 Subject: [PATCH 01/54] Remove the Source CRD, as decided in https://github.com/knative/eventing/pull/482. (#559) --- cmd/webhook/main.go | 1 - config/300-source.yaml | 29 ---- pkg/apis/eventing/v1alpha1/source_defaults.go | 27 --- .../eventing/v1alpha1/source_defaults_test.go | 27 --- pkg/apis/eventing/v1alpha1/source_types.go | 156 ----------------- .../eventing/v1alpha1/source_types_test.go | 142 ---------------- .../eventing/v1alpha1/source_validation.go | 43 ----- .../v1alpha1/source_validation_test.go | 110 ------------ .../v1alpha1/zz_generated.deepcopy.go | 128 -------------- .../eventing/v1alpha1/eventing_client.go | 5 - .../v1alpha1/fake/fake_eventing_client.go | 4 - .../eventing/v1alpha1/fake/fake_source.go | 128 -------------- .../eventing/v1alpha1/generated_expansion.go | 2 - .../typed/eventing/v1alpha1/source.go | 157 ------------------ .../eventing/v1alpha1/interface.go | 7 - .../eventing/v1alpha1/source.go | 89 ---------- .../informers/externalversions/generic.go | 2 - .../eventing/v1alpha1/expansion_generated.go | 8 - .../listers/eventing/v1alpha1/source.go | 94 ----------- pkg/controller/owner_references.go | 2 - 20 files changed, 1161 deletions(-) delete mode 100644 config/300-source.yaml delete mode 100644 pkg/apis/eventing/v1alpha1/source_defaults.go delete mode 100644 pkg/apis/eventing/v1alpha1/source_defaults_test.go delete mode 100644 pkg/apis/eventing/v1alpha1/source_types.go delete mode 100644 pkg/apis/eventing/v1alpha1/source_types_test.go delete mode 100644 pkg/apis/eventing/v1alpha1/source_validation.go delete mode 100644 pkg/apis/eventing/v1alpha1/source_validation_test.go delete mode 100644 pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_source.go delete mode 100644 pkg/client/clientset/versioned/typed/eventing/v1alpha1/source.go delete mode 100644 pkg/client/informers/externalversions/eventing/v1alpha1/source.go delete mode 100644 pkg/client/listers/eventing/v1alpha1/source.go diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index 8388bb45502..aefd15caff0 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -92,7 +92,6 @@ func main() { // For group eventing.knative.dev, eventingv1alpha1.SchemeGroupVersion.WithKind("Channel"): &eventingv1alpha1.Channel{}, eventingv1alpha1.SchemeGroupVersion.WithKind("ClusterProvisioner"): &eventingv1alpha1.ClusterProvisioner{}, - eventingv1alpha1.SchemeGroupVersion.WithKind("Source"): &eventingv1alpha1.Source{}, eventingv1alpha1.SchemeGroupVersion.WithKind("Subscription"): &eventingv1alpha1.Subscription{}, // For group channels.knative.dev, diff --git a/config/300-source.yaml b/config/300-source.yaml deleted file mode 100644 index 6e63f85dbfc..00000000000 --- a/config/300-source.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: sources.eventing.knative.dev -spec: - group: eventing.knative.dev - version: v1alpha1 - names: - kind: Source - plural: sources - singular: source - categories: - - all - - knative - - eventing - scope: Namespaced diff --git a/pkg/apis/eventing/v1alpha1/source_defaults.go b/pkg/apis/eventing/v1alpha1/source_defaults.go deleted file mode 100644 index 9d417f8e35a..00000000000 --- a/pkg/apis/eventing/v1alpha1/source_defaults.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -// SetDefaults defaults -func (s *Source) SetDefaults() { - s.Spec.SetDefaults() -} - -// SetDefaults defaults the Source spec. -func (ss *SourceSpec) SetDefaults() { - // no defaults -} diff --git a/pkg/apis/eventing/v1alpha1/source_defaults_test.go b/pkg/apis/eventing/v1alpha1/source_defaults_test.go deleted file mode 100644 index 2373f9a70c8..00000000000 --- a/pkg/apis/eventing/v1alpha1/source_defaults_test.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "testing" -) - -// No-op test because method does nothing. -func TestSourceSetDefaults(t *testing.T) { - s := Source{} - s.SetDefaults() -} diff --git a/pkg/apis/eventing/v1alpha1/source_types.go b/pkg/apis/eventing/v1alpha1/source_types.go deleted file mode 100644 index 5b58b416b95..00000000000 --- a/pkg/apis/eventing/v1alpha1/source_types.go +++ /dev/null @@ -1,156 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/knative/pkg/apis/duck" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" - "github.com/knative/pkg/webhook" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// +genclient -// +genclient:noStatus -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Source resource Describes a specific configuration (credentials, etc) of a -// source system which can be used to supply events. Sources emit events using a -// channel specified in their status. They cannot receive events. -type Source struct { - metav1.TypeMeta `json:",inline"` - // +optional - metav1.ObjectMeta `json:"metadata,omitempty"` - - // Spec defines the the Provisioner and arguments provided for this Source. - Spec SourceSpec `json:"spec"` - - // Status is the current status of the Source. - // +optional - Status SourceStatus `json:"status,omitempty"` -} - -// Check that Source can be validated and can be defaulted. -var _ webhook.GenericCRD = (*Source)(nil) - -// Check that Source implements the Conditions duck type. -var _ = duck.VerifyType(&Source{}, &duckv1alpha1.Conditions{}) - -// Check that Source implements the Generation duck type. -var emptyGenSource duckv1alpha1.Generation -var _ = duck.VerifyType(&Source{}, &emptyGenSource) - -// And it's Subscribable -var _ = duck.VerifyType(&Subscription{}, &duckv1alpha1.Subscribable{}) - -// SourceSpec is the spec for a Source resource. -type SourceSpec struct { - // TODO: Generation does not work correctly with CRD. They are scrubbed - // by the APIserver (https://github.com/kubernetes/kubernetes/issues/58778) - // So, we add Generation here. Once that gets fixed, remove this and use - // ObjectMeta.Generation instead. - // +optional - Generation int64 `json:"generation,omitempty"` - - // Provisioner is used to create any backing resources and configuration. - // +required - Provisioner *ProvisionerReference `json:"provisioner,omitempty"` - - // Arguments defines the arguments to pass to the Provisioner which provisions - // this Source. - // +optional - Arguments *runtime.RawExtension `json:"arguments,omitempty"` - - // Specify an existing channel to use to emit events. If empty, create a new - // Channel using the cluster/namespace default. - // - // This object must fulfill the Channelable contract. - // - // You can specify only the following fields of the ObjectReference: - // - Kind - // - APIVersion - // - Name - // Currently Kind must be "Channel" and - // APIVersion must be "eventing.knative.dev/v1alpha1" - // +optional - Channel *corev1.ObjectReference `json:"target,omitempty"` -} - -const ( - // SourceConditionReady has status True when the Source is ready to send events. - SourceConditionReady = duckv1alpha1.ConditionReady - - // SourceConditionProvisioned has status True when the Source's backing - // resources have been provisioned. - SourceConditionProvisioned duckv1alpha1.ConditionType = "Provisioned" -) - -var sourceCondSet = duckv1alpha1.NewLivingConditionSet(SourceConditionProvisioned) - -// SourceStatus is the status for a Source resource -type SourceStatus struct { - // Conditions holds the state of a source at a point in time. - // +optional - // +patchMergeKey=type - // +patchStrategy=merge - Conditions duckv1alpha1.Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` - - // ObservedGeneration is the 'Generation' of the Source that - // was last reconciled by the controller. - // +optional - ObservedGeneration int64 `json:"observedGeneration,omitempty"` - - // Source might be Subscribable. This points to the Channelable object. - Subscribable duckv1alpha1.Subscribable `json:"subscribable,omitempty"` -} - -// GetCondition returns the condition currently associated with the given type, or nil. -func (ss *SourceStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { - return sourceCondSet.Manage(ss).GetCondition(t) -} - -// IsReady returns true if the resource is ready overall. -func (ss *SourceStatus) IsReady() bool { - return sourceCondSet.Manage(ss).IsHappy() -} - -// InitializeConditions sets relevant unset conditions to Unknown state. -func (ss *SourceStatus) InitializeConditions() { - sourceCondSet.Manage(ss).InitializeConditions() -} - -// MarkProvisioned sets the condition that the source has had its backing resources created. -func (ss *SourceStatus) MarkProvisioned() { - sourceCondSet.Manage(ss).MarkTrue(SourceConditionProvisioned) -} - -// MarkDeprovisioned sets the condition that the source has had its backing resources removed. -func (ss *SourceStatus) MarkDeprovisioned(reason, messageFormat string, messageA ...interface{}) { - sourceCondSet.Manage(ss).MarkFalse(SourceConditionProvisioned, reason, messageFormat, messageA...) -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// SourceList is a list of Source resources -type SourceList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []Source `json:"items"` -} diff --git a/pkg/apis/eventing/v1alpha1/source_types_test.go b/pkg/apis/eventing/v1alpha1/source_types_test.go deleted file mode 100644 index 380e0c98324..00000000000 --- a/pkg/apis/eventing/v1alpha1/source_types_test.go +++ /dev/null @@ -1,142 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "testing" - - "github.com/google/go-cmp/cmp" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" - corev1 "k8s.io/api/core/v1" -) - -func TestSourceStatusIsReady(t *testing.T) { - tests := []struct { - name string - s *SourceStatus - want bool - }{{ - name: "uninitialized", - s: &SourceStatus{}, - want: false, - }, { - name: "initialized", - s: func() *SourceStatus { - s := &SourceStatus{} - s.InitializeConditions() - return s - }(), - want: false, - }, { - name: "ready true condition", - s: &SourceStatus{ - Conditions: []duckv1alpha1.Condition{{ - Type: SourceConditionReady, - Status: corev1.ConditionTrue, - }, { - Type: SourceConditionProvisioned, - Status: corev1.ConditionTrue, - }}, - }, - want: true, - }, { - name: "ready false condition", - s: &SourceStatus{ - Conditions: []duckv1alpha1.Condition{{ - Type: SourceConditionReady, - Status: corev1.ConditionFalse, - }, { - Type: SourceConditionProvisioned, - Status: corev1.ConditionTrue, - }}, - }, - want: false, - }, { - name: "unknown condition", - s: &SourceStatus{ - Conditions: []duckv1alpha1.Condition{{ - Type: "foo", - Status: corev1.ConditionTrue, - }}, - }, - want: false, - }, { - name: "mark provisioned", - s: func() *SourceStatus { - s := &SourceStatus{} - s.InitializeConditions() - s.MarkProvisioned() - return s - }(), - want: true, - }, { - name: "mark deprovisioned", - s: func() *SourceStatus { - s := &SourceStatus{} - s.InitializeConditions() - s.MarkDeprovisioned("Testing", "Just a test") - return s - }(), - want: false, - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.s.IsReady() - if diff := cmp.Diff(test.want, got); diff != "" { - t.Errorf("%s: unexpected condition (-want, +got) = %v", test.name, diff) - } - }) - } -} - -func TestSourceStatusGetCondition(t *testing.T) { - tests := []struct { - name string - s *SourceStatus - condQuery duckv1alpha1.ConditionType - want *duckv1alpha1.Condition - }{{ - name: "single condition", - s: &SourceStatus{ - Conditions: []duckv1alpha1.Condition{ - condReady, - }, - }, - condQuery: duckv1alpha1.ConditionReady, - want: &condReady, - }, { - name: "unknown condition", - s: &SourceStatus{ - Conditions: []duckv1alpha1.Condition{ - condReady, - condUnprovisioned, - }, - }, - condQuery: duckv1alpha1.ConditionType("foo"), - want: nil, - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.s.GetCondition(test.condQuery) - if diff := cmp.Diff(test.want, got); diff != "" { - t.Errorf("unexpected condition (-want, +got) = %v", diff) - } - }) - } -} diff --git a/pkg/apis/eventing/v1alpha1/source_validation.go b/pkg/apis/eventing/v1alpha1/source_validation.go deleted file mode 100644 index 5876a2a108c..00000000000 --- a/pkg/apis/eventing/v1alpha1/source_validation.go +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/pkg/apis" - "k8s.io/apimachinery/pkg/api/equality" -) - -// Validate validates the Source resource. -func (s *Source) Validate() *apis.FieldError { - return s.Spec.Validate().ViaField("spec") -} - -// Validate validates the Source spec -func (ss *SourceSpec) Validate() *apis.FieldError { - if equality.Semantic.DeepEqual(ss, &SourceSpec{}) { - return apis.ErrMissingField("provisioner") - } - var errs *apis.FieldError - - if ss.Channel != nil && !isChannelableEmpty(*ss.Channel) { - errs = errs.Also(isValidChannelable(*ss.Channel).ViaField("channel")) - } - - // TODO: could validate that arguments are json if that is a requirement. - - return errs -} diff --git a/pkg/apis/eventing/v1alpha1/source_validation_test.go b/pkg/apis/eventing/v1alpha1/source_validation_test.go deleted file mode 100644 index 212a24ad9a3..00000000000 --- a/pkg/apis/eventing/v1alpha1/source_validation_test.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "testing" - - "github.com/knative/pkg/apis" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -func TestSourceValidate(t *testing.T) { - tests := []CRDTest{{ - name: "empty", - cr: &Source{ - Spec: SourceSpec{}, - }, - want: apis.ErrMissingField("spec.provisioner"), - }, { - name: "minimum valid", - cr: &Source{ - Spec: SourceSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, - }, - }, - }, - }, { - name: "full valid", - cr: &Source{ - Spec: SourceSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, - }, - Arguments: &runtime.RawExtension{ - Raw: []byte(`{"foo":"baz"}`), - }, - Channel: &corev1.ObjectReference{ - Name: "bar", - Kind: "Channel", - APIVersion: "eventing.knative.dev/v1alpha1", - }, - }, - }, - }, { - name: "invalid, set extra channel parameters", - cr: &Source{ - Spec: SourceSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, - }, - Channel: &corev1.ObjectReference{ - Name: "bar", - Kind: "Channel", - APIVersion: "eventing.knative.dev/v1alpha1", - ResourceVersion: "SoMad", - }, - }, - }, - want: &apis.FieldError{ - Message: "must not set the field(s)", - Paths: []string{"spec.channel.ResourceVersion"}, - Details: "only name, apiVersion and kind are supported fields", - }, - }, { - name: "invalid, set extra channel as not a channel", - cr: &Source{ - Spec: SourceSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, - }, - Channel: &corev1.ObjectReference{ - Name: "backwards", - Kind: "lennahC", - APIVersion: "eventing.knative.dev/v1alpha1", - }, - }, - }, - want: &apis.FieldError{ - Message: "invalid value \"lennahC\"", - Paths: []string{"spec.channel.kind"}, - Details: "only 'Channel' kind is allowed", - }, - }} - - doValidateTest(t, tests) -} diff --git a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go index ddde0c1d91b..b7c7de54f5e 100644 --- a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go @@ -340,134 +340,6 @@ func (in *ResultStrategy) DeepCopy() *ResultStrategy { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Source) DeepCopyInto(out *Source) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Source. -func (in *Source) DeepCopy() *Source { - if in == nil { - return nil - } - out := new(Source) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Source) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SourceList) DeepCopyInto(out *SourceList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Source, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceList. -func (in *SourceList) DeepCopy() *SourceList { - if in == nil { - return nil - } - out := new(SourceList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *SourceList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SourceSpec) DeepCopyInto(out *SourceSpec) { - *out = *in - if in.Provisioner != nil { - in, out := &in.Provisioner, &out.Provisioner - if *in == nil { - *out = nil - } else { - *out = new(ProvisionerReference) - (*in).DeepCopyInto(*out) - } - } - if in.Arguments != nil { - in, out := &in.Arguments, &out.Arguments - if *in == nil { - *out = nil - } else { - *out = new(runtime.RawExtension) - (*in).DeepCopyInto(*out) - } - } - if in.Channel != nil { - in, out := &in.Channel, &out.Channel - if *in == nil { - *out = nil - } else { - *out = new(v1.ObjectReference) - **out = **in - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceSpec. -func (in *SourceSpec) DeepCopy() *SourceSpec { - if in == nil { - return nil - } - out := new(SourceSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SourceStatus) DeepCopyInto(out *SourceStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make(duck_v1alpha1.Conditions, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - out.Subscribable = in.Subscribable - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SourceStatus. -func (in *SourceStatus) DeepCopy() *SourceStatus { - if in == nil { - return nil - } - out := new(SourceStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Subscription) DeepCopyInto(out *Subscription) { *out = *in diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/eventing_client.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/eventing_client.go index c9474ed0042..3d84eebe8e4 100644 --- a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/eventing_client.go +++ b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/eventing_client.go @@ -29,7 +29,6 @@ type EventingV1alpha1Interface interface { RESTClient() rest.Interface ChannelsGetter ClusterProvisionersGetter - SourcesGetter SubscriptionsGetter } @@ -46,10 +45,6 @@ func (c *EventingV1alpha1Client) ClusterProvisioners() ClusterProvisionerInterfa return newClusterProvisioners(c) } -func (c *EventingV1alpha1Client) Sources(namespace string) SourceInterface { - return newSources(c, namespace) -} - func (c *EventingV1alpha1Client) Subscriptions(namespace string) SubscriptionInterface { return newSubscriptions(c, namespace) } diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_eventing_client.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_eventing_client.go index dfd67eea8b1..0127acc10fb 100644 --- a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_eventing_client.go +++ b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_eventing_client.go @@ -36,10 +36,6 @@ func (c *FakeEventingV1alpha1) ClusterProvisioners() v1alpha1.ClusterProvisioner return &FakeClusterProvisioners{c} } -func (c *FakeEventingV1alpha1) Sources(namespace string) v1alpha1.SourceInterface { - return &FakeSources{c, namespace} -} - func (c *FakeEventingV1alpha1) Subscriptions(namespace string) v1alpha1.SubscriptionInterface { return &FakeSubscriptions{c, namespace} } diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_source.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_source.go deleted file mode 100644 index 7fa9395eaa4..00000000000 --- a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_source.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeSources implements SourceInterface -type FakeSources struct { - Fake *FakeEventingV1alpha1 - ns string -} - -var sourcesResource = schema.GroupVersionResource{Group: "eventing.knative.dev", Version: "v1alpha1", Resource: "sources"} - -var sourcesKind = schema.GroupVersionKind{Group: "eventing.knative.dev", Version: "v1alpha1", Kind: "Source"} - -// Get takes name of the source, and returns the corresponding source object, and an error if there is any. -func (c *FakeSources) Get(name string, options v1.GetOptions) (result *v1alpha1.Source, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(sourcesResource, c.ns, name), &v1alpha1.Source{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Source), err -} - -// List takes label and field selectors, and returns the list of Sources that match those selectors. -func (c *FakeSources) List(opts v1.ListOptions) (result *v1alpha1.SourceList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(sourcesResource, sourcesKind, c.ns, opts), &v1alpha1.SourceList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.SourceList{ListMeta: obj.(*v1alpha1.SourceList).ListMeta} - for _, item := range obj.(*v1alpha1.SourceList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested sources. -func (c *FakeSources) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(sourcesResource, c.ns, opts)) - -} - -// Create takes the representation of a source and creates it. Returns the server's representation of the source, and an error, if there is any. -func (c *FakeSources) Create(source *v1alpha1.Source) (result *v1alpha1.Source, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(sourcesResource, c.ns, source), &v1alpha1.Source{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Source), err -} - -// Update takes the representation of a source and updates it. Returns the server's representation of the source, and an error, if there is any. -func (c *FakeSources) Update(source *v1alpha1.Source) (result *v1alpha1.Source, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(sourcesResource, c.ns, source), &v1alpha1.Source{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Source), err -} - -// Delete takes name of the source and deletes it. Returns an error if one occurs. -func (c *FakeSources) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(sourcesResource, c.ns, name), &v1alpha1.Source{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeSources) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(sourcesResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.SourceList{}) - return err -} - -// Patch applies the patch and returns the patched source. -func (c *FakeSources) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Source, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(sourcesResource, c.ns, name, data, subresources...), &v1alpha1.Source{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Source), err -} diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/generated_expansion.go index 121200ef9f9..2d119a1cc27 100644 --- a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/generated_expansion.go @@ -22,6 +22,4 @@ type ChannelExpansion interface{} type ClusterProvisionerExpansion interface{} -type SourceExpansion interface{} - type SubscriptionExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/source.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/source.go deleted file mode 100644 index f0a4a8a74f7..00000000000 --- a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/source.go +++ /dev/null @@ -1,157 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// SourcesGetter has a method to return a SourceInterface. -// A group's client should implement this interface. -type SourcesGetter interface { - Sources(namespace string) SourceInterface -} - -// SourceInterface has methods to work with Source resources. -type SourceInterface interface { - Create(*v1alpha1.Source) (*v1alpha1.Source, error) - Update(*v1alpha1.Source) (*v1alpha1.Source, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.Source, error) - List(opts v1.ListOptions) (*v1alpha1.SourceList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Source, err error) - SourceExpansion -} - -// sources implements SourceInterface -type sources struct { - client rest.Interface - ns string -} - -// newSources returns a Sources -func newSources(c *EventingV1alpha1Client, namespace string) *sources { - return &sources{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the source, and returns the corresponding source object, and an error if there is any. -func (c *sources) Get(name string, options v1.GetOptions) (result *v1alpha1.Source, err error) { - result = &v1alpha1.Source{} - err = c.client.Get(). - Namespace(c.ns). - Resource("sources"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Sources that match those selectors. -func (c *sources) List(opts v1.ListOptions) (result *v1alpha1.SourceList, err error) { - result = &v1alpha1.SourceList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("sources"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested sources. -func (c *sources) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("sources"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a source and creates it. Returns the server's representation of the source, and an error, if there is any. -func (c *sources) Create(source *v1alpha1.Source) (result *v1alpha1.Source, err error) { - result = &v1alpha1.Source{} - err = c.client.Post(). - Namespace(c.ns). - Resource("sources"). - Body(source). - Do(). - Into(result) - return -} - -// Update takes the representation of a source and updates it. Returns the server's representation of the source, and an error, if there is any. -func (c *sources) Update(source *v1alpha1.Source) (result *v1alpha1.Source, err error) { - result = &v1alpha1.Source{} - err = c.client.Put(). - Namespace(c.ns). - Resource("sources"). - Name(source.Name). - Body(source). - Do(). - Into(result) - return -} - -// Delete takes name of the source and deletes it. Returns an error if one occurs. -func (c *sources) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("sources"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *sources) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("sources"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched source. -func (c *sources) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Source, err error) { - result = &v1alpha1.Source{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("sources"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/informers/externalversions/eventing/v1alpha1/interface.go b/pkg/client/informers/externalversions/eventing/v1alpha1/interface.go index 2a44cf0f052..1fd4c99c7dc 100644 --- a/pkg/client/informers/externalversions/eventing/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/eventing/v1alpha1/interface.go @@ -28,8 +28,6 @@ type Interface interface { Channels() ChannelInformer // ClusterProvisioners returns a ClusterProvisionerInformer. ClusterProvisioners() ClusterProvisionerInformer - // Sources returns a SourceInformer. - Sources() SourceInformer // Subscriptions returns a SubscriptionInformer. Subscriptions() SubscriptionInformer } @@ -55,11 +53,6 @@ func (v *version) ClusterProvisioners() ClusterProvisionerInformer { return &clusterProvisionerInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } -// Sources returns a SourceInformer. -func (v *version) Sources() SourceInformer { - return &sourceInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - // Subscriptions returns a SubscriptionInformer. func (v *version) Subscriptions() SubscriptionInformer { return &subscriptionInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} diff --git a/pkg/client/informers/externalversions/eventing/v1alpha1/source.go b/pkg/client/informers/externalversions/eventing/v1alpha1/source.go deleted file mode 100644 index eb06180eed1..00000000000 --- a/pkg/client/informers/externalversions/eventing/v1alpha1/source.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - eventing_v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - versioned "github.com/knative/eventing/pkg/client/clientset/versioned" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/knative/eventing/pkg/client/listers/eventing/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// SourceInformer provides access to a shared informer and lister for -// Sources. -type SourceInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.SourceLister -} - -type sourceInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewSourceInformer constructs a new informer for Source type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewSourceInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredSourceInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredSourceInformer constructs a new informer for Source type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredSourceInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.EventingV1alpha1().Sources(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.EventingV1alpha1().Sources(namespace).Watch(options) - }, - }, - &eventing_v1alpha1.Source{}, - resyncPeriod, - indexers, - ) -} - -func (f *sourceInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredSourceInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *sourceInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&eventing_v1alpha1.Source{}, f.defaultInformer) -} - -func (f *sourceInformer) Lister() v1alpha1.SourceLister { - return v1alpha1.NewSourceLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 64978278fda..521a45e5029 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -70,8 +70,6 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().Channels().Informer()}, nil case eventing_v1alpha1.SchemeGroupVersion.WithResource("clusterprovisioners"): return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().ClusterProvisioners().Informer()}, nil - case eventing_v1alpha1.SchemeGroupVersion.WithResource("sources"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().Sources().Informer()}, nil case eventing_v1alpha1.SchemeGroupVersion.WithResource("subscriptions"): return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().Subscriptions().Informer()}, nil diff --git a/pkg/client/listers/eventing/v1alpha1/expansion_generated.go b/pkg/client/listers/eventing/v1alpha1/expansion_generated.go index f3854a6804d..abc32a5ee74 100644 --- a/pkg/client/listers/eventing/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/eventing/v1alpha1/expansion_generated.go @@ -30,14 +30,6 @@ type ChannelNamespaceListerExpansion interface{} // ClusterProvisionerLister. type ClusterProvisionerListerExpansion interface{} -// SourceListerExpansion allows custom methods to be added to -// SourceLister. -type SourceListerExpansion interface{} - -// SourceNamespaceListerExpansion allows custom methods to be added to -// SourceNamespaceLister. -type SourceNamespaceListerExpansion interface{} - // SubscriptionListerExpansion allows custom methods to be added to // SubscriptionLister. type SubscriptionListerExpansion interface{} diff --git a/pkg/client/listers/eventing/v1alpha1/source.go b/pkg/client/listers/eventing/v1alpha1/source.go deleted file mode 100644 index ec5c3a667ba..00000000000 --- a/pkg/client/listers/eventing/v1alpha1/source.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// SourceLister helps list Sources. -type SourceLister interface { - // List lists all Sources in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.Source, err error) - // Sources returns an object that can list and get Sources. - Sources(namespace string) SourceNamespaceLister - SourceListerExpansion -} - -// sourceLister implements the SourceLister interface. -type sourceLister struct { - indexer cache.Indexer -} - -// NewSourceLister returns a new SourceLister. -func NewSourceLister(indexer cache.Indexer) SourceLister { - return &sourceLister{indexer: indexer} -} - -// List lists all Sources in the indexer. -func (s *sourceLister) List(selector labels.Selector) (ret []*v1alpha1.Source, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Source)) - }) - return ret, err -} - -// Sources returns an object that can list and get Sources. -func (s *sourceLister) Sources(namespace string) SourceNamespaceLister { - return sourceNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// SourceNamespaceLister helps list and get Sources. -type SourceNamespaceLister interface { - // List lists all Sources in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha1.Source, err error) - // Get retrieves the Source from the indexer for a given namespace and name. - Get(name string) (*v1alpha1.Source, error) - SourceNamespaceListerExpansion -} - -// sourceNamespaceLister implements the SourceNamespaceLister -// interface. -type sourceNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Sources in the indexer for a given namespace. -func (s sourceNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Source, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Source)) - }) - return ret, err -} - -// Get retrieves the Source from the indexer for a given namespace and name. -func (s sourceNamespaceLister) Get(name string) (*v1alpha1.Source, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("source"), name) - } - return obj.(*v1alpha1.Source), nil -} diff --git a/pkg/controller/owner_references.go b/pkg/controller/owner_references.go index 5780dca9af9..db5a2a51977 100644 --- a/pkg/controller/owner_references.go +++ b/pkg/controller/owner_references.go @@ -53,8 +53,6 @@ func kind(obj metav1.Object) schema.GroupVersionKind { return flowsv1alpha.SchemeGroupVersion.WithKind("Flow") // Eventing - case *eventingv1alpha.Source: - return eventingv1alpha.SchemeGroupVersion.WithKind("Source") case *eventingv1alpha.Channel: return eventingv1alpha.SchemeGroupVersion.WithKind("Channel") case *eventingv1alpha.ClusterProvisioner: From 0e9276a573ffbe1d27ceb5cd201146faf2237c60 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Thu, 25 Oct 2018 14:28:32 -0700 Subject: [PATCH 02/54] No longer use the subscribable interface. (#555) * No longer use the subscribable interface. * Delete no longer used function. --- pkg/apis/eventing/v1alpha1/channel_types.go | 33 +- .../eventing/v1alpha1/channel_types_test.go | 87 ------ pkg/apis/eventing/v1alpha1/implements_test.go | 2 - .../subscribable_channelable_validation.go | 4 +- .../eventing/v1alpha1/subscription_types.go | 9 - .../v1alpha1/subscription_validation.go | 4 +- .../v1alpha1/zz_generated.deepcopy.go | 3 - .../eventing/inmemory/channel/reconcile.go | 1 - .../inmemory/channel/reconcile_test.go | 20 +- .../eventing/subscription/reconcile.go | 32 +- .../eventing/subscription/reconcile_test.go | 282 +++++++----------- 11 files changed, 119 insertions(+), 358 deletions(-) diff --git a/pkg/apis/eventing/v1alpha1/channel_types.go b/pkg/apis/eventing/v1alpha1/channel_types.go index d5122b08344..697dda0fb79 100644 --- a/pkg/apis/eventing/v1alpha1/channel_types.go +++ b/pkg/apis/eventing/v1alpha1/channel_types.go @@ -20,7 +20,6 @@ import ( "github.com/knative/pkg/apis" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" "github.com/knative/pkg/webhook" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -29,8 +28,8 @@ import ( // +genclient:noStatus // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// Channel is an abstract resource that implements the Subscribable and Sinkable -// contracts. The Provisioner provisions infrastructure to accepts events and +// Channel is an abstract resource that implements the Sinkable contract. +// The Provisioner provisions infrastructure to accepts events and // deliver to Subscriptions. type Channel struct { metav1.TypeMeta `json:",inline"` @@ -76,7 +75,7 @@ type ChannelSpec struct { Channelable *duckv1alpha1.Channelable `json:"channelable,omitempty"` } -var chanCondSet = duckv1alpha1.NewLivingConditionSet(ChannelConditionProvisioned, ChannelConditionSinkable, ChannelConditionSubscribable) +var chanCondSet = duckv1alpha1.NewLivingConditionSet(ChannelConditionProvisioned, ChannelConditionSinkable) // ChannelStatus represents the current state of a Channel. type ChannelStatus struct { @@ -93,9 +92,6 @@ type ChannelStatus struct { // It generally has the form {channel}.{namespace}.svc.cluster.local Sinkable duckv1alpha1.Sinkable `json:"sinkable,omitempty"` - // Channel is Subscribable. It just points to itself - Subscribable duckv1alpha1.Subscribable `json:"subscribable,omitempty"` - // Represents the latest available observations of a channel's current state. // +optional // +patchMergeKey=type @@ -115,10 +111,6 @@ const ( // ChannelConditionSinkable has status true when this Channel meets the Sinkable contract and // has a non-empty domainInternal. ChannelConditionSinkable duckv1alpha1.ConditionType = "Sinkable" - - // ChannelConditionSubscribable has status true when this Channel meets the Subscribable - // contract and has a non-empty Channelable object reference. - ChannelConditionSubscribable duckv1alpha1.ConditionType = "Subscribable" ) // GetCondition returns the condition currently associated with the given type, or nil. @@ -141,25 +133,6 @@ func (cs *ChannelStatus) MarkProvisioned() { chanCondSet.Manage(cs).MarkTrue(ChannelConditionProvisioned) } -// SetSubscribable makes this Channel Subscribable, by having it point at itself. The 'name' and -// 'namespace' should be the name and namespace of the Channel this ChannelStatus is on. It also -// sets the ChannelConditionSubscribable to true. -func (cs *ChannelStatus) SetSubscribable(namespace, name string) { - if namespace != "" || name != "" { - cs.Subscribable.Channelable = corev1.ObjectReference{ - Kind: "Channel", - APIVersion: SchemeGroupVersion.String(), - Namespace: namespace, - Name: name, - } - chanCondSet.Manage(cs).MarkTrue(ChannelConditionSubscribable) - } else { - cs.Subscribable.Channelable = corev1.ObjectReference{} - chanCondSet.Manage(cs).MarkFalse(ChannelConditionSubscribable, "notSubscribable", "not Subscribable") - } - -} - // SetSinkable makes this Channel sinkable by setting the domainInternal. It also sets the // ChannelConditionSinkable to true. func (cs *ChannelStatus) SetSinkable(domainInternal string) { diff --git a/pkg/apis/eventing/v1alpha1/channel_types_test.go b/pkg/apis/eventing/v1alpha1/channel_types_test.go index e2e6cac4104..3cdee5c281d 100644 --- a/pkg/apis/eventing/v1alpha1/channel_types_test.go +++ b/pkg/apis/eventing/v1alpha1/channel_types_test.go @@ -102,9 +102,6 @@ func TestChannelInitializeConditions(t *testing.T) { }, { Type: ChannelConditionSinkable, Status: corev1.ConditionUnknown, - }, { - Type: ChannelConditionSubscribable, - Status: corev1.ConditionUnknown, }}, }, }, { @@ -125,9 +122,6 @@ func TestChannelInitializeConditions(t *testing.T) { }, { Type: ChannelConditionSinkable, Status: corev1.ConditionUnknown, - }, { - Type: ChannelConditionSubscribable, - Status: corev1.ConditionUnknown, }}, }, }, { @@ -148,9 +142,6 @@ func TestChannelInitializeConditions(t *testing.T) { }, { Type: ChannelConditionSinkable, Status: corev1.ConditionUnknown, - }, { - Type: ChannelConditionSubscribable, - Status: corev1.ConditionUnknown, }}}, }, } @@ -170,19 +161,16 @@ func TestChannelIsReady(t *testing.T) { tests := []struct { name string markProvisioned bool - setSubscribable bool setSinkable bool wantReady bool }{{ name: "all happy", markProvisioned: true, - setSubscribable: true, setSinkable: true, wantReady: true, }, { name: "one sad", markProvisioned: false, - setSubscribable: true, setSinkable: true, wantReady: false, }} @@ -192,9 +180,6 @@ func TestChannelIsReady(t *testing.T) { if test.markProvisioned { cs.MarkProvisioned() } - if test.setSubscribable { - cs.SetSubscribable("foo", "bar") - } if test.setSinkable { cs.SetSinkable("foo.bar") } @@ -206,78 +191,6 @@ func TestChannelIsReady(t *testing.T) { } } -func TestChannelStatus_SetSubscribable(t *testing.T) { - testCases := map[string]struct { - namespace string - name string - want *ChannelStatus - }{ - "empty namespace and name": { - want: &ChannelStatus{ - Conditions: []duckv1alpha1.Condition{ - // Note that Ready is here because when the condition is marked False, duck - // automatically sets Ready to false. - { - Type: ChannelConditionReady, - Status: corev1.ConditionFalse, - }, - { - Type: ChannelConditionSubscribable, - Status: corev1.ConditionFalse, - }, - }, - }, - }, - "empty namespace": { - name: "foobar", - want: &ChannelStatus{ - Subscribable: duckv1alpha1.Subscribable{ - Channelable: corev1.ObjectReference{ - APIVersion: SchemeGroupVersion.String(), - Kind: "Channel", - Name: "foobar", - }, - }, - Conditions: []duckv1alpha1.Condition{ - { - Type: ChannelConditionSubscribable, - Status: corev1.ConditionTrue, - }, - }, - }, - }, - "subscribable": { - namespace: "test-namespace", - name: "test-name", - want: &ChannelStatus{ - Subscribable: duckv1alpha1.Subscribable{ - Channelable: corev1.ObjectReference{ - APIVersion: SchemeGroupVersion.String(), - Kind: "Channel", - Namespace: "test-namespace", - Name: "test-name", - }, - }, - Conditions: []duckv1alpha1.Condition{ - { - Type: ChannelConditionSubscribable, - Status: corev1.ConditionTrue, - }, - }, - }, - }, - } - for n, tc := range testCases { - t.Run(n, func(t *testing.T) { - cs := &ChannelStatus{} - cs.SetSubscribable(tc.namespace, tc.name) - if diff := cmp.Diff(tc.want, cs, ignoreTransitionTimeMessageAndReason); diff != "" { - t.Errorf("unexpected conditions (-want, +got) = %v", diff) - } - }) - } -} - func TestChannelStatus_SetSinkable(t *testing.T) { testCases := map[string]struct { domainInternal string diff --git a/pkg/apis/eventing/v1alpha1/implements_test.go b/pkg/apis/eventing/v1alpha1/implements_test.go index 7b40b2d9af3..1e0198fe50c 100644 --- a/pkg/apis/eventing/v1alpha1/implements_test.go +++ b/pkg/apis/eventing/v1alpha1/implements_test.go @@ -28,14 +28,12 @@ func TestTypesImplements(t *testing.T) { // Channel {instance: &Channel{}, iface: &duckv1alpha1.Conditions{}}, {instance: &Channel{}, iface: &duckv1alpha1.Channelable{}}, - {instance: &Channel{}, iface: &duckv1alpha1.Subscribable{}}, {instance: &Channel{}, iface: &duckv1alpha1.Sinkable{}}, // ClusterProvisioner {instance: &ClusterProvisioner{}, iface: &duckv1alpha1.Conditions{}}, // Subscription {instance: &Subscription{}, iface: &duckv1alpha1.Conditions{}}, {instance: &Subscription{}, iface: &emptyGen}, - {instance: &Subscription{}, iface: &duckv1alpha1.Subscribable{}}, } for _, tc := range testCases { if err := duck.VerifyType(tc.instance, tc.iface); err != nil { diff --git a/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go b/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go index 7918fc3d481..452ab0dcbad 100644 --- a/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go +++ b/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go @@ -25,7 +25,7 @@ import ( "k8s.io/apimachinery/pkg/api/equality" ) -func isSubscribableEmpty(f corev1.ObjectReference) bool { +func isChannelEmpty(f corev1.ObjectReference) bool { return equality.Semantic.DeepEqual(f, corev1.ObjectReference{}) } @@ -33,7 +33,7 @@ func isSubscribableEmpty(f corev1.ObjectReference) bool { // - Kind == 'Channel' // - APIVersion == 'eventing.knative.dev/v1alpha1' // - Name == not empty -func isValidSubscribable(f corev1.ObjectReference) *apis.FieldError { +func isValidChannel(f corev1.ObjectReference) *apis.FieldError { errs := isValidObjectReference(f) if f.Kind != "Channel" { diff --git a/pkg/apis/eventing/v1alpha1/subscription_types.go b/pkg/apis/eventing/v1alpha1/subscription_types.go index 4af64819a17..567d156a468 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_types.go +++ b/pkg/apis/eventing/v1alpha1/subscription_types.go @@ -71,8 +71,6 @@ type SubscriptionSpec struct { // for receiving events. The object must have spec.subscriptions // list which will then be modified accordingly. // - // This object must fulfill the Subscribable contract. - // // You can specify only the following fields of the ObjectReference: // - Kind // - APIVersion @@ -171,10 +169,6 @@ type SubscriptionStatus struct { // +patchStrategy=merge Conditions duckv1alpha1.Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` - // Subscription might be Subscribable. This depends if there's a Result channel - // In that case, this points to that resource. - Subscribable duckv1alpha1.Subscribable `json:"subscribable,omitempty"` - // PhysicalSubscription is the fully resolved values that this Subscription represents. PhysicalSubscription SubscriptionStatusPhysicalSubscription `json:"physicalSubscription,omitEmpty"` } @@ -182,9 +176,6 @@ type SubscriptionStatus struct { // SubscriptionStatusPhysicalSubscription represents the fully resolved values for this // Subscription. type SubscriptionStatusPhysicalSubscription struct { - // From is the object pointed to in status.from's Subscribable contract. - From corev1.ObjectReference `json:"from,omitEmpty"` - // CallDomain is the fully resolved domain for spec.callable. CallDomain string `json:"callDomain,omitEmpty"` diff --git a/pkg/apis/eventing/v1alpha1/subscription_validation.go b/pkg/apis/eventing/v1alpha1/subscription_validation.go index ddbe9468f01..20d17797005 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_validation.go +++ b/pkg/apis/eventing/v1alpha1/subscription_validation.go @@ -86,7 +86,7 @@ func isValidEndpointSpec(e EndpointSpec) *apis.FieldError { } func isFromEmpty(f corev1.ObjectReference) bool { - return isSubscribableEmpty(f) + return isChannelEmpty(f) } // Valid from only contains the following fields: @@ -94,7 +94,7 @@ func isFromEmpty(f corev1.ObjectReference) bool { // - APIVersion == 'eventing.knative.dev/v1alpha1' // - Name == not empty func isValidFrom(f corev1.ObjectReference) *apis.FieldError { - return isValidSubscribable(f) + return isValidChannel(f) } func isResultStrategyNilOrEmpty(r *ResultStrategy) bool { diff --git a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go index b7c7de54f5e..4f946e69bac 100644 --- a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go @@ -134,7 +134,6 @@ func (in *ChannelSpec) DeepCopy() *ChannelSpec { func (in *ChannelStatus) DeepCopyInto(out *ChannelStatus) { *out = *in out.Sinkable = in.Sinkable - out.Subscribable = in.Subscribable if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions *out = make(duck_v1alpha1.Conditions, len(*in)) @@ -446,7 +445,6 @@ func (in *SubscriptionStatus) DeepCopyInto(out *SubscriptionStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } - out.Subscribable = in.Subscribable out.PhysicalSubscription = in.PhysicalSubscription return } @@ -464,7 +462,6 @@ func (in *SubscriptionStatus) DeepCopy() *SubscriptionStatus { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SubscriptionStatusPhysicalSubscription) DeepCopyInto(out *SubscriptionStatusPhysicalSubscription) { *out = *in - out.From = in.From return } diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 439361e8978..704f8c99330 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -141,7 +141,6 @@ func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) } r.addFinalizer(c) - c.Status.SetSubscribable(c.Namespace, c.Name) if svc, err := r.createK8sService(ctx, c); err != nil { logger.Info("Error creating the Channel's K8s Service", zap.Error(err)) diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index 13466bad4ae..e1905b74570 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -314,7 +314,7 @@ func TestReconcile(t *testing.T) { MockGets: errorGettingK8sService(), }, WantPresent: []runtime.Object{ - makeChannelWithFinalizerAndSubscribable(), + makeChannelWithFinalizer(), }, WantErrMsg: testErrorMessage, }, @@ -329,7 +329,7 @@ func TestReconcile(t *testing.T) { }, WantPresent: []runtime.Object{ // TODO: This should have a useful error message saying that the K8s Service failed. - makeChannelWithFinalizerAndSubscribable(), + makeChannelWithFinalizer(), }, WantErrMsg: testErrorMessage, }, @@ -358,7 +358,7 @@ func TestReconcile(t *testing.T) { WantPresent: []runtime.Object{ // TODO: This should have a useful error message saying that the VirtualService // failed. - makeChannelWithFinalizerAndSubscribableAndSinkable(), + makeChannelWithFinalizerAndSinkable(), }, WantErrMsg: testErrorMessage, }, @@ -375,7 +375,7 @@ func TestReconcile(t *testing.T) { WantPresent: []runtime.Object{ // TODO: This should have a useful error message saying that the VirtualService // failed. - makeChannelWithFinalizerAndSubscribableAndSinkable(), + makeChannelWithFinalizerAndSinkable(), }, WantErrMsg: testErrorMessage, }, @@ -483,21 +483,15 @@ func makeChannel() *eventingv1alpha1.Channel { return c } -func makeChannelWithFinalizerAndSubscribable() *eventingv1alpha1.Channel { +func makeChannelWithFinalizerAndSinkable() *eventingv1alpha1.Channel { c := makeChannelWithFinalizer() - c.Status.SetSubscribable(c.Namespace, c.Name) - return c -} - -func makeChannelWithFinalizerAndSubscribableAndSinkable() *eventingv1alpha1.Channel { - c := makeChannelWithFinalizerAndSubscribable() c.Status.SetSinkable(fmt.Sprintf("%s-channel.%s.svc.cluster.local", c.Name, c.Namespace)) return c } func makeReadyChannel() *eventingv1alpha1.Channel { - // Ready channels have the finalizer and are Subscribable and Sinkable. - c := makeChannelWithFinalizerAndSubscribableAndSinkable() + // Ready channels have the finalizer and are Sinkable. + c := makeChannelWithFinalizerAndSinkable() c.Status.MarkProvisioned() return c } diff --git a/pkg/controller/eventing/subscription/reconcile.go b/pkg/controller/eventing/subscription/reconcile.go index 36b307e0094..759ec89f6c7 100644 --- a/pkg/controller/eventing/subscription/reconcile.go +++ b/pkg/controller/eventing/subscription/reconcile.go @@ -86,19 +86,12 @@ func (r *reconciler) reconcile(subscription *v1alpha1.Subscription) error { deletionTimestamp := accessor.GetDeletionTimestamp() glog.Infof("DeletionTimestamp: %v", deletionTimestamp) - // Reconcile the subscription to the From channel that's consuming events that are either - // going to the call or if there's no call, directly to result. - from, err := r.resolveFromChannelable(subscription.Namespace, &subscription.Spec.From) + // Verify that `from` exists. + _, err = r.fetchObjectReference(subscription.Namespace, &subscription.Spec.From) if err != nil { - glog.Warningf("Failed to resolve From %+v : %s", subscription.Spec.From, err) + glog.Warningf("Failed to validate `from` exists: %+v, %v", subscription.Spec.From, err) return err } - if from.Status.Subscribable == nil { - return fmt.Errorf("from is not subscribable %s %s/%s", subscription.Spec.From.Kind, subscription.Namespace, subscription.Spec.From.Name) - } - - subscription.Status.PhysicalSubscription.From = from.Status.Subscribable.Channelable - glog.Infof("Resolved from subscribable to: %+v", from.Status.Subscribable.Channelable) callDomain := "" if subscription.Spec.Call != nil { @@ -226,21 +219,6 @@ func (r *reconciler) resolveResult(namespace string, resultStrategy v1alpha1.Res return "", fmt.Errorf("status does not contain sinkable") } -// resolveFromChannelable fetches an object based on ObjectReference. It assumes that the -// fetched object then implements Subscribable interface and returns the ObjectReference -// representing the Channelable interface. -func (r *reconciler) resolveFromChannelable(namespace string, ref *corev1.ObjectReference) (*duckv1alpha1.Subscription, error) { - obj, err := r.fetchObjectReference(namespace, ref) - if err != nil { - glog.Warningf("Failed to fetch From target %+v: %s", ref, err) - return nil, err - } - - c := duckv1alpha1.Subscription{} - err = duck.FromUnstructured(obj, &c) - return &c, err -} - // fetchObjectReference fetches an object based on ObjectReference. func (r *reconciler) fetchObjectReference(namespace string, ref *corev1.ObjectReference) (duck.Marshalable, error) { resourceClient, err := r.CreateResourceInterface(namespace, ref) @@ -263,7 +241,7 @@ func (r *reconciler) syncPhysicalFromChannel(sub *v1alpha1.Subscription) error { channelable := r.createChannelable(subs) - return r.patchPhysicalFrom(sub.Namespace, sub.Status.PhysicalSubscription.From, channelable) + return r.patchPhysicalFrom(sub.Namespace, sub.Spec.From, channelable) } func (r *reconciler) listAllSubscriptionsWithPhysicalFrom(sub *v1alpha1.Subscription) ([]v1alpha1.Subscription, error) { @@ -296,7 +274,7 @@ func (r *reconciler) listAllSubscriptionsWithPhysicalFrom(sub *v1alpha1.Subscrip // This is the sub that is being reconciled. It has already been added to the list. continue } - if equality.Semantic.DeepEqual(sub.Status.PhysicalSubscription.From, s.Status.PhysicalSubscription.From) { + if equality.Semantic.DeepEqual(sub.Spec.From, s.Spec.From) { subs = append(subs, s) } } diff --git a/pkg/controller/eventing/subscription/reconcile_test.go b/pkg/controller/eventing/subscription/reconcile_test.go index 38042eab6d2..dbc7d036c2d 100644 --- a/pkg/controller/eventing/subscription/reconcile_test.go +++ b/pkg/controller/eventing/subscription/reconcile_test.go @@ -72,35 +72,64 @@ var testCases = []controllertesting.TestCase{ }, WantErrMsg: `channels.eventing.knative.dev "fromchannel" not found`, }, { - Name: "subscription, but From is not subscribable", + Name: "subscription, but From is not channelable", InitialState: []runtime.Object{ - getNewSubscription(), + getNewSourceSubscription(), }, - WantErrMsg: "from is not subscribable Channel testnamespace/fromchannel", + // TODO: JSON patch is not working on the fake, see + // https://github.com/kubernetes/client-go/issues/478. Marking this as expecting a specific + // failure for now, until upstream is fixed. It should actually fail saying that there is no + // Spec.Subscribers field. + WantErrMsg: "invalid JSON document", Scheme: scheme.Scheme, Objects: []runtime.Object{ // Source channel + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), + "kind": sourceKind, + "metadata": map[string]interface{}{ + "namespace": testNS, + "name": sourceName, + }, + "spec": map[string]interface{}{}, + }, + }, + // Call (using knative route) + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "serving.knative.dev/v1alpha1", + "kind": routeKind, + "metadata": map[string]interface{}{ + "namespace": testNS, + "name": routeName, + }, + "status": map[string]interface{}{ + "targetable": map[string]interface{}{ + "domainInternal": targetDNS, + }, + }, + }, + }, + // Result channel &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), "kind": channelKind, "metadata": map[string]interface{}{ "namespace": testNS, - "name": fromChannelName, + "name": resultChannelName, }, "spec": map[string]interface{}{ "channelable": map[string]interface{}{}, }, "status": map[string]interface{}{ - "notsubscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, + "sinkable": map[string]interface{}{ + "domainInternal": sinkableDNS, }, }, - }}, + }, + }, }, }, { Name: "Valid from, call does not exist", @@ -109,7 +138,7 @@ var testCases = []controllertesting.TestCase{ }, WantErrMsg: `routes.serving.knative.dev "callroute" not found`, WantPresent: []runtime.Object{ - getNewSubscriptionWithUnknownConditionsAndPhysicalFrom(), + getNewSubscriptionWithUnknownConditions(), }, Scheme: scheme.Scheme, Objects: []runtime.Object{ @@ -125,16 +154,8 @@ var testCases = []controllertesting.TestCase{ "spec": map[string]interface{}{ "channelable": map[string]interface{}{}, }, - "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, - }, - }}, + }, + }, }, }, { Name: "Valid from, call is not targetable", @@ -142,7 +163,7 @@ var testCases = []controllertesting.TestCase{ getNewSubscription(), }, WantPresent: []runtime.Object{ - getNewSubscriptionWithUnknownConditionsAndPhysicalFrom(), + getNewSubscriptionWithUnknownConditions(), }, WantErrMsg: "status does not contain targetable", Scheme: scheme.Scheme, @@ -159,16 +180,8 @@ var testCases = []controllertesting.TestCase{ "spec": map[string]interface{}{ "channelable": map[string]interface{}{}, }, - "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, - }, - }}, + }, + }, // Call (using knative route) &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -181,7 +194,8 @@ var testCases = []controllertesting.TestCase{ "status": map[string]interface{}{ "someotherstuff": targetDNS, }, - }}, + }, + }, }, }, { Name: "Valid from and call, result does not exist", @@ -189,7 +203,7 @@ var testCases = []controllertesting.TestCase{ getNewSubscription(), }, WantPresent: []runtime.Object{ - getNewSubscriptionWithUnknownConditionsAndPhysicalFromCall(), + getNewSubscriptionWithUnknownConditionsAndPhysicalCall(), }, WantErrMsg: `channels.eventing.knative.dev "resultchannel" not found`, Scheme: scheme.Scheme, @@ -206,16 +220,8 @@ var testCases = []controllertesting.TestCase{ "spec": map[string]interface{}{ "channelable": map[string]interface{}{}, }, - "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, - }, - }}, + }, + }, // Call (using knative route) &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -230,7 +236,8 @@ var testCases = []controllertesting.TestCase{ "domainInternal": targetDNS, }, }, - }}, + }, + }, }, }, { Name: "valid from, call, result is not sinkable", @@ -242,7 +249,7 @@ var testCases = []controllertesting.TestCase{ // TODO: Again this works on gke cluster, but I need to set // something else up here. later... // getNewSubscriptionWithReferencesResolvedStatus(), - getNewSubscriptionWithUnknownConditionsAndPhysicalFromCall(), + getNewSubscriptionWithUnknownConditionsAndPhysicalCall(), }, Scheme: scheme.Scheme, Objects: []runtime.Object{ @@ -258,16 +265,8 @@ var testCases = []controllertesting.TestCase{ "spec": map[string]interface{}{ "channelable": map[string]interface{}{}, }, - "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, - }, - }}, + }, + }, // Call (using knative route) &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -282,7 +281,8 @@ var testCases = []controllertesting.TestCase{ "domainInternal": targetDNS, }, }, - }}, + }, + }, // Result channel &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -295,16 +295,8 @@ var testCases = []controllertesting.TestCase{ "spec": map[string]interface{}{ "channelable": map[string]interface{}{}, }, - "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, - }, - }}, + }, + }, }, }, { Name: "new subscription: adds status, all targets resolved, subscribers modified", @@ -316,7 +308,7 @@ var testCases = []controllertesting.TestCase{ // failure for now, until upstream is fixed. WantResult: reconcile.Result{}, WantPresent: []runtime.Object{ - getNewSubscriptionWithReferencesResolvedAndPhysicalFromCallResult(), + getNewSubscriptionWithReferencesResolvedAndPhysicalCallResult(), }, WantErrMsg: "invalid JSON document", Scheme: scheme.Scheme, @@ -333,16 +325,8 @@ var testCases = []controllertesting.TestCase{ "spec": map[string]interface{}{ "channelable": map[string]interface{}{}, }, - "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, - }, - }}, + }, + }, // Call (using knative route) &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -357,7 +341,8 @@ var testCases = []controllertesting.TestCase{ "domainInternal": targetDNS, }, }, - }}, + }, + }, // Result channel &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -371,18 +356,12 @@ var testCases = []controllertesting.TestCase{ "channelable": map[string]interface{}{}, }, "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, "sinkable": map[string]interface{}{ "domainInternal": sinkableDNS, }, }, - }}, + }, + }, }, }, { Name: "new subscription to K8s Service: adds status, all targets resolved, subscribers modified", @@ -412,16 +391,8 @@ var testCases = []controllertesting.TestCase{ "spec": map[string]interface{}{ "channelable": map[string]interface{}{}, }, - "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, - }, - }}, + }, + }, // Call (using K8s Service) &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -431,7 +402,8 @@ var testCases = []controllertesting.TestCase{ "namespace": testNS, "name": k8sServiceName, }, - }}, + }, + }, // Result channel &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -445,18 +417,12 @@ var testCases = []controllertesting.TestCase{ "channelable": map[string]interface{}{}, }, "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, "sinkable": map[string]interface{}{ "domainInternal": sinkableDNS, }, }, - }}, + }, + }, }, }, { Name: "new subscription with from channel: adds status, all targets resolved, subscribers modified", @@ -485,16 +451,8 @@ var testCases = []controllertesting.TestCase{ "spec": map[string]interface{}{ "channelable": map[string]interface{}{}, }, - "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, - }, - }}, + }, + }, // Source channel &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -507,16 +465,8 @@ var testCases = []controllertesting.TestCase{ "spec": map[string]interface{}{ "channelable": map[string]interface{}{}, }, - "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, - }, - }}, + }, + }, // Call (using knative route) &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -531,7 +481,8 @@ var testCases = []controllertesting.TestCase{ "domainInternal": targetDNS, }, }, - }}, + }, + }, // Result channel &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -545,13 +496,6 @@ var testCases = []controllertesting.TestCase{ "channelable": map[string]interface{}{}, }, "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, "sinkable": map[string]interface{}{ "domainInternal": sinkableDNS, }, @@ -566,7 +510,7 @@ var testCases = []controllertesting.TestCase{ // The first two Subscriptions both have the same physical From, so we should see that // Channel updated with both Subscriptions. getNewSubscriptionWithFromChannel(), - rename(getNewSubscriptionWithReferencesResolvedAndPhysicalFromCallResult()), + rename(getNewSubscriptionWithReferencesResolvedAndPhysicalCallResult()), // This subscription has a different physical From, so we should not see it in the same // Channel as the first two. getSubscriptionWithDifferentChannel(), @@ -585,7 +529,7 @@ var testCases = []controllertesting.TestCase{ //getChannelWithMultipleSubscriptions(), getNewSubscriptionWithSourceWithReferencesResolvedAndPhysicalFromCallResult(), // Unaltered because this Subscription was not reconciled. - rename(getNewSubscriptionWithReferencesResolvedAndPhysicalFromCallResult()), + rename(getNewSubscriptionWithReferencesResolvedAndPhysicalCallResult()), getSubscriptionWithDifferentChannel(), }, Scheme: scheme.Scheme, @@ -602,16 +546,8 @@ var testCases = []controllertesting.TestCase{ "spec": map[string]interface{}{ "channelable": map[string]interface{}{}, }, - "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, - }, - }}, + }, + }, // Source channel &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -624,16 +560,8 @@ var testCases = []controllertesting.TestCase{ "spec": map[string]interface{}{ "channelable": map[string]interface{}{}, }, - "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, - }, - }}, + }, + }, // Call (using knative route) &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -648,7 +576,8 @@ var testCases = []controllertesting.TestCase{ "domainInternal": targetDNS, }, }, - }}, + }, + }, // Result channel &unstructured.Unstructured{ Object: map[string]interface{}{ @@ -662,13 +591,6 @@ var testCases = []controllertesting.TestCase{ "channelable": map[string]interface{}{}, }, "status": map[string]interface{}{ - "subscribable": map[string]interface{}{ - "channelable": map[string]interface{}{ - "kind": channelKind, - "name": fromChannelName, - "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), - }, - }, "sinkable": map[string]interface{}{ "domainInternal": sinkableDNS, }, @@ -762,6 +684,16 @@ func getNewSubscription() *eventingv1alpha1.Subscription { return subscription } +func getNewSourceSubscription() *eventingv1alpha1.Subscription { + sub := getNewSubscription() + sub.Spec.From = corev1.ObjectReference{ + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: sourceKind, + Name: sourceName, + } + return sub +} + func getNewSubscriptionToK8sService() *eventingv1alpha1.Subscription { sub := getNewSubscription() sub.Spec.Call = &eventingv1alpha1.EndpointSpec{ @@ -813,21 +745,14 @@ func getNewSubscriptionWithUnknownConditions() *eventingv1alpha1.Subscription { return s } -func getNewSubscriptionWithUnknownConditionsAndPhysicalFrom() *eventingv1alpha1.Subscription { +func getNewSubscriptionWithUnknownConditionsAndPhysicalCall() *eventingv1alpha1.Subscription { s := getNewSubscriptionWithUnknownConditions() - // The original From is a Channel, so points at itself. - s.Status.PhysicalSubscription.From = s.Spec.From - return s -} - -func getNewSubscriptionWithUnknownConditionsAndPhysicalFromCall() *eventingv1alpha1.Subscription { - s := getNewSubscriptionWithUnknownConditionsAndPhysicalFrom() s.Status.PhysicalSubscription.CallDomain = targetDNS return s } -func getNewSubscriptionWithReferencesResolvedAndPhysicalFromCallResult() *eventingv1alpha1.Subscription { - s := getNewSubscriptionWithUnknownConditionsAndPhysicalFrom() +func getNewSubscriptionWithReferencesResolvedAndPhysicalCallResult() *eventingv1alpha1.Subscription { + s := getNewSubscriptionWithUnknownConditions() s.Status.MarkReferencesResolved() s.Status.PhysicalSubscription.CallDomain = targetDNS s.Status.PhysicalSubscription.ResultDomain = sinkableDNS @@ -839,7 +764,6 @@ func getNewSubscriptionToK8sServiceWithReferencesResolvedAndPhysicalFromCallResu s.Status.InitializeConditions() s.Status.MarkReferencesResolved() s.Status.PhysicalSubscription = eventingv1alpha1.SubscriptionStatusPhysicalSubscription{ - From: s.Spec.From, CallDomain: k8sServiceDNS, ResultDomain: sinkableDNS, } @@ -851,11 +775,6 @@ func getNewSubscriptionWithSourceWithReferencesResolvedAndPhysicalFromCallResult s.Status.InitializeConditions() s.Status.MarkReferencesResolved() s.Status.PhysicalSubscription = eventingv1alpha1.SubscriptionStatusPhysicalSubscription{ - From: corev1.ObjectReference{ - APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), - Kind: channelKind, - Name: fromChannelName, - }, CallDomain: targetDNS, ResultDomain: sinkableDNS, } @@ -872,7 +791,6 @@ func getSubscriptionWithDifferentChannel() *eventingv1alpha1.Subscription { s := getNewSubscriptionWithSourceWithReferencesResolvedAndPhysicalFromCallResult() s.Name = "different-channel" s.UID = "different-channel-UID" - s.Status.PhysicalSubscription.From.Name = "other-channel" s.Status.PhysicalSubscription.CallDomain = "some-other-domain" return s } From 5d73d6eac49404d1a7db461249d9219de7709a9f Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Thu, 25 Oct 2018 16:53:32 -0700 Subject: [PATCH 03/54] Change the Channelable interface to use URIs. (#561) * Change Channelable to match the definition in https://github.com/knative/eventing/pull/482. * No longer use the subscribable interface. * Move the Channelable duck type into the eventing repo. --- hack/update-codegen.sh | 6 + pkg/apis/duck/v1alpha1/channelable_types.go | 87 +++++++++++ pkg/apis/duck/v1alpha1/doc.go | 20 +++ .../duck/v1alpha1/zz_generated.deepcopy.go | 139 ++++++++++++++++++ pkg/apis/eventing/v1alpha1/channel_types.go | 3 +- .../eventing/v1alpha1/channel_validation.go | 4 +- .../v1alpha1/channel_validation_test.go | 28 ++-- pkg/apis/eventing/v1alpha1/implements_test.go | 3 +- .../eventing/v1alpha1/subscription_types.go | 8 +- .../v1alpha1/zz_generated.deepcopy.go | 9 +- .../inmemory/channel/reconcile_test.go | 40 ++--- .../eventing/subscription/reconcile.go | 53 ++++--- .../eventing/subscription/reconcile_test.go | 32 ++-- .../filesystem/filesystem_watcher_test.go | 48 +++--- pkg/sidecar/configmap/parse_test.go | 49 +++--- pkg/sidecar/configmap/watcher/watcher_test.go | 15 +- pkg/sidecar/fanout/fanout_handler.go | 10 +- pkg/sidecar/fanout/fanout_handler_test.go | 73 ++++----- .../multi_channel_fanout_handler_test.go | 45 +++--- pkg/sidecar/multichannelfanout/parse_test.go | 49 +++--- pkg/sidecar/swappable/swappable_test.go | 29 ++-- 21 files changed, 512 insertions(+), 238 deletions(-) create mode 100644 pkg/apis/duck/v1alpha1/channelable_types.go create mode 100644 pkg/apis/duck/v1alpha1/doc.go create mode 100644 pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index 9301dc91d9d..6422e616532 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -31,5 +31,11 @@ ${CODEGEN_PKG}/generate-groups.sh "deepcopy,client,informer,lister" \ "channels:v1alpha1 feeds:v1alpha1 flows:v1alpha1 eventing:v1alpha1" \ --go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt +# Only deepcopy the Duck types, as they are not real resources. +${CODEGEN_PKG}/generate-groups.sh "deepcopy" \ + github.com/knative/eventing/pkg/client github.com/knative/eventing/pkg/apis \ + "duck:v1alpha1" \ + --go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt + # Make sure our dependencies are up-to-date ${REPO_ROOT_DIR}/hack/update-deps.sh diff --git a/pkg/apis/duck/v1alpha1/channelable_types.go b/pkg/apis/duck/v1alpha1/channelable_types.go new file mode 100644 index 00000000000..ab33f4db72b --- /dev/null +++ b/pkg/apis/duck/v1alpha1/channelable_types.go @@ -0,0 +1,87 @@ +/* + * Copyright 2018 The Knative Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package v1alpha1 + +import ( + "github.com/knative/pkg/apis/duck" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// Channelable is the schema for the channelable portion of the spec +// section of the resource. +type Channelable struct { + // TODO: What is actually required here for Channel spec. + // This is the list of subscriptions for this channel. + Subscribers []ChannelSubscriberSpec `json:"subscribers,omitempty"` +} + +// ChannelSubscriberSpec defines a single subscriber to a Channel. +// CallableURI is the endpoint for the call +// SinkableURI is the endpoint for the result +// At least one of them must be present +type ChannelSubscriberSpec struct { + // +optional + CallableURI string `json:"callableURI,omitempty"` + // +optional + SinkableURI string `json:"sinkableURI,omitempty"` +} + +// DuckChannel is a skeleton type wrapping Channelable in the manner we expect resource writers +// defining compatible resources to embed it. We will typically use this type to deserialize +// Channelable ObjectReferences and access the Channelable data. This is not a real resource. +type Channel struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + // ChannelSpec is the part where Channelable object is + // configured as to be compatible with Channelable contract. + Spec ChannelSpec `json:"spec"` +} + +// DuckChannelSpec shows how we expect folks to embed Channelable in their Spec field. +type ChannelSpec struct { + Channelable *Channelable `json:"channelable,omitempty"` +} + +// GetFullType implements duck.Implementable +func (_ *Channelable) GetFullType() duck.Populatable { + return &Channel{} +} + +// Populate implements duck.Populatable +func (t *Channel) Populate() { + t.Spec.Channelable = &Channelable{ + // Populate ALL fields + Subscribers: []ChannelSubscriberSpec{{"call1", "sink2"}, {"call2", "sink2"}}, + } +} + +// GetListType implements apis.Listable +func (r *Channel) GetListType() runtime.Object { + return &ChannelList{} +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ChannelList is a list of Channel resources +type ChannelList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []Channel `json:"items"` +} diff --git a/pkg/apis/duck/v1alpha1/doc.go b/pkg/apis/duck/v1alpha1/doc.go new file mode 100644 index 00000000000..0dcbcff0129 --- /dev/null +++ b/pkg/apis/duck/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2018 The Knative Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Api versions allow the api contract for a resource to be changed while keeping +// backward compatibility by support multiple concurrent versions +// of the same resource + +// +k8s:deepcopy-gen=package +// +groupName=duck.knative.dev +package v1alpha1 diff --git a/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000000..c3707fddd3d --- /dev/null +++ b/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,139 @@ +// +build !ignore_autogenerated + +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Channel) DeepCopyInto(out *Channel) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Channel. +func (in *Channel) DeepCopy() *Channel { + if in == nil { + return nil + } + out := new(Channel) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ChannelList) DeepCopyInto(out *ChannelList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Channel, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelList. +func (in *ChannelList) DeepCopy() *ChannelList { + if in == nil { + return nil + } + out := new(ChannelList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ChannelList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ChannelSpec) DeepCopyInto(out *ChannelSpec) { + *out = *in + if in.Channelable != nil { + in, out := &in.Channelable, &out.Channelable + if *in == nil { + *out = nil + } else { + *out = new(Channelable) + (*in).DeepCopyInto(*out) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelSpec. +func (in *ChannelSpec) DeepCopy() *ChannelSpec { + if in == nil { + return nil + } + out := new(ChannelSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ChannelSubscriberSpec) DeepCopyInto(out *ChannelSubscriberSpec) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelSubscriberSpec. +func (in *ChannelSubscriberSpec) DeepCopy() *ChannelSubscriberSpec { + if in == nil { + return nil + } + out := new(ChannelSubscriberSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Channelable) DeepCopyInto(out *Channelable) { + *out = *in + if in.Subscribers != nil { + in, out := &in.Subscribers, &out.Subscribers + *out = make([]ChannelSubscriberSpec, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Channelable. +func (in *Channelable) DeepCopy() *Channelable { + if in == nil { + return nil + } + out := new(Channelable) + in.DeepCopyInto(out) + return out +} diff --git a/pkg/apis/eventing/v1alpha1/channel_types.go b/pkg/apis/eventing/v1alpha1/channel_types.go index 697dda0fb79..56702f56d00 100644 --- a/pkg/apis/eventing/v1alpha1/channel_types.go +++ b/pkg/apis/eventing/v1alpha1/channel_types.go @@ -17,6 +17,7 @@ package v1alpha1 import ( + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "github.com/knative/pkg/apis" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" "github.com/knative/pkg/webhook" @@ -72,7 +73,7 @@ type ChannelSpec struct { Arguments *runtime.RawExtension `json:"arguments,omitempty"` // Channel conforms to Duck type Channelable. - Channelable *duckv1alpha1.Channelable `json:"channelable,omitempty"` + Channelable *eventingduck.Channelable `json:"channelable,omitempty"` } var chanCondSet = duckv1alpha1.NewLivingConditionSet(ChannelConditionProvisioned, ChannelConditionSinkable) diff --git a/pkg/apis/eventing/v1alpha1/channel_validation.go b/pkg/apis/eventing/v1alpha1/channel_validation.go index 1a41996e3ae..0840dbec25c 100644 --- a/pkg/apis/eventing/v1alpha1/channel_validation.go +++ b/pkg/apis/eventing/v1alpha1/channel_validation.go @@ -36,8 +36,8 @@ func (cs *ChannelSpec) Validate() *apis.FieldError { if cs.Channelable != nil { for i, subscriber := range cs.Channelable.Subscribers { - if subscriber.SinkableDomain == "" && subscriber.CallableDomain == "" { - fe := apis.ErrMissingField("sinkableDomain", "callableDomain") + if subscriber.SinkableURI == "" && subscriber.CallableURI == "" { + fe := apis.ErrMissingField("sinkableURI", "callableURI") fe.Details = "expected at least one of, got none" errs = errs.Also(fe.ViaField(fmt.Sprintf("subscriber[%d]", i)).ViaField("channelable")) } diff --git a/pkg/apis/eventing/v1alpha1/channel_validation_test.go b/pkg/apis/eventing/v1alpha1/channel_validation_test.go index e72623cf818..6255252a45d 100644 --- a/pkg/apis/eventing/v1alpha1/channel_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/channel_validation_test.go @@ -20,8 +20,8 @@ import ( "testing" "github.com/google/go-cmp/cmp" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "github.com/knative/pkg/apis" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -56,10 +56,10 @@ func TestChannelValidation(t *testing.T) { Name: "foo", }, }, - Channelable: &duckv1alpha1.Channelable{ - Subscribers: []duckv1alpha1.ChannelSubscriberSpec{{ - CallableDomain: "callableendpoint", - SinkableDomain: "resultendpoint", + Channelable: &eventingduck.Channelable{ + Subscribers: []eventingduck.ChannelSubscriberSpec{{ + CallableURI: "callableendpoint", + SinkableURI: "resultendpoint", }}, }}, }, @@ -73,15 +73,15 @@ func TestChannelValidation(t *testing.T) { Name: "foo", }, }, - Channelable: &duckv1alpha1.Channelable{ - Subscribers: []duckv1alpha1.ChannelSubscriberSpec{{ - CallableDomain: "callableendpoint", - SinkableDomain: "callableendpoint", + Channelable: &eventingduck.Channelable{ + Subscribers: []eventingduck.ChannelSubscriberSpec{{ + CallableURI: "callableendpoint", + SinkableURI: "callableendpoint", }, {}}, }}, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("spec.channelable.subscriber[1].callableDomain", "spec.channelable.subscriber[1].sinkableDomain") + fe := apis.ErrMissingField("spec.channelable.subscriber[1].callableURI", "spec.channelable.subscriber[1].sinkableURI") fe.Details = "expected at least one of, got none" return fe }(), @@ -94,17 +94,17 @@ func TestChannelValidation(t *testing.T) { Name: "foo", }, }, - Channelable: &duckv1alpha1.Channelable{ - Subscribers: []duckv1alpha1.ChannelSubscriberSpec{{}, {}}, + Channelable: &eventingduck.Channelable{ + Subscribers: []eventingduck.ChannelSubscriberSpec{{}, {}}, }, }, }, want: func() *apis.FieldError { var errs *apis.FieldError - fe := apis.ErrMissingField("spec.channelable.subscriber[0].callableDomain", "spec.channelable.subscriber[0].sinkableDomain") + fe := apis.ErrMissingField("spec.channelable.subscriber[0].callableURI", "spec.channelable.subscriber[0].sinkableURI") fe.Details = "expected at least one of, got none" errs = errs.Also(fe) - fe = apis.ErrMissingField("spec.channelable.subscriber[1].callableDomain", "spec.channelable.subscriber[1].sinkableDomain") + fe = apis.ErrMissingField("spec.channelable.subscriber[1].callableURI", "spec.channelable.subscriber[1].sinkableURI") fe.Details = "expected at least one of, got none" errs = errs.Also(fe) return errs diff --git a/pkg/apis/eventing/v1alpha1/implements_test.go b/pkg/apis/eventing/v1alpha1/implements_test.go index 1e0198fe50c..1443a89a455 100644 --- a/pkg/apis/eventing/v1alpha1/implements_test.go +++ b/pkg/apis/eventing/v1alpha1/implements_test.go @@ -15,6 +15,7 @@ package v1alpha1 import ( "testing" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "github.com/knative/pkg/apis/duck" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" ) @@ -27,7 +28,7 @@ func TestTypesImplements(t *testing.T) { }{ // Channel {instance: &Channel{}, iface: &duckv1alpha1.Conditions{}}, - {instance: &Channel{}, iface: &duckv1alpha1.Channelable{}}, + {instance: &Channel{}, iface: &eventingduck.Channelable{}}, {instance: &Channel{}, iface: &duckv1alpha1.Sinkable{}}, // ClusterProvisioner {instance: &ClusterProvisioner{}, iface: &duckv1alpha1.Conditions{}}, diff --git a/pkg/apis/eventing/v1alpha1/subscription_types.go b/pkg/apis/eventing/v1alpha1/subscription_types.go index 567d156a468..226707d5f38 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_types.go +++ b/pkg/apis/eventing/v1alpha1/subscription_types.go @@ -176,11 +176,11 @@ type SubscriptionStatus struct { // SubscriptionStatusPhysicalSubscription represents the fully resolved values for this // Subscription. type SubscriptionStatusPhysicalSubscription struct { - // CallDomain is the fully resolved domain for spec.callable. - CallDomain string `json:"callDomain,omitEmpty"` + // CallURI is the fully resolved URI for spec.callable. + CallURI string `json:"callURI,omitEmpty"` - // ResultDomain is the fully resolved domain for the spec.result. - ResultDomain string `json:"resultDomain,omitEmpty"` + // ResultURI is the fully resolved URI for the spec.result. + ResultURI string `json:"resultURI,omitEmpty"` } const ( diff --git a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go index 4f946e69bac..01a36278187 100644 --- a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go @@ -21,7 +21,8 @@ limitations under the License. package v1alpha1 import ( - duck_v1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + duck_v1alpha1 "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + apis_duck_v1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" v1 "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -136,7 +137,7 @@ func (in *ChannelStatus) DeepCopyInto(out *ChannelStatus) { out.Sinkable = in.Sinkable if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make(duck_v1alpha1.Conditions, len(*in)) + *out = make(apis_duck_v1alpha1.Conditions, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -237,7 +238,7 @@ func (in *ClusterProvisionerStatus) DeepCopyInto(out *ClusterProvisionerStatus) *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make(duck_v1alpha1.Conditions, len(*in)) + *out = make(apis_duck_v1alpha1.Conditions, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -440,7 +441,7 @@ func (in *SubscriptionStatus) DeepCopyInto(out *SubscriptionStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make(duck_v1alpha1.Conditions, len(*in)) + *out = make(apis_duck_v1alpha1.Conditions, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index e1905b74570..1d31a9d02a6 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -24,12 +24,12 @@ import ( "testing" "github.com/google/go-cmp/cmp" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" controllertesting "github.com/knative/eventing/pkg/controller/testing" "github.com/knative/eventing/pkg/sidecar/configmap" "github.com/knative/eventing/pkg/sidecar/fanout" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" @@ -73,16 +73,16 @@ var ( Namespace: cNamespace, Name: "c1", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "foo", + CallableURI: "foo", }, { - SinkableDomain: "bar", + SinkableURI: "bar", }, { - CallableDomain: "baz", - SinkableDomain: "qux", + CallableURI: "baz", + SinkableURI: "qux", }, }, }, @@ -91,9 +91,9 @@ var ( Namespace: cNamespace, Name: "c3", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "steve", + CallableURI: "steve", }, }, }, @@ -116,17 +116,17 @@ var ( Name: cpName, }, }, - Channelable: &duckv1alpha1.Channelable{ - Subscribers: []duckv1alpha1.ChannelSubscriberSpec{ + Channelable: &eventingduck.Channelable{ + Subscribers: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "foo", + CallableURI: "foo", }, { - SinkableDomain: "bar", + SinkableURI: "bar", }, { - CallableDomain: "baz", - SinkableDomain: "qux", + CallableURI: "baz", + SinkableURI: "qux", }, }, }, @@ -146,10 +146,10 @@ var ( Name: "some-other-provisioner", }, }, - Channelable: &duckv1alpha1.Channelable{ - Subscribers: []duckv1alpha1.ChannelSubscriberSpec{ + Channelable: &eventingduck.Channelable{ + Subscribers: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "anything", + CallableURI: "anything", }, }, }, @@ -169,10 +169,10 @@ var ( Name: cpName, }, }, - Channelable: &duckv1alpha1.Channelable{ - Subscribers: []duckv1alpha1.ChannelSubscriberSpec{ + Channelable: &eventingduck.Channelable{ + Subscribers: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "steve", + CallableURI: "steve", }, }, }, diff --git a/pkg/controller/eventing/subscription/reconcile.go b/pkg/controller/eventing/subscription/reconcile.go index 759ec89f6c7..cea0c518c48 100644 --- a/pkg/controller/eventing/subscription/reconcile.go +++ b/pkg/controller/eventing/subscription/reconcile.go @@ -19,8 +19,10 @@ package subscription import ( "context" "fmt" + "net/url" "github.com/golang/glog" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/controller" duckapis "github.com/knative/pkg/apis" @@ -93,33 +95,33 @@ func (r *reconciler) reconcile(subscription *v1alpha1.Subscription) error { return err } - callDomain := "" + callURI := "" if subscription.Spec.Call != nil { - callDomain, err = r.resolveEndpointSpec(subscription.Namespace, *subscription.Spec.Call) + callURI, err = r.resolveEndpointSpec(subscription.Namespace, *subscription.Spec.Call) if err != nil { glog.Warningf("Failed to resolve Call %+v : %s", *subscription.Spec.Call, err) return err } - if callDomain == "" { + if callURI == "" { return fmt.Errorf("could not get domain from call (is it not targetable?)") } - subscription.Status.PhysicalSubscription.CallDomain = callDomain - glog.Infof("Resolved call to: %q", callDomain) + subscription.Status.PhysicalSubscription.CallURI = callURI + glog.Infof("Resolved call to: %q", callURI) } - resultDomain := "" + resultURI := "" if subscription.Spec.Result != nil { - resultDomain, err = r.resolveResult(subscription.Namespace, *subscription.Spec.Result) + resultURI, err = r.resolveResult(subscription.Namespace, *subscription.Spec.Result) if err != nil { glog.Warningf("Failed to resolve Result %v : %v", subscription.Spec.Result, err) return err } - if resultDomain == "" { + if resultURI == "" { glog.Warningf("Failed to resolve result %v to actual domain", *subscription.Spec.Result) return err } - subscription.Status.PhysicalSubscription.ResultDomain = resultDomain - glog.Infof("Resolved result to: %q", resultDomain) + subscription.Status.PhysicalSubscription.ResultURI = resultURI + glog.Infof("Resolved result to: %q", resultURI) } // Everything that was supposed to be resolved was, so flip the status bit on that. @@ -179,7 +181,7 @@ func (r *reconciler) resolveEndpointSpec(namespace string, es v1alpha1.EndpointS glog.Warningf("Failed to fetch EndpointSpec target as a K8s Service %+v: %s", es.TargetRef, err) return "", err } - return controller.ServiceHostName(svc.Name, svc.Namespace), nil + return domainToURL(controller.ServiceHostName(svc.Name, svc.Namespace)), nil } obj, err := r.fetchObjectReference(namespace, es.TargetRef) @@ -195,7 +197,7 @@ func (r *reconciler) resolveEndpointSpec(namespace string, es v1alpha1.EndpointS } if t.Status.Targetable != nil { - return t.Status.Targetable.DomainInternal, nil + return domainToURL(t.Status.Targetable.DomainInternal), nil } return "", fmt.Errorf("status does not contain targetable") } @@ -214,7 +216,7 @@ func (r *reconciler) resolveResult(namespace string, resultStrategy v1alpha1.Res return "", err } if s.Status.Sinkable != nil { - return s.Status.Sinkable.DomainInternal, nil + return domainToURL(s.Status.Sinkable.DomainInternal), nil } return "", fmt.Errorf("status does not contain sinkable") } @@ -230,6 +232,15 @@ func (r *reconciler) fetchObjectReference(namespace string, ref *corev1.ObjectRe return resourceClient.Get(ref.Name, metav1.GetOptions{}) } +func domainToURL(domain string) string { + u := url.URL{ + Scheme: "http", + Host: domain, + Path: "/", + } + return u.String() +} + func (r *reconciler) syncPhysicalFromChannel(sub *v1alpha1.Subscription) error { glog.Infof("Reconciling Physical From Channel: %+v", sub) @@ -286,26 +297,26 @@ func (r *reconciler) listAllSubscriptionsWithPhysicalFrom(sub *v1alpha1.Subscrip } } -func (r *reconciler) createChannelable(subs []v1alpha1.Subscription) *duckv1alpha1.Channelable { - rv := &duckv1alpha1.Channelable{} +func (r *reconciler) createChannelable(subs []v1alpha1.Subscription) *eventingduck.Channelable { + rv := &eventingduck.Channelable{} for _, sub := range subs { - if sub.Status.PhysicalSubscription.CallDomain != "" || sub.Status.PhysicalSubscription.ResultDomain != "" { - rv.Subscribers = append(rv.Subscribers, duckv1alpha1.ChannelSubscriberSpec{ - CallableDomain: sub.Status.PhysicalSubscription.CallDomain, - SinkableDomain: sub.Status.PhysicalSubscription.ResultDomain, + if sub.Status.PhysicalSubscription.CallURI != "" || sub.Status.PhysicalSubscription.ResultURI != "" { + rv.Subscribers = append(rv.Subscribers, eventingduck.ChannelSubscriberSpec{ + CallableURI: sub.Status.PhysicalSubscription.CallURI, + SinkableURI: sub.Status.PhysicalSubscription.ResultURI, }) } } return rv } -func (r *reconciler) patchPhysicalFrom(namespace string, physicalFrom corev1.ObjectReference, subs *duckv1alpha1.Channelable) error { +func (r *reconciler) patchPhysicalFrom(namespace string, physicalFrom corev1.ObjectReference, subs *eventingduck.Channelable) error { // First get the original object and convert it to only the bits we care about s, err := r.fetchObjectReference(namespace, &physicalFrom) if err != nil { return err } - original := duckv1alpha1.Channel{} + original := eventingduck.Channel{} err = duck.FromUnstructured(s, &original) if err != nil { return err diff --git a/pkg/controller/eventing/subscription/reconcile_test.go b/pkg/controller/eventing/subscription/reconcile_test.go index dbc7d036c2d..d8dec2b7f7c 100644 --- a/pkg/controller/eventing/subscription/reconcile_test.go +++ b/pkg/controller/eventing/subscription/reconcile_test.go @@ -20,6 +20,7 @@ import ( "fmt" "testing" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" controllertesting "github.com/knative/eventing/pkg/controller/testing" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" @@ -646,8 +647,8 @@ func getNewChannel(name string) *eventingv1alpha1.Channel { func rename(sub *eventingv1alpha1.Subscription) *eventingv1alpha1.Subscription { sub.Name = "renamed" sub.UID = "renamed-UID" - sub.Status.PhysicalSubscription.CallDomain = "" - sub.Status.PhysicalSubscription.ResultDomain = otherSinkableDNS + sub.Status.PhysicalSubscription.CallURI = "" + sub.Status.PhysicalSubscription.ResultURI = otherSinkableDNS return sub } @@ -744,18 +745,17 @@ func getNewSubscriptionWithUnknownConditions() *eventingv1alpha1.Subscription { s.Status.InitializeConditions() return s } - func getNewSubscriptionWithUnknownConditionsAndPhysicalCall() *eventingv1alpha1.Subscription { s := getNewSubscriptionWithUnknownConditions() - s.Status.PhysicalSubscription.CallDomain = targetDNS + s.Status.PhysicalSubscription.CallURI = domainToURL(targetDNS) return s } func getNewSubscriptionWithReferencesResolvedAndPhysicalCallResult() *eventingv1alpha1.Subscription { s := getNewSubscriptionWithUnknownConditions() s.Status.MarkReferencesResolved() - s.Status.PhysicalSubscription.CallDomain = targetDNS - s.Status.PhysicalSubscription.ResultDomain = sinkableDNS + s.Status.PhysicalSubscription.CallURI = domainToURL(targetDNS) + s.Status.PhysicalSubscription.ResultURI = domainToURL(sinkableDNS) return s } @@ -764,8 +764,8 @@ func getNewSubscriptionToK8sServiceWithReferencesResolvedAndPhysicalFromCallResu s.Status.InitializeConditions() s.Status.MarkReferencesResolved() s.Status.PhysicalSubscription = eventingv1alpha1.SubscriptionStatusPhysicalSubscription{ - CallDomain: k8sServiceDNS, - ResultDomain: sinkableDNS, + CallURI: domainToURL(k8sServiceDNS), + ResultURI: domainToURL(sinkableDNS), } return s } @@ -775,8 +775,8 @@ func getNewSubscriptionWithSourceWithReferencesResolvedAndPhysicalFromCallResult s.Status.InitializeConditions() s.Status.MarkReferencesResolved() s.Status.PhysicalSubscription = eventingv1alpha1.SubscriptionStatusPhysicalSubscription{ - CallDomain: targetDNS, - ResultDomain: sinkableDNS, + CallURI: domainToURL(targetDNS), + ResultURI: domainToURL(sinkableDNS), } return s } @@ -791,7 +791,7 @@ func getSubscriptionWithDifferentChannel() *eventingv1alpha1.Subscription { s := getNewSubscriptionWithSourceWithReferencesResolvedAndPhysicalFromCallResult() s.Name = "different-channel" s.UID = "different-channel-UID" - s.Status.PhysicalSubscription.CallDomain = "some-other-domain" + s.Status.PhysicalSubscription.CallURI = "some-other-domain" return s } @@ -830,14 +830,14 @@ func getChannelWithMultipleSubscriptions() *eventingv1alpha1.Channel { }, ObjectMeta: om(testNS, fromChannelName), Spec: eventingv1alpha1.ChannelSpec{ - Channelable: &duckv1alpha1.Channelable{ - Subscribers: []duckv1alpha1.ChannelSubscriberSpec{ + Channelable: &eventingduck.Channelable{ + Subscribers: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: targetDNS, - SinkableDomain: sinkableDNS, + CallableURI: targetDNS, + SinkableURI: sinkableDNS, }, { - SinkableDomain: otherSinkableDNS, + SinkableURI: otherSinkableDNS, }, }, }, diff --git a/pkg/sidecar/configmap/filesystem/filesystem_watcher_test.go b/pkg/sidecar/configmap/filesystem/filesystem_watcher_test.go index f36e3275018..2e54aad92c8 100644 --- a/pkg/sidecar/configmap/filesystem/filesystem_watcher_test.go +++ b/pkg/sidecar/configmap/filesystem/filesystem_watcher_test.go @@ -27,10 +27,10 @@ import ( "time" "github.com/google/go-cmp/cmp" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "github.com/knative/eventing/pkg/sidecar/configmap" "github.com/knative/eventing/pkg/sidecar/fanout" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" "go.uber.org/zap" yaml "gopkg.in/yaml.v2" ) @@ -83,20 +83,20 @@ func TestReadConfigMap(t *testing.T) { name: c1 fanoutConfig: subscriptions: - - callableDomain: event-changer.default.svc.cluster.local - sinkableDomain: message-dumper-bar.default.svc.cluster.local - - callableDomain: message-dumper-foo.default.svc.cluster.local - - sinkableDomain: message-dumper-bar.default.svc.cluster.local + - callableURI: event-changer.default.svc.cluster.local + sinkableURI: message-dumper-bar.default.svc.cluster.local + - callableURI: message-dumper-foo.default.svc.cluster.local + - sinkableURI: message-dumper-bar.default.svc.cluster.local - namespace: default name: c2 fanoutConfig: subscriptions: - - sinkableDomain: message-dumper-foo.default.svc.cluster.local + - sinkableURI: message-dumper-foo.default.svc.cluster.local - namespace: other name: c3 fanoutConfig: subscriptions: - - sinkableDomain: message-dumper-foo.default.svc.cluster.local + - sinkableURI: message-dumper-foo.default.svc.cluster.local `, expected: &multichannelfanout.Config{ ChannelConfigs: []multichannelfanout.ChannelConfig{ @@ -104,16 +104,16 @@ func TestReadConfigMap(t *testing.T) { Namespace: "default", Name: "c1", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "event-changer.default.svc.cluster.local", - SinkableDomain: "message-dumper-bar.default.svc.cluster.local", + CallableURI: "event-changer.default.svc.cluster.local", + SinkableURI: "message-dumper-bar.default.svc.cluster.local", }, { - CallableDomain: "message-dumper-foo.default.svc.cluster.local", + CallableURI: "message-dumper-foo.default.svc.cluster.local", }, { - SinkableDomain: "message-dumper-bar.default.svc.cluster.local", + SinkableURI: "message-dumper-bar.default.svc.cluster.local", }, }, }, @@ -122,9 +122,9 @@ func TestReadConfigMap(t *testing.T) { Namespace: "default", Name: "c2", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: "message-dumper-foo.default.svc.cluster.local", + SinkableURI: "message-dumper-foo.default.svc.cluster.local", }, }, }, @@ -133,9 +133,9 @@ func TestReadConfigMap(t *testing.T) { Namespace: "other", Name: "c3", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: "message-dumper-foo.default.svc.cluster.local", + SinkableURI: "message-dumper-foo.default.svc.cluster.local", }, }, }, @@ -186,9 +186,9 @@ func TestWatch(t *testing.T) { Namespace: "default", Name: "c1", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: "foo.bar", + SinkableURI: "foo.bar", }, }, }, @@ -203,9 +203,9 @@ func TestWatch(t *testing.T) { Namespace: "default", Name: "c1", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: "foo.bar", + SinkableURI: "foo.bar", }, }, }, @@ -221,9 +221,9 @@ func TestWatch(t *testing.T) { Namespace: "default", Name: "c1", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: "foo.bar", + SinkableURI: "foo.bar", }, }, }, @@ -236,9 +236,9 @@ func TestWatch(t *testing.T) { Namespace: "default", Name: "new-channel", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "baz.qux", + CallableURI: "baz.qux", }, }, }, diff --git a/pkg/sidecar/configmap/parse_test.go b/pkg/sidecar/configmap/parse_test.go index 5cddb280b89..135f7620424 100644 --- a/pkg/sidecar/configmap/parse_test.go +++ b/pkg/sidecar/configmap/parse_test.go @@ -17,13 +17,14 @@ limitations under the License. package configmap import ( + "strings" + "testing" + "github.com/google/go-cmp/cmp" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "github.com/knative/eventing/pkg/sidecar/fanout" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" "go.uber.org/zap" - "strings" - "testing" ) func TestNewFanoutConfig(t *testing.T) { @@ -64,20 +65,20 @@ func TestNewFanoutConfig(t *testing.T) { name: c1 fanoutConfig: subscriptions: - - callableDomain: event-changer.default.svc.cluster.local - sinkableDomain: message-dumper-bar.default.svc.cluster.local - - callableDomain: message-dumper-foo.default.svc.cluster.local - - sinkableDomain: message-dumper-bar.default.svc.cluster.local + - callableURI: event-changer.default.svc.cluster.local + sinkableURI: message-dumper-bar.default.svc.cluster.local + - callableURI: message-dumper-foo.default.svc.cluster.local + - sinkableURI: message-dumper-bar.default.svc.cluster.local - namespace: default name: c2 fanoutConfig: subscriptions: - - sinkableDomain: message-dumper-foo.default.svc.cluster.local + - sinkableURI: message-dumper-foo.default.svc.cluster.local - namespace: other name: c3 fanoutConfig: subscriptions: - - sinkableDomain: message-dumper-foo.default.svc.cluster.local + - sinkableURI: message-dumper-foo.default.svc.cluster.local `, expected: &multichannelfanout.Config{ ChannelConfigs: []multichannelfanout.ChannelConfig{ @@ -85,16 +86,16 @@ func TestNewFanoutConfig(t *testing.T) { Namespace: "default", Name: "c1", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "event-changer.default.svc.cluster.local", - SinkableDomain: "message-dumper-bar.default.svc.cluster.local", + CallableURI: "event-changer.default.svc.cluster.local", + SinkableURI: "message-dumper-bar.default.svc.cluster.local", }, { - CallableDomain: "message-dumper-foo.default.svc.cluster.local", + CallableURI: "message-dumper-foo.default.svc.cluster.local", }, { - SinkableDomain: "message-dumper-bar.default.svc.cluster.local", + SinkableURI: "message-dumper-bar.default.svc.cluster.local", }, }, }, @@ -103,9 +104,9 @@ func TestNewFanoutConfig(t *testing.T) { Namespace: "default", Name: "c2", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: "message-dumper-foo.default.svc.cluster.local", + SinkableURI: "message-dumper-foo.default.svc.cluster.local", }, }, }, @@ -114,9 +115,9 @@ func TestNewFanoutConfig(t *testing.T) { Namespace: "other", Name: "c3", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: "message-dumper-foo.default.svc.cluster.local", + SinkableURI: "message-dumper-foo.default.svc.cluster.local", }, }, }, @@ -156,16 +157,16 @@ func TestSerializeConfig(t *testing.T) { Namespace: "default", Name: "c1", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "foo.example.com", - SinkableDomain: "bar.example.com", + CallableURI: "foo.example.com", + SinkableURI: "bar.example.com", }, { - SinkableDomain: "qux.example.com", + SinkableURI: "qux.example.com", }, { - CallableDomain: "baz.example.com", + CallableURI: "baz.example.com", }, {}, }, @@ -175,7 +176,7 @@ func TestSerializeConfig(t *testing.T) { Namespace: "other", Name: "no-subs", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{}, + Subscriptions: []eventingduck.ChannelSubscriberSpec{}, }, }, }, diff --git a/pkg/sidecar/configmap/watcher/watcher_test.go b/pkg/sidecar/configmap/watcher/watcher_test.go index e6296e5f201..fd92d6e7ed1 100644 --- a/pkg/sidecar/configmap/watcher/watcher_test.go +++ b/pkg/sidecar/configmap/watcher/watcher_test.go @@ -18,16 +18,17 @@ package watcher import ( "errors" + "testing" + "github.com/google/go-cmp/cmp" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" sidecarconfigmap "github.com/knative/eventing/pkg/sidecar/configmap" "github.com/knative/eventing/pkg/sidecar/fanout" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" "github.com/knative/pkg/configmap" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "testing" ) const ( @@ -66,8 +67,8 @@ func TestReconcile(t *testing.T) { namespace: bar fanoutConfig: subscriptions: - - callableDomain: callable - sinkableDomain: sinkable`, + - callableURI: callable + sinkableURI: sinkable`, }, expectedConfig: &multichannelfanout.Config{ ChannelConfigs: []multichannelfanout.ChannelConfig{ @@ -75,10 +76,10 @@ func TestReconcile(t *testing.T) { Name: "foo", Namespace: "bar", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "callable", - SinkableDomain: "sinkable", + CallableURI: "callable", + SinkableURI: "sinkable", }, }, }, diff --git a/pkg/sidecar/fanout/fanout_handler.go b/pkg/sidecar/fanout/fanout_handler.go index 065a10d64f0..48ff82a5988 100644 --- a/pkg/sidecar/fanout/fanout_handler.go +++ b/pkg/sidecar/fanout/fanout_handler.go @@ -26,8 +26,8 @@ import ( "net/http" "time" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "github.com/knative/eventing/pkg/buses" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" "go.uber.org/zap" ) @@ -39,7 +39,7 @@ const ( // Configuration for a fanout.Handler. type Config struct { - Subscriptions []duckv1alpha1.ChannelSubscriberSpec `json:"subscriptions"` + Subscriptions []eventingduck.ChannelSubscriberSpec `json:"subscriptions"` } // http.Handler that takes a single request in and fans it out to N other servers. @@ -96,7 +96,7 @@ func (f *Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { func (f *Handler) dispatch(msg *buses.Message) error { errorCh := make(chan error, len(f.config.Subscriptions)) for _, sub := range f.config.Subscriptions { - go func(s duckv1alpha1.ChannelSubscriberSpec) { + go func(s eventingduck.ChannelSubscriberSpec) { errorCh <- f.makeFanoutRequest(*msg, s) }(sub) } @@ -119,6 +119,6 @@ func (f *Handler) dispatch(msg *buses.Message) error { // makeFanoutRequest sends the request to exactly one subscription. It handles both the `call` and // the `sink` portions of the subscription. -func (f *Handler) makeFanoutRequest(m buses.Message, sub duckv1alpha1.ChannelSubscriberSpec) error { - return f.dispatcher.DispatchMessage(&m, sub.CallableDomain, sub.SinkableDomain, buses.DispatchDefaults{}) +func (f *Handler) makeFanoutRequest(m buses.Message, sub eventingduck.ChannelSubscriberSpec) error { + return f.dispatcher.DispatchMessage(&m, sub.CallableURI, sub.SinkableURI, buses.DispatchDefaults{}) } diff --git a/pkg/sidecar/fanout/fanout_handler_test.go b/pkg/sidecar/fanout/fanout_handler_test.go index 9bdf020e544..b1d2376a408 100644 --- a/pkg/sidecar/fanout/fanout_handler_test.go +++ b/pkg/sidecar/fanout/fanout_handler_test.go @@ -18,10 +18,6 @@ package fanout import ( "errors" - "github.com/knative/eventing/pkg/buses" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" - "go.uber.org/atomic" - "go.uber.org/zap" "io" "io/ioutil" "net/http" @@ -29,6 +25,11 @@ import ( "strings" "testing" "time" + + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + "github.com/knative/eventing/pkg/buses" + "go.uber.org/atomic" + "go.uber.org/zap" ) // Domains used in subscriptions, which will be replaced by the real domains of the started HTTP @@ -59,7 +60,7 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { testCases := map[string]struct { receiverFunc func(buses.ChannelReference, *buses.Message) error timeout time.Duration - subs []duckv1alpha1.ChannelSubscriberSpec + subs []eventingduck.ChannelSubscriberSpec callable func(http.ResponseWriter, *http.Request) sinkable func(http.ResponseWriter, *http.Request) expectedStatus int @@ -72,9 +73,9 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { }, "fanout times out": { timeout: time.Millisecond, - subs: []duckv1alpha1.ChannelSubscriberSpec{ + subs: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: replaceCallable, + CallableURI: replaceCallable, }, }, callable: func(writer http.ResponseWriter, _ *http.Request) { @@ -84,19 +85,19 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { expectedStatus: http.StatusInternalServerError, }, "zero subs succeed": { - subs: []duckv1alpha1.ChannelSubscriberSpec{}, + subs: []eventingduck.ChannelSubscriberSpec{}, expectedStatus: http.StatusAccepted, }, "empty sub succeeds": { - subs: []duckv1alpha1.ChannelSubscriberSpec{ + subs: []eventingduck.ChannelSubscriberSpec{ {}, }, expectedStatus: http.StatusAccepted, }, "sinkable fails": { - subs: []duckv1alpha1.ChannelSubscriberSpec{ + subs: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: replaceSinkable, + SinkableURI: replaceSinkable, }, }, sinkable: func(writer http.ResponseWriter, _ *http.Request) { @@ -105,9 +106,9 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { expectedStatus: http.StatusInternalServerError, }, "callable fails": { - subs: []duckv1alpha1.ChannelSubscriberSpec{ + subs: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: replaceCallable, + CallableURI: replaceCallable, }, }, callable: func(writer http.ResponseWriter, _ *http.Request) { @@ -116,10 +117,10 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { expectedStatus: http.StatusInternalServerError, }, "callable succeeds, sinkable fails": { - subs: []duckv1alpha1.ChannelSubscriberSpec{ + subs: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: replaceCallable, - SinkableDomain: replaceSinkable, + CallableURI: replaceCallable, + SinkableURI: replaceSinkable, }, }, callable: callableSucceed, @@ -129,10 +130,10 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { expectedStatus: http.StatusInternalServerError, }, "one sub succeeds": { - subs: []duckv1alpha1.ChannelSubscriberSpec{ + subs: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: replaceCallable, - SinkableDomain: replaceSinkable, + CallableURI: replaceCallable, + SinkableURI: replaceSinkable, }, }, callable: callableSucceed, @@ -142,14 +143,14 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { expectedStatus: http.StatusAccepted, }, "one sub succeeds, one sub fails": { - subs: []duckv1alpha1.ChannelSubscriberSpec{ + subs: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: replaceCallable, - SinkableDomain: replaceSinkable, + CallableURI: replaceCallable, + SinkableURI: replaceSinkable, }, { - CallableDomain: replaceCallable, - SinkableDomain: replaceSinkable, + CallableURI: replaceCallable, + SinkableURI: replaceSinkable, }, }, callable: callableSucceed, @@ -157,18 +158,18 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { expectedStatus: http.StatusInternalServerError, }, "all subs succeed": { - subs: []duckv1alpha1.ChannelSubscriberSpec{ + subs: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: replaceCallable, - SinkableDomain: replaceSinkable, + CallableURI: replaceCallable, + SinkableURI: replaceSinkable, }, { - CallableDomain: replaceCallable, - SinkableDomain: replaceSinkable, + CallableURI: replaceCallable, + SinkableURI: replaceSinkable, }, { - CallableDomain: replaceCallable, - SinkableDomain: replaceSinkable, + CallableURI: replaceCallable, + SinkableURI: replaceSinkable, }, }, callable: callableSucceed, @@ -193,13 +194,13 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { defer sinkableServer.Close() // Rewrite the subs to use the servers we just started. - subs := make([]duckv1alpha1.ChannelSubscriberSpec, 0) + subs := make([]eventingduck.ChannelSubscriberSpec, 0) for _, sub := range tc.subs { - if sub.CallableDomain == replaceCallable { - sub.CallableDomain = callableServer.URL[7:] // strip the leading 'http://' + if sub.CallableURI == replaceCallable { + sub.CallableURI = callableServer.URL[7:] // strip the leading 'http://' } - if sub.SinkableDomain == replaceSinkable { - sub.SinkableDomain = sinkableServer.URL[7:] // strip the leading 'http://' + if sub.SinkableURI == replaceSinkable { + sub.SinkableURI = sinkableServer.URL[7:] // strip the leading 'http://' } subs = append(subs, sub) } diff --git a/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler_test.go b/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler_test.go index 3bc5e6f0faf..82693b0c3a6 100644 --- a/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler_test.go +++ b/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler_test.go @@ -18,14 +18,15 @@ package multichannelfanout import ( "fmt" - "github.com/google/go-cmp/cmp" - "github.com/knative/eventing/pkg/sidecar/fanout" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" - "go.uber.org/zap" "net/http" "net/http/httptest" "strings" "testing" + + "github.com/google/go-cmp/cmp" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + "github.com/knative/eventing/pkg/sidecar/fanout" + "go.uber.org/zap" ) const ( @@ -108,9 +109,9 @@ func TestCopyWithNewConfig(t *testing.T) { Namespace: "default", Name: "c1", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "callabledomain", + CallableURI: "callabledomain", }, }, }, @@ -123,9 +124,9 @@ func TestCopyWithNewConfig(t *testing.T) { Namespace: "default", Name: "somethingdifferent", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: "sinkabledomain", + SinkableURI: "sinkabledomain", }, }, }, @@ -161,9 +162,9 @@ func TestConfigDiff(t *testing.T) { Namespace: "default", Name: "c1", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "callabledomain", + CallableURI: "callabledomain", }, }, }, @@ -191,9 +192,9 @@ func TestConfigDiff(t *testing.T) { Namespace: "default", Name: "c1", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "different", + CallableURI: "different", }, }, }, @@ -238,9 +239,9 @@ func TestServeHTTP(t *testing.T) { Namespace: "default", Name: "first-channel", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: replaceDomain, + SinkableURI: replaceDomain, }, }, }, @@ -258,9 +259,9 @@ func TestServeHTTP(t *testing.T) { Namespace: "default", Name: "first-channel", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: "first-to-domain", + SinkableURI: "first-to-domain", }, }, }, @@ -269,9 +270,9 @@ func TestServeHTTP(t *testing.T) { Namespace: "default", Name: "second-channel", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: replaceDomain, + CallableURI: replaceDomain, }, }, }, @@ -317,11 +318,11 @@ func TestServeHTTP(t *testing.T) { func replaceDomains(config Config, replacement string) { for i, cc := range config.ChannelConfigs { for j, sub := range cc.FanoutConfig.Subscriptions { - if sub.CallableDomain == replaceDomain { - sub.CallableDomain = replacement + if sub.CallableURI == replaceDomain { + sub.CallableURI = replacement } - if sub.SinkableDomain == replaceDomain { - sub.SinkableDomain = replacement + if sub.SinkableURI == replaceDomain { + sub.SinkableURI = replacement } cc.FanoutConfig.Subscriptions[j] = sub } diff --git a/pkg/sidecar/multichannelfanout/parse_test.go b/pkg/sidecar/multichannelfanout/parse_test.go index 568ac7d3391..c510db6b890 100644 --- a/pkg/sidecar/multichannelfanout/parse_test.go +++ b/pkg/sidecar/multichannelfanout/parse_test.go @@ -17,12 +17,13 @@ limitations under the License. package multichannelfanout import ( + "strings" + "testing" + "github.com/google/go-cmp/cmp" + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" "github.com/knative/eventing/pkg/sidecar/fanout" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" "go.uber.org/zap" - "strings" - "testing" ) func TestConfigMapData(t *testing.T) { @@ -33,11 +34,11 @@ func TestConfigMapData(t *testing.T) { expectedErr bool }{ { - name: "no data", - expected: &Config {}, + name: "no data", + expected: &Config{}, }, { - name: "invalid YAML", + name: "invalid YAML", config: ` key: - value @@ -56,27 +57,27 @@ func TestConfigMapData(t *testing.T) { expectedErr: true, }, { - name: "valid", + name: "valid", config: ` channelConfigs: - namespace: default name: c1 fanoutConfig: subscriptions: - - callableDomain: event-changer.default.svc.cluster.local - sinkableDomain: message-dumper-bar.default.svc.cluster.local - - callableDomain: message-dumper-foo.default.svc.cluster.local - - sinkableDomain: message-dumper-bar.default.svc.cluster.local + - callableURI: event-changer.default.svc.cluster.local + sinkableURI: message-dumper-bar.default.svc.cluster.local + - callableURI: message-dumper-foo.default.svc.cluster.local + - sinkableURI: message-dumper-bar.default.svc.cluster.local - namespace: default name: c2 fanoutConfig: subscriptions: - - sinkableDomain: message-dumper-foo.default.svc.cluster.local + - sinkableURI: message-dumper-foo.default.svc.cluster.local - namespace: other name: c3 fanoutConfig: subscriptions: - - sinkableDomain: message-dumper-foo.default.svc.cluster.local + - sinkableURI: message-dumper-foo.default.svc.cluster.local `, expected: &Config{ ChannelConfigs: []ChannelConfig{ @@ -84,16 +85,16 @@ func TestConfigMapData(t *testing.T) { Namespace: "default", Name: "c1", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: "event-changer.default.svc.cluster.local", - SinkableDomain: "message-dumper-bar.default.svc.cluster.local", + CallableURI: "event-changer.default.svc.cluster.local", + SinkableURI: "message-dumper-bar.default.svc.cluster.local", }, { - CallableDomain: "message-dumper-foo.default.svc.cluster.local", + CallableURI: "message-dumper-foo.default.svc.cluster.local", }, { - SinkableDomain: "message-dumper-bar.default.svc.cluster.local", + SinkableURI: "message-dumper-bar.default.svc.cluster.local", }, }, }, @@ -102,9 +103,9 @@ func TestConfigMapData(t *testing.T) { Namespace: "default", Name: "c2", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: "message-dumper-foo.default.svc.cluster.local", + SinkableURI: "message-dumper-foo.default.svc.cluster.local", }, }, }, @@ -113,9 +114,9 @@ func TestConfigMapData(t *testing.T) { Namespace: "other", Name: "c3", FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: "message-dumper-foo.default.svc.cluster.local", + SinkableURI: "message-dumper-foo.default.svc.cluster.local", }, }, }, @@ -126,7 +127,9 @@ func TestConfigMapData(t *testing.T) { } for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - if tc.name != "valid" { return } + if tc.name != "valid" { + return + } c, e := Parse(zap.NewNop(), formatData(tc.config)) if tc.expectedErr { if e == nil { diff --git a/pkg/sidecar/swappable/swappable_test.go b/pkg/sidecar/swappable/swappable_test.go index 2c663bf800b..13a8b7936f3 100644 --- a/pkg/sidecar/swappable/swappable_test.go +++ b/pkg/sidecar/swappable/swappable_test.go @@ -18,14 +18,15 @@ package swappable import ( "fmt" - "github.com/knative/eventing/pkg/sidecar/fanout" - "github.com/knative/eventing/pkg/sidecar/multichannelfanout" - duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" - "go.uber.org/zap" "net/http" "net/http/httptest" "strings" "testing" + + eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" + "github.com/knative/eventing/pkg/sidecar/fanout" + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" + "go.uber.org/zap" ) const ( @@ -46,9 +47,9 @@ func TestHandler(t *testing.T) { Namespace: namespace, Name: name, FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: replaceDomain, + CallableURI: replaceDomain, }, }, }, @@ -61,9 +62,9 @@ func TestHandler(t *testing.T) { Namespace: namespace, Name: name, FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableDomain: replaceDomain, + SinkableURI: replaceDomain, }, }, }, @@ -98,9 +99,9 @@ func TestHandler_InvalidConfigChange(t *testing.T) { Namespace: namespace, Name: name, FanoutConfig: fanout.Config{ - Subscriptions: []duckv1alpha1.ChannelSubscriberSpec{ + Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableDomain: replaceDomain, + CallableURI: replaceDomain, }, }, }, @@ -203,11 +204,11 @@ func makeRequest(namespace, name string) *http.Request { func replaceDomains(c multichannelfanout.Config, replacement string) multichannelfanout.Config { for i, cc := range c.ChannelConfigs { for j, sub := range cc.FanoutConfig.Subscriptions { - if sub.SinkableDomain == replaceDomain { - sub.SinkableDomain = replacement + if sub.SinkableURI == replaceDomain { + sub.SinkableURI = replacement } - if sub.CallableDomain == replaceDomain { - sub.CallableDomain = replacement + if sub.CallableURI == replaceDomain { + sub.CallableURI = replacement } cc.FanoutConfig.Subscriptions[j] = sub } From b063fed4663bcf2b30ba2e1542a2190fcaeb91b4 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Fri, 26 Oct 2018 12:06:32 -0400 Subject: [PATCH 04/54] Align ClusterChannelProvisioner with spec (#562) * Rename ClusterProvisioner -> ClusterChannelProvisioner * Remove ClusterChannelProvisioner Reconciles field * review feedback --- cmd/webhook/main.go | 6 +- config/300-clusterprovisioner.yaml | 8 +- .../provisioners/in-memory-channel/README.md | 8 +- .../in-memory-channel/in-memory-channel.yaml | 8 +- pkg/apis/eventing/logkey/constants.go | 4 +- ...> cluster_channel_provisioner_defaults.go} | 6 +- ...ster_channel_provisioner_defaults_test.go} | 4 +- ...o => cluster_channel_provisioner_types.go} | 67 ++++---- ...cluster_channel_provisioner_types_test.go} | 28 ++-- ...cluster_channel_provisioner_validation.go} | 18 +-- ...ter_channel_provisioner_validation_test.go | 49 ++++++ .../cluster_provisioner_validation_test.go | 83 ---------- pkg/apis/eventing/v1alpha1/implements_test.go | 4 +- pkg/apis/eventing/v1alpha1/register.go | 4 +- .../v1alpha1/zz_generated.deepcopy.go | 39 +++-- .../v1alpha1/clusterchannelprovisioner.go | 147 ++++++++++++++++++ .../eventing/v1alpha1/clusterprovisioner.go | 147 ------------------ .../eventing/v1alpha1/eventing_client.go | 6 +- .../fake/fake_clusterchannelprovisioner.go | 120 ++++++++++++++ .../v1alpha1/fake/fake_clusterprovisioner.go | 120 -------------- .../v1alpha1/fake/fake_eventing_client.go | 4 +- .../eventing/v1alpha1/generated_expansion.go | 2 +- ...sioner.go => clusterchannelprovisioner.go} | 38 ++--- .../eventing/v1alpha1/interface.go | 10 +- .../informers/externalversions/generic.go | 4 +- .../v1alpha1/clusterchannelprovisioner.go | 65 ++++++++ .../eventing/v1alpha1/clusterprovisioner.go | 65 -------- .../eventing/v1alpha1/expansion_generated.go | 6 +- .../eventing/inmemory/channel/reconcile.go | 6 +- .../inmemory/channel/reconcile_test.go | 12 +- .../controller.go | 14 +- .../reconcile.go | 93 ++++++----- .../reconcile_test.go | 108 ++++++------- .../eventing/inmemory/controller/main.go | 12 +- pkg/controller/owner_references.go | 4 +- 35 files changed, 624 insertions(+), 695 deletions(-) rename pkg/apis/eventing/v1alpha1/{cluster_provisioner_defaults.go => cluster_channel_provisioner_defaults.go} (79%) rename pkg/apis/eventing/v1alpha1/{cluster_provisioner_defaults_test.go => cluster_channel_provisioner_defaults_test.go} (87%) rename pkg/apis/eventing/v1alpha1/{cluster_provisioner_types.go => cluster_channel_provisioner_types.go} (54%) rename pkg/apis/eventing/v1alpha1/{cluster_provisioner_types_test.go => cluster_channel_provisioner_types_test.go} (79%) rename pkg/apis/eventing/v1alpha1/{cluster_provisioner_validation.go => cluster_channel_provisioner_validation.go} (54%) create mode 100644 pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_validation_test.go delete mode 100644 pkg/apis/eventing/v1alpha1/cluster_provisioner_validation_test.go create mode 100644 pkg/client/clientset/versioned/typed/eventing/v1alpha1/clusterchannelprovisioner.go delete mode 100644 pkg/client/clientset/versioned/typed/eventing/v1alpha1/clusterprovisioner.go create mode 100644 pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_clusterchannelprovisioner.go delete mode 100644 pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_clusterprovisioner.go rename pkg/client/informers/externalversions/eventing/v1alpha1/{clusterprovisioner.go => clusterchannelprovisioner.go} (53%) create mode 100644 pkg/client/listers/eventing/v1alpha1/clusterchannelprovisioner.go delete mode 100644 pkg/client/listers/eventing/v1alpha1/clusterprovisioner.go rename pkg/controller/eventing/inmemory/{clusterprovisioner => clusterchannelprovisioner}/controller.go (79%) rename pkg/controller/eventing/inmemory/{clusterprovisioner => clusterchannelprovisioner}/reconcile.go (57%) rename pkg/controller/eventing/inmemory/{clusterprovisioner => clusterchannelprovisioner}/reconcile_test.go (76%) diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index aefd15caff0..0a0c21af2a4 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -90,9 +90,9 @@ func main() { Options: options, Handlers: map[schema.GroupVersionKind]webhook.GenericCRD{ // For group eventing.knative.dev, - eventingv1alpha1.SchemeGroupVersion.WithKind("Channel"): &eventingv1alpha1.Channel{}, - eventingv1alpha1.SchemeGroupVersion.WithKind("ClusterProvisioner"): &eventingv1alpha1.ClusterProvisioner{}, - eventingv1alpha1.SchemeGroupVersion.WithKind("Subscription"): &eventingv1alpha1.Subscription{}, + eventingv1alpha1.SchemeGroupVersion.WithKind("Channel"): &eventingv1alpha1.Channel{}, + eventingv1alpha1.SchemeGroupVersion.WithKind("ClusterChannelProvisioner"): &eventingv1alpha1.ClusterChannelProvisioner{}, + eventingv1alpha1.SchemeGroupVersion.WithKind("Subscription"): &eventingv1alpha1.Subscription{}, // For group channels.knative.dev, channelsv1alpha1.SchemeGroupVersion.WithKind("Bus"): &channelsv1alpha1.Bus{}, diff --git a/config/300-clusterprovisioner.yaml b/config/300-clusterprovisioner.yaml index e8e75547c3f..cb2361f52b8 100644 --- a/config/300-clusterprovisioner.yaml +++ b/config/300-clusterprovisioner.yaml @@ -14,14 +14,14 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: - name: clusterprovisioners.eventing.knative.dev + name: clusterchannelprovisioners.eventing.knative.dev spec: group: eventing.knative.dev version: v1alpha1 names: - kind: ClusterProvisioner - plural: clusterprovisioners - singular: clusterprovisioner + kind: ClusterChannelProvisioner + plural: clusterchannelprovisioners + singular: clusterchannelprovisioner categories: - all - knative diff --git a/config/provisioners/in-memory-channel/README.md b/config/provisioners/in-memory-channel/README.md index d895f9b32c4..81b065f61f1 100644 --- a/config/provisioners/in-memory-channel/README.md +++ b/config/provisioners/in-memory-channel/README.md @@ -18,7 +18,7 @@ They differ from most Channels in that they have: ### Deployment steps: 1. Setup [Knative Eventing](../../../DEVELOPMENT.md). -1. Apply the 'in-memory-channel' ClusterProvisioner, Controller, and Dispatcher. +1. Apply the 'in-memory-channel' ClusterChannelProvisioner, Controller, and Dispatcher. ```shell ko apply -f config/providers/in-memory-channel/in-memory-channel.yaml ```` @@ -33,19 +33,19 @@ They differ from most Channels in that they have: provisioner: ref: apiVersion: eventing.knative.dev/v1alpha1 - kind: ClusterProvisioner + kind: ClusterChannelProvisioner name: in-memory-channel ``` ### Components The major components are: -* ClusterProvisioner Controller +* ClusterChannelProvisioner Controller * Channel Controller * Channel Dispatcher * Channel Dispatcher Config Map. -The ClusterProvisioner Controller and the Channel Controller are colocated in one Pod. +The ClusterChannelProvisioner Controller and the Channel Controller are colocated in one Pod. ```shell kubectl get deployment -n knative-eventing in-memory-channel-controller ``` diff --git a/config/provisioners/in-memory-channel/in-memory-channel.yaml b/config/provisioners/in-memory-channel/in-memory-channel.yaml index 44a15c66175..547dbb3ebc9 100644 --- a/config/provisioners/in-memory-channel/in-memory-channel.yaml +++ b/config/provisioners/in-memory-channel/in-memory-channel.yaml @@ -13,7 +13,7 @@ # limitations under the License. apiVersion: eventing.knative.dev/v1alpha1 -kind: ClusterProvisioner +kind: ClusterChannelProvisioner metadata: name: in-memory-channel spec: @@ -40,7 +40,7 @@ rules: - eventing.knative.dev resources: - channels - - clusterprovisioners + - clusterchannelprovisioners verbs: - get - list @@ -101,7 +101,7 @@ spec: replicas: 1 selector: matchLabels: &labels - clusterProvisioner: in-memory-channel + clusterChannelProvisioner: in-memory-channel role: controller template: metadata: @@ -164,7 +164,7 @@ spec: replicas: 1 selector: matchLabels: &labels - clusterProvisioner: in-memory-channel + clusterChannelProvisioner: in-memory-channel role: dispatcher template: metadata: diff --git a/pkg/apis/eventing/logkey/constants.go b/pkg/apis/eventing/logkey/constants.go index e83233cc9a5..d9d383b18b5 100644 --- a/pkg/apis/eventing/logkey/constants.go +++ b/pkg/apis/eventing/logkey/constants.go @@ -19,6 +19,6 @@ package logkey const ( kNative = "knative.dev/" - // ClusterProvisioner is the key used for cluster provisioner names in structured logs - ClusterProvisioner = kNative + "clusterprovisioner" + // ClusterChannelProvisioner is the key used for cluster provisioner names in structured logs + ClusterChannelProvisioner = kNative + "clusterchannelprovisioner" ) diff --git a/pkg/apis/eventing/v1alpha1/cluster_provisioner_defaults.go b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_defaults.go similarity index 79% rename from pkg/apis/eventing/v1alpha1/cluster_provisioner_defaults.go rename to pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_defaults.go index c5b74ae0da6..59a8089676f 100644 --- a/pkg/apis/eventing/v1alpha1/cluster_provisioner_defaults.go +++ b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_defaults.go @@ -17,11 +17,11 @@ limitations under the License. package v1alpha1 // SetDefaults defaults -func (p *ClusterProvisioner) SetDefaults() { +func (p *ClusterChannelProvisioner) SetDefaults() { p.Spec.SetDefaults() } -// SetDefaults defaults the ClusterProvisioner spec. -func (ps *ClusterProvisionerSpec) SetDefaults() { +// SetDefaults defaults the ClusterChannelProvisioner spec. +func (ps *ClusterChannelProvisionerSpec) SetDefaults() { // no defaults } diff --git a/pkg/apis/eventing/v1alpha1/cluster_provisioner_defaults_test.go b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_defaults_test.go similarity index 87% rename from pkg/apis/eventing/v1alpha1/cluster_provisioner_defaults_test.go rename to pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_defaults_test.go index f193167fa2b..565b2b4a91f 100644 --- a/pkg/apis/eventing/v1alpha1/cluster_provisioner_defaults_test.go +++ b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_defaults_test.go @@ -19,7 +19,7 @@ package v1alpha1 import "testing" // No-op test because method does nothing. -func TestClusterProvisionerSetDefaults(t *testing.T) { - p := ClusterProvisioner{} +func TestClusterChannelProvisionerSetDefaults(t *testing.T) { + p := ClusterChannelProvisioner{} p.SetDefaults() } diff --git a/pkg/apis/eventing/v1alpha1/cluster_provisioner_types.go b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types.go similarity index 54% rename from pkg/apis/eventing/v1alpha1/cluster_provisioner_types.go rename to pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types.go index 6a0a32ecb0b..657bd67bab3 100644 --- a/pkg/apis/eventing/v1alpha1/cluster_provisioner_types.go +++ b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types.go @@ -30,93 +30,88 @@ import ( // +genclient:nonNamespaced // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// ClusterProvisioner encapsulates a provisioning strategy for the backing -// resources required to realize a particular resource type. -type ClusterProvisioner struct { +// ClusterChannelProvisioner encapsulates a provisioning strategy for the +// backing resources required to realize a particular resource type. +type ClusterChannelProvisioner struct { metav1.TypeMeta `json:",inline"` // +optional metav1.ObjectMeta `json:"metadata,omitempty"` // Spec defines the Types provisioned by this Provisioner. - Spec ClusterProvisionerSpec `json:"spec"` + Spec ClusterChannelProvisionerSpec `json:"spec"` // Status is the current status of the Provisioner. // +optional - Status ClusterProvisionerStatus `json:"status,omitempty"` + Status ClusterChannelProvisionerStatus `json:"status,omitempty"` } -// Check that ClusterProvisioner can be validated and can be defaulted. -var _ apis.Validatable = (*ClusterProvisioner)(nil) -var _ apis.Defaultable = (*ClusterProvisioner)(nil) -var _ runtime.Object = (*ClusterProvisioner)(nil) -var _ webhook.GenericCRD = (*ClusterProvisioner)(nil) +// Check that ClusterChannelProvisioner can be validated and can be defaulted. +var _ apis.Validatable = (*ClusterChannelProvisioner)(nil) +var _ apis.Defaultable = (*ClusterChannelProvisioner)(nil) +var _ runtime.Object = (*ClusterChannelProvisioner)(nil) +var _ webhook.GenericCRD = (*ClusterChannelProvisioner)(nil) -// ClusterProvisionerSpec is the spec for a ClusterProvisioner resource. -type ClusterProvisionerSpec struct { +// ClusterChannelProvisionerSpec is the spec for a ClusterChannelProvisioner resource. +type ClusterChannelProvisionerSpec struct { // TODO: Generation does not work correctly with CRD. They are scrubbed // by the APIserver (https://github.com/kubernetes/kubernetes/issues/58778) // So, we add Generation here. Once that gets fixed, remove this and use // ObjectMeta.Generation instead. // +optional Generation int64 `json:"generation,omitempty"` - - // Reconciles is the kind of the resource the provisioner controller watches to - // produce required backing resources. - // +required - Reconciles metav1.GroupKind `json:"reconciles"` } -var cProvCondSet = duckv1alpha1.NewLivingConditionSet() +var ccProvCondSet = duckv1alpha1.NewLivingConditionSet() -// ClusterProvisionerStatus is the status for a ClusterProvisioner resource -type ClusterProvisionerStatus struct { +// ClusterChannelProvisionerStatus is the status for a ClusterChannelProvisioner resource +type ClusterChannelProvisionerStatus struct { // Conditions holds the state of a cluster provisioner at a point in time. // +optional // +patchMergeKey=type // +patchStrategy=merge Conditions duckv1alpha1.Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` - // ObservedGeneration is the 'Generation' of the ClusterProvisioner that + // ObservedGeneration is the 'Generation' of the ClusterChannelProvisioner that // was last reconciled by the controller. // +optional ObservedGeneration int64 `json:"observedGeneration,omitempty"` } const ( - // ClusterProvisionerConditionReady has status True when the Controller reconciling objects + // ClusterChannelProvisionerConditionReady has status True when the Controller reconciling objects // controlled by it is ready to control them. - ClusterProvisionerConditionReady = duckv1alpha1.ConditionReady + ClusterChannelProvisionerConditionReady = duckv1alpha1.ConditionReady ) // GetCondition returns the condition currently associated with the given type, or nil. -func (ps *ClusterProvisionerStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { - return cProvCondSet.Manage(ps).GetCondition(t) +func (ps *ClusterChannelProvisionerStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { + return ccProvCondSet.Manage(ps).GetCondition(t) } // IsReady returns true if the resource is ready overall. -func (ps *ClusterProvisionerStatus) IsReady() bool { - return cProvCondSet.Manage(ps).IsHappy() +func (ps *ClusterChannelProvisionerStatus) IsReady() bool { + return ccProvCondSet.Manage(ps).IsHappy() } // InitializeConditions sets relevant unset conditions to Unknown state. -func (ps *ClusterProvisionerStatus) InitializeConditions() { - cProvCondSet.Manage(ps).InitializeConditions() +func (ps *ClusterChannelProvisionerStatus) InitializeConditions() { + ccProvCondSet.Manage(ps).InitializeConditions() } -// MarkReady marks this ClusterProvisioner as Ready=true. +// MarkReady marks this ClusterChannelProvisioner as Ready=true. // // Note that this is not the normal pattern for duck conditions, but because there is (currently) -// no other condition on ClusterProvisioners, the normal IsReady() logic doesn't work well. -func (ps *ClusterProvisionerStatus) MarkReady() { - cProvCondSet.Manage(ps).MarkTrue(ClusterProvisionerConditionReady) +// no other condition on ClusterChannelProvisioners, the normal IsReady() logic doesn't work well. +func (ps *ClusterChannelProvisionerStatus) MarkReady() { + ccProvCondSet.Manage(ps).MarkTrue(ClusterChannelProvisionerConditionReady) } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// ClusterProvisionerList is a list of ClusterProvisioner resources -type ClusterProvisionerList struct { +// ClusterChannelProvisionerList is a list of ClusterChannelProvisioner resources +type ClusterChannelProvisionerList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata"` - Items []ClusterProvisioner `json:"items"` + Items []ClusterChannelProvisioner `json:"items"` } diff --git a/pkg/apis/eventing/v1alpha1/cluster_provisioner_types_test.go b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types_test.go similarity index 79% rename from pkg/apis/eventing/v1alpha1/cluster_provisioner_types_test.go rename to pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types_test.go index afb7e8a0ff9..ac2fa9ae915 100644 --- a/pkg/apis/eventing/v1alpha1/cluster_provisioner_types_test.go +++ b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types_test.go @@ -24,26 +24,26 @@ import ( corev1 "k8s.io/api/core/v1" ) -func TestClusterProvisionerStatusIsReady(t *testing.T) { +func TestClusterChannelProvisionerStatusIsReady(t *testing.T) { tests := []struct { name string - ps *ClusterProvisionerStatus + ps *ClusterChannelProvisionerStatus want bool }{{ name: "uninitialized", - ps: &ClusterProvisionerStatus{}, + ps: &ClusterChannelProvisionerStatus{}, want: false, }, { name: "initialized", - ps: func() *ClusterProvisionerStatus { - ps := &ClusterProvisionerStatus{} + ps: func() *ClusterChannelProvisionerStatus { + ps := &ClusterChannelProvisionerStatus{} ps.InitializeConditions() return ps }(), want: false, }, { name: "ready true condition", - ps: &ClusterProvisionerStatus{ + ps: &ClusterChannelProvisionerStatus{ Conditions: []duckv1alpha1.Condition{{ Type: ChannelConditionReady, Status: corev1.ConditionTrue, @@ -52,7 +52,7 @@ func TestClusterProvisionerStatusIsReady(t *testing.T) { want: true, }, { name: "ready false condition", - ps: &ClusterProvisionerStatus{ + ps: &ClusterChannelProvisionerStatus{ Conditions: []duckv1alpha1.Condition{{ Type: ChannelConditionReady, Status: corev1.ConditionFalse, @@ -61,7 +61,7 @@ func TestClusterProvisionerStatusIsReady(t *testing.T) { want: false, }, { name: "unknown condition", - ps: &ClusterProvisionerStatus{ + ps: &ClusterChannelProvisionerStatus{ Conditions: []duckv1alpha1.Condition{{ Type: "foo", Status: corev1.ConditionTrue, @@ -80,15 +80,15 @@ func TestClusterProvisionerStatusIsReady(t *testing.T) { } } -func TestClusterProvisionerStatusGetCondition(t *testing.T) { +func TestClusterChannelProvisionerStatusGetCondition(t *testing.T) { tests := []struct { name string - ps *ClusterProvisionerStatus + ps *ClusterChannelProvisionerStatus condQuery duckv1alpha1.ConditionType want *duckv1alpha1.Condition }{{ name: "single condition", - ps: &ClusterProvisionerStatus{ + ps: &ClusterChannelProvisionerStatus{ Conditions: []duckv1alpha1.Condition{ condReady, }, @@ -97,7 +97,7 @@ func TestClusterProvisionerStatusGetCondition(t *testing.T) { want: &condReady, }, { name: "unknown condition", - ps: &ClusterProvisionerStatus{ + ps: &ClusterChannelProvisionerStatus{ Conditions: []duckv1alpha1.Condition{ condReady, condUnprovisioned, @@ -117,8 +117,8 @@ func TestClusterProvisionerStatusGetCondition(t *testing.T) { } } -func TestClusterProvisionerStatus_MarkReady(t *testing.T) { - ps := ClusterProvisionerStatus{} +func TestClusterChannelProvisionerStatus_MarkReady(t *testing.T) { + ps := ClusterChannelProvisionerStatus{} ps.InitializeConditions() if ps.IsReady() { t.Errorf("Should not be ready when initialized.") diff --git a/pkg/apis/eventing/v1alpha1/cluster_provisioner_validation.go b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_validation.go similarity index 54% rename from pkg/apis/eventing/v1alpha1/cluster_provisioner_validation.go rename to pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_validation.go index c46e166819f..748786777af 100644 --- a/pkg/apis/eventing/v1alpha1/cluster_provisioner_validation.go +++ b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_validation.go @@ -18,26 +18,16 @@ package v1alpha1 import ( "github.com/knative/pkg/apis" - "k8s.io/apimachinery/pkg/api/equality" ) -// Validate validates the ClusterProvisioner resource. -func (p *ClusterProvisioner) Validate() *apis.FieldError { +// Validate validates the ClusterChannelProvisioner resource. +func (p *ClusterChannelProvisioner) Validate() *apis.FieldError { return p.Spec.Validate().ViaField("spec") } -// Validate validates the ClusterProvisioner spec -func (ps *ClusterProvisionerSpec) Validate() *apis.FieldError { - if equality.Semantic.DeepEqual(ps, &ClusterProvisionerSpec{}) { - return apis.ErrMissingField("reconciles") - } +// Validate validates the ClusterChannelProvisioner spec +func (ps *ClusterChannelProvisionerSpec) Validate() *apis.FieldError { var errs *apis.FieldError - if ps.Reconciles.Kind == "" { - errs = errs.Also(apis.ErrMissingField("kind").ViaField("reconciles")) - } - if ps.Reconciles.Group == "" { - errs = errs.Also(apis.ErrMissingField("group").ViaField("reconciles")) - } return errs } diff --git a/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_validation_test.go b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_validation_test.go new file mode 100644 index 00000000000..8a6639512c1 --- /dev/null +++ b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_validation_test.go @@ -0,0 +1,49 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/knative/pkg/apis" +) + +func TestClusterChannelProvisionerValidate(t *testing.T) { + tests := []struct { + name string + p *ClusterChannelProvisioner + want *apis.FieldError + }{{ + name: "valid", + p: &ClusterChannelProvisioner{ + Spec: ClusterChannelProvisionerSpec{}, + }, + }, { + name: "empty", + p: &ClusterChannelProvisioner{}, + }} + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := test.p.Validate() + if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { + t.Errorf("validate (-want, +got) = %v", diff) + } + }) + } +} diff --git a/pkg/apis/eventing/v1alpha1/cluster_provisioner_validation_test.go b/pkg/apis/eventing/v1alpha1/cluster_provisioner_validation_test.go deleted file mode 100644 index 7d77e4a6d8c..00000000000 --- a/pkg/apis/eventing/v1alpha1/cluster_provisioner_validation_test.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/google/go-cmp/cmp" - "github.com/knative/pkg/apis" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "testing" -) - -func TestClusterProvisionerValidate(t *testing.T) { - tests := []struct { - name string - p *ClusterProvisioner - want *apis.FieldError - }{{ - name: "valid", - p: &ClusterProvisioner{ - Spec: ClusterProvisionerSpec{ - Reconciles: metav1.GroupKind{ - Group: "knative.dev", - Kind: "Channel", - }, - }, - }, - }, { - name: "invalid cluster provisioner, empty reconciles", - p: &ClusterProvisioner{ - Spec: ClusterProvisionerSpec{ - Reconciles: metav1.GroupKind{}, - }, - }, - want: apis.ErrMissingField("spec.reconciles"), - }, { - name: "invalid cluster provisioner, empty kind", - p: &ClusterProvisioner{ - Spec: ClusterProvisionerSpec{ - Reconciles: metav1.GroupKind{ - Group: "eventing.knative.test", - }, - }, - }, - want: apis.ErrMissingField("spec.reconciles.kind"), - }, { - name: "invalid cluster provisioner", - p: &ClusterProvisioner{ - Spec: ClusterProvisionerSpec{ - Reconciles: metav1.GroupKind{ - Kind: "Channel", - }, - }, - }, - want: apis.ErrMissingField("spec.reconciles.group"), - }, { - name: "empty", - p: &ClusterProvisioner{}, - want: apis.ErrMissingField("spec.reconciles"), - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.p.Validate() - if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("validate (-want, +got) = %v", diff) - } - }) - } -} diff --git a/pkg/apis/eventing/v1alpha1/implements_test.go b/pkg/apis/eventing/v1alpha1/implements_test.go index 1443a89a455..83953a3f959 100644 --- a/pkg/apis/eventing/v1alpha1/implements_test.go +++ b/pkg/apis/eventing/v1alpha1/implements_test.go @@ -30,8 +30,8 @@ func TestTypesImplements(t *testing.T) { {instance: &Channel{}, iface: &duckv1alpha1.Conditions{}}, {instance: &Channel{}, iface: &eventingduck.Channelable{}}, {instance: &Channel{}, iface: &duckv1alpha1.Sinkable{}}, - // ClusterProvisioner - {instance: &ClusterProvisioner{}, iface: &duckv1alpha1.Conditions{}}, + // ClusterChannelProvisioner + {instance: &ClusterChannelProvisioner{}, iface: &duckv1alpha1.Conditions{}}, // Subscription {instance: &Subscription{}, iface: &duckv1alpha1.Conditions{}}, {instance: &Subscription{}, iface: &emptyGen}, diff --git a/pkg/apis/eventing/v1alpha1/register.go b/pkg/apis/eventing/v1alpha1/register.go index e3fb19db05d..df6338f66c1 100644 --- a/pkg/apis/eventing/v1alpha1/register.go +++ b/pkg/apis/eventing/v1alpha1/register.go @@ -47,8 +47,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, &Channel{}, &ChannelList{}, - &ClusterProvisioner{}, - &ClusterProvisionerList{}, + &ClusterChannelProvisioner{}, + &ClusterChannelProvisionerList{}, &Subscription{}, &SubscriptionList{}, ) diff --git a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go index 01a36278187..a472830054a 100644 --- a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go @@ -156,7 +156,7 @@ func (in *ChannelStatus) DeepCopy() *ChannelStatus { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterProvisioner) DeepCopyInto(out *ClusterProvisioner) { +func (in *ClusterChannelProvisioner) DeepCopyInto(out *ClusterChannelProvisioner) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) @@ -165,18 +165,18 @@ func (in *ClusterProvisioner) DeepCopyInto(out *ClusterProvisioner) { return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterProvisioner. -func (in *ClusterProvisioner) DeepCopy() *ClusterProvisioner { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterChannelProvisioner. +func (in *ClusterChannelProvisioner) DeepCopy() *ClusterChannelProvisioner { if in == nil { return nil } - out := new(ClusterProvisioner) + out := new(ClusterChannelProvisioner) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ClusterProvisioner) DeepCopyObject() runtime.Object { +func (in *ClusterChannelProvisioner) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -184,13 +184,13 @@ func (in *ClusterProvisioner) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterProvisionerList) DeepCopyInto(out *ClusterProvisionerList) { +func (in *ClusterChannelProvisionerList) DeepCopyInto(out *ClusterChannelProvisionerList) { *out = *in out.TypeMeta = in.TypeMeta out.ListMeta = in.ListMeta if in.Items != nil { in, out := &in.Items, &out.Items - *out = make([]ClusterProvisioner, len(*in)) + *out = make([]ClusterChannelProvisioner, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -198,18 +198,18 @@ func (in *ClusterProvisionerList) DeepCopyInto(out *ClusterProvisionerList) { return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterProvisionerList. -func (in *ClusterProvisionerList) DeepCopy() *ClusterProvisionerList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterChannelProvisionerList. +func (in *ClusterChannelProvisionerList) DeepCopy() *ClusterChannelProvisionerList { if in == nil { return nil } - out := new(ClusterProvisionerList) + out := new(ClusterChannelProvisionerList) in.DeepCopyInto(out) return out } // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ClusterProvisionerList) DeepCopyObject() runtime.Object { +func (in *ClusterChannelProvisionerList) DeepCopyObject() runtime.Object { if c := in.DeepCopy(); c != nil { return c } @@ -217,24 +217,23 @@ func (in *ClusterProvisionerList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterProvisionerSpec) DeepCopyInto(out *ClusterProvisionerSpec) { +func (in *ClusterChannelProvisionerSpec) DeepCopyInto(out *ClusterChannelProvisionerSpec) { *out = *in - out.Reconciles = in.Reconciles return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterProvisionerSpec. -func (in *ClusterProvisionerSpec) DeepCopy() *ClusterProvisionerSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterChannelProvisionerSpec. +func (in *ClusterChannelProvisionerSpec) DeepCopy() *ClusterChannelProvisionerSpec { if in == nil { return nil } - out := new(ClusterProvisionerSpec) + out := new(ClusterChannelProvisionerSpec) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterProvisionerStatus) DeepCopyInto(out *ClusterProvisionerStatus) { +func (in *ClusterChannelProvisionerStatus) DeepCopyInto(out *ClusterChannelProvisionerStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions @@ -246,12 +245,12 @@ func (in *ClusterProvisionerStatus) DeepCopyInto(out *ClusterProvisionerStatus) return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterProvisionerStatus. -func (in *ClusterProvisionerStatus) DeepCopy() *ClusterProvisionerStatus { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterChannelProvisionerStatus. +func (in *ClusterChannelProvisionerStatus) DeepCopy() *ClusterChannelProvisionerStatus { if in == nil { return nil } - out := new(ClusterProvisionerStatus) + out := new(ClusterChannelProvisionerStatus) in.DeepCopyInto(out) return out } diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/clusterchannelprovisioner.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/clusterchannelprovisioner.go new file mode 100644 index 00000000000..951b0f919eb --- /dev/null +++ b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/clusterchannelprovisioner.go @@ -0,0 +1,147 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ClusterChannelProvisionersGetter has a method to return a ClusterChannelProvisionerInterface. +// A group's client should implement this interface. +type ClusterChannelProvisionersGetter interface { + ClusterChannelProvisioners() ClusterChannelProvisionerInterface +} + +// ClusterChannelProvisionerInterface has methods to work with ClusterChannelProvisioner resources. +type ClusterChannelProvisionerInterface interface { + Create(*v1alpha1.ClusterChannelProvisioner) (*v1alpha1.ClusterChannelProvisioner, error) + Update(*v1alpha1.ClusterChannelProvisioner) (*v1alpha1.ClusterChannelProvisioner, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.ClusterChannelProvisioner, error) + List(opts v1.ListOptions) (*v1alpha1.ClusterChannelProvisionerList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterChannelProvisioner, err error) + ClusterChannelProvisionerExpansion +} + +// clusterChannelProvisioners implements ClusterChannelProvisionerInterface +type clusterChannelProvisioners struct { + client rest.Interface +} + +// newClusterChannelProvisioners returns a ClusterChannelProvisioners +func newClusterChannelProvisioners(c *EventingV1alpha1Client) *clusterChannelProvisioners { + return &clusterChannelProvisioners{ + client: c.RESTClient(), + } +} + +// Get takes name of the clusterChannelProvisioner, and returns the corresponding clusterChannelProvisioner object, and an error if there is any. +func (c *clusterChannelProvisioners) Get(name string, options v1.GetOptions) (result *v1alpha1.ClusterChannelProvisioner, err error) { + result = &v1alpha1.ClusterChannelProvisioner{} + err = c.client.Get(). + Resource("clusterchannelprovisioners"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ClusterChannelProvisioners that match those selectors. +func (c *clusterChannelProvisioners) List(opts v1.ListOptions) (result *v1alpha1.ClusterChannelProvisionerList, err error) { + result = &v1alpha1.ClusterChannelProvisionerList{} + err = c.client.Get(). + Resource("clusterchannelprovisioners"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested clusterChannelProvisioners. +func (c *clusterChannelProvisioners) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Resource("clusterchannelprovisioners"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Create takes the representation of a clusterChannelProvisioner and creates it. Returns the server's representation of the clusterChannelProvisioner, and an error, if there is any. +func (c *clusterChannelProvisioners) Create(clusterChannelProvisioner *v1alpha1.ClusterChannelProvisioner) (result *v1alpha1.ClusterChannelProvisioner, err error) { + result = &v1alpha1.ClusterChannelProvisioner{} + err = c.client.Post(). + Resource("clusterchannelprovisioners"). + Body(clusterChannelProvisioner). + Do(). + Into(result) + return +} + +// Update takes the representation of a clusterChannelProvisioner and updates it. Returns the server's representation of the clusterChannelProvisioner, and an error, if there is any. +func (c *clusterChannelProvisioners) Update(clusterChannelProvisioner *v1alpha1.ClusterChannelProvisioner) (result *v1alpha1.ClusterChannelProvisioner, err error) { + result = &v1alpha1.ClusterChannelProvisioner{} + err = c.client.Put(). + Resource("clusterchannelprovisioners"). + Name(clusterChannelProvisioner.Name). + Body(clusterChannelProvisioner). + Do(). + Into(result) + return +} + +// Delete takes name of the clusterChannelProvisioner and deletes it. Returns an error if one occurs. +func (c *clusterChannelProvisioners) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("clusterchannelprovisioners"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *clusterChannelProvisioners) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.client.Delete(). + Resource("clusterchannelprovisioners"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched clusterChannelProvisioner. +func (c *clusterChannelProvisioners) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterChannelProvisioner, err error) { + result = &v1alpha1.ClusterChannelProvisioner{} + err = c.client.Patch(pt). + Resource("clusterchannelprovisioners"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/clusterprovisioner.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/clusterprovisioner.go deleted file mode 100644 index 9b61e6c9073..00000000000 --- a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/clusterprovisioner.go +++ /dev/null @@ -1,147 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// ClusterProvisionersGetter has a method to return a ClusterProvisionerInterface. -// A group's client should implement this interface. -type ClusterProvisionersGetter interface { - ClusterProvisioners() ClusterProvisionerInterface -} - -// ClusterProvisionerInterface has methods to work with ClusterProvisioner resources. -type ClusterProvisionerInterface interface { - Create(*v1alpha1.ClusterProvisioner) (*v1alpha1.ClusterProvisioner, error) - Update(*v1alpha1.ClusterProvisioner) (*v1alpha1.ClusterProvisioner, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.ClusterProvisioner, error) - List(opts v1.ListOptions) (*v1alpha1.ClusterProvisionerList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterProvisioner, err error) - ClusterProvisionerExpansion -} - -// clusterProvisioners implements ClusterProvisionerInterface -type clusterProvisioners struct { - client rest.Interface -} - -// newClusterProvisioners returns a ClusterProvisioners -func newClusterProvisioners(c *EventingV1alpha1Client) *clusterProvisioners { - return &clusterProvisioners{ - client: c.RESTClient(), - } -} - -// Get takes name of the clusterProvisioner, and returns the corresponding clusterProvisioner object, and an error if there is any. -func (c *clusterProvisioners) Get(name string, options v1.GetOptions) (result *v1alpha1.ClusterProvisioner, err error) { - result = &v1alpha1.ClusterProvisioner{} - err = c.client.Get(). - Resource("clusterprovisioners"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of ClusterProvisioners that match those selectors. -func (c *clusterProvisioners) List(opts v1.ListOptions) (result *v1alpha1.ClusterProvisionerList, err error) { - result = &v1alpha1.ClusterProvisionerList{} - err = c.client.Get(). - Resource("clusterprovisioners"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested clusterProvisioners. -func (c *clusterProvisioners) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Resource("clusterprovisioners"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a clusterProvisioner and creates it. Returns the server's representation of the clusterProvisioner, and an error, if there is any. -func (c *clusterProvisioners) Create(clusterProvisioner *v1alpha1.ClusterProvisioner) (result *v1alpha1.ClusterProvisioner, err error) { - result = &v1alpha1.ClusterProvisioner{} - err = c.client.Post(). - Resource("clusterprovisioners"). - Body(clusterProvisioner). - Do(). - Into(result) - return -} - -// Update takes the representation of a clusterProvisioner and updates it. Returns the server's representation of the clusterProvisioner, and an error, if there is any. -func (c *clusterProvisioners) Update(clusterProvisioner *v1alpha1.ClusterProvisioner) (result *v1alpha1.ClusterProvisioner, err error) { - result = &v1alpha1.ClusterProvisioner{} - err = c.client.Put(). - Resource("clusterprovisioners"). - Name(clusterProvisioner.Name). - Body(clusterProvisioner). - Do(). - Into(result) - return -} - -// Delete takes name of the clusterProvisioner and deletes it. Returns an error if one occurs. -func (c *clusterProvisioners) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("clusterprovisioners"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *clusterProvisioners) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Resource("clusterprovisioners"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched clusterProvisioner. -func (c *clusterProvisioners) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterProvisioner, err error) { - result = &v1alpha1.ClusterProvisioner{} - err = c.client.Patch(pt). - Resource("clusterprovisioners"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/eventing_client.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/eventing_client.go index 3d84eebe8e4..ef337f70bf0 100644 --- a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/eventing_client.go +++ b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/eventing_client.go @@ -28,7 +28,7 @@ import ( type EventingV1alpha1Interface interface { RESTClient() rest.Interface ChannelsGetter - ClusterProvisionersGetter + ClusterChannelProvisionersGetter SubscriptionsGetter } @@ -41,8 +41,8 @@ func (c *EventingV1alpha1Client) Channels(namespace string) ChannelInterface { return newChannels(c, namespace) } -func (c *EventingV1alpha1Client) ClusterProvisioners() ClusterProvisionerInterface { - return newClusterProvisioners(c) +func (c *EventingV1alpha1Client) ClusterChannelProvisioners() ClusterChannelProvisionerInterface { + return newClusterChannelProvisioners(c) } func (c *EventingV1alpha1Client) Subscriptions(namespace string) SubscriptionInterface { diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_clusterchannelprovisioner.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_clusterchannelprovisioner.go new file mode 100644 index 00000000000..785d0af0b42 --- /dev/null +++ b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_clusterchannelprovisioner.go @@ -0,0 +1,120 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package fake + +import ( + v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + labels "k8s.io/apimachinery/pkg/labels" + schema "k8s.io/apimachinery/pkg/runtime/schema" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + testing "k8s.io/client-go/testing" +) + +// FakeClusterChannelProvisioners implements ClusterChannelProvisionerInterface +type FakeClusterChannelProvisioners struct { + Fake *FakeEventingV1alpha1 +} + +var clusterchannelprovisionersResource = schema.GroupVersionResource{Group: "eventing.knative.dev", Version: "v1alpha1", Resource: "clusterchannelprovisioners"} + +var clusterchannelprovisionersKind = schema.GroupVersionKind{Group: "eventing.knative.dev", Version: "v1alpha1", Kind: "ClusterChannelProvisioner"} + +// Get takes name of the clusterChannelProvisioner, and returns the corresponding clusterChannelProvisioner object, and an error if there is any. +func (c *FakeClusterChannelProvisioners) Get(name string, options v1.GetOptions) (result *v1alpha1.ClusterChannelProvisioner, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootGetAction(clusterchannelprovisionersResource, name), &v1alpha1.ClusterChannelProvisioner{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterChannelProvisioner), err +} + +// List takes label and field selectors, and returns the list of ClusterChannelProvisioners that match those selectors. +func (c *FakeClusterChannelProvisioners) List(opts v1.ListOptions) (result *v1alpha1.ClusterChannelProvisionerList, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootListAction(clusterchannelprovisionersResource, clusterchannelprovisionersKind, opts), &v1alpha1.ClusterChannelProvisionerList{}) + if obj == nil { + return nil, err + } + + label, _, _ := testing.ExtractFromListOptions(opts) + if label == nil { + label = labels.Everything() + } + list := &v1alpha1.ClusterChannelProvisionerList{ListMeta: obj.(*v1alpha1.ClusterChannelProvisionerList).ListMeta} + for _, item := range obj.(*v1alpha1.ClusterChannelProvisionerList).Items { + if label.Matches(labels.Set(item.Labels)) { + list.Items = append(list.Items, item) + } + } + return list, err +} + +// Watch returns a watch.Interface that watches the requested clusterChannelProvisioners. +func (c *FakeClusterChannelProvisioners) Watch(opts v1.ListOptions) (watch.Interface, error) { + return c.Fake. + InvokesWatch(testing.NewRootWatchAction(clusterchannelprovisionersResource, opts)) +} + +// Create takes the representation of a clusterChannelProvisioner and creates it. Returns the server's representation of the clusterChannelProvisioner, and an error, if there is any. +func (c *FakeClusterChannelProvisioners) Create(clusterChannelProvisioner *v1alpha1.ClusterChannelProvisioner) (result *v1alpha1.ClusterChannelProvisioner, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootCreateAction(clusterchannelprovisionersResource, clusterChannelProvisioner), &v1alpha1.ClusterChannelProvisioner{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterChannelProvisioner), err +} + +// Update takes the representation of a clusterChannelProvisioner and updates it. Returns the server's representation of the clusterChannelProvisioner, and an error, if there is any. +func (c *FakeClusterChannelProvisioners) Update(clusterChannelProvisioner *v1alpha1.ClusterChannelProvisioner) (result *v1alpha1.ClusterChannelProvisioner, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootUpdateAction(clusterchannelprovisionersResource, clusterChannelProvisioner), &v1alpha1.ClusterChannelProvisioner{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterChannelProvisioner), err +} + +// Delete takes name of the clusterChannelProvisioner and deletes it. Returns an error if one occurs. +func (c *FakeClusterChannelProvisioners) Delete(name string, options *v1.DeleteOptions) error { + _, err := c.Fake. + Invokes(testing.NewRootDeleteAction(clusterchannelprovisionersResource, name), &v1alpha1.ClusterChannelProvisioner{}) + return err +} + +// DeleteCollection deletes a collection of objects. +func (c *FakeClusterChannelProvisioners) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + action := testing.NewRootDeleteCollectionAction(clusterchannelprovisionersResource, listOptions) + + _, err := c.Fake.Invokes(action, &v1alpha1.ClusterChannelProvisionerList{}) + return err +} + +// Patch applies the patch and returns the patched clusterChannelProvisioner. +func (c *FakeClusterChannelProvisioners) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterChannelProvisioner, err error) { + obj, err := c.Fake. + Invokes(testing.NewRootPatchSubresourceAction(clusterchannelprovisionersResource, name, data, subresources...), &v1alpha1.ClusterChannelProvisioner{}) + if obj == nil { + return nil, err + } + return obj.(*v1alpha1.ClusterChannelProvisioner), err +} diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_clusterprovisioner.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_clusterprovisioner.go deleted file mode 100644 index ce5b93f76ce..00000000000 --- a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_clusterprovisioner.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeClusterProvisioners implements ClusterProvisionerInterface -type FakeClusterProvisioners struct { - Fake *FakeEventingV1alpha1 -} - -var clusterprovisionersResource = schema.GroupVersionResource{Group: "eventing.knative.dev", Version: "v1alpha1", Resource: "clusterprovisioners"} - -var clusterprovisionersKind = schema.GroupVersionKind{Group: "eventing.knative.dev", Version: "v1alpha1", Kind: "ClusterProvisioner"} - -// Get takes name of the clusterProvisioner, and returns the corresponding clusterProvisioner object, and an error if there is any. -func (c *FakeClusterProvisioners) Get(name string, options v1.GetOptions) (result *v1alpha1.ClusterProvisioner, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(clusterprovisionersResource, name), &v1alpha1.ClusterProvisioner{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterProvisioner), err -} - -// List takes label and field selectors, and returns the list of ClusterProvisioners that match those selectors. -func (c *FakeClusterProvisioners) List(opts v1.ListOptions) (result *v1alpha1.ClusterProvisionerList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(clusterprovisionersResource, clusterprovisionersKind, opts), &v1alpha1.ClusterProvisionerList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.ClusterProvisionerList{ListMeta: obj.(*v1alpha1.ClusterProvisionerList).ListMeta} - for _, item := range obj.(*v1alpha1.ClusterProvisionerList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested clusterProvisioners. -func (c *FakeClusterProvisioners) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(clusterprovisionersResource, opts)) -} - -// Create takes the representation of a clusterProvisioner and creates it. Returns the server's representation of the clusterProvisioner, and an error, if there is any. -func (c *FakeClusterProvisioners) Create(clusterProvisioner *v1alpha1.ClusterProvisioner) (result *v1alpha1.ClusterProvisioner, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(clusterprovisionersResource, clusterProvisioner), &v1alpha1.ClusterProvisioner{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterProvisioner), err -} - -// Update takes the representation of a clusterProvisioner and updates it. Returns the server's representation of the clusterProvisioner, and an error, if there is any. -func (c *FakeClusterProvisioners) Update(clusterProvisioner *v1alpha1.ClusterProvisioner) (result *v1alpha1.ClusterProvisioner, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(clusterprovisionersResource, clusterProvisioner), &v1alpha1.ClusterProvisioner{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterProvisioner), err -} - -// Delete takes name of the clusterProvisioner and deletes it. Returns an error if one occurs. -func (c *FakeClusterProvisioners) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(clusterprovisionersResource, name), &v1alpha1.ClusterProvisioner{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeClusterProvisioners) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(clusterprovisionersResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.ClusterProvisionerList{}) - return err -} - -// Patch applies the patch and returns the patched clusterProvisioner. -func (c *FakeClusterProvisioners) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterProvisioner, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(clusterprovisionersResource, name, data, subresources...), &v1alpha1.ClusterProvisioner{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterProvisioner), err -} diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_eventing_client.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_eventing_client.go index 0127acc10fb..0080c07b8d4 100644 --- a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_eventing_client.go +++ b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake/fake_eventing_client.go @@ -32,8 +32,8 @@ func (c *FakeEventingV1alpha1) Channels(namespace string) v1alpha1.ChannelInterf return &FakeChannels{c, namespace} } -func (c *FakeEventingV1alpha1) ClusterProvisioners() v1alpha1.ClusterProvisionerInterface { - return &FakeClusterProvisioners{c} +func (c *FakeEventingV1alpha1) ClusterChannelProvisioners() v1alpha1.ClusterChannelProvisionerInterface { + return &FakeClusterChannelProvisioners{c} } func (c *FakeEventingV1alpha1) Subscriptions(namespace string) v1alpha1.SubscriptionInterface { diff --git a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/generated_expansion.go index 2d119a1cc27..fcef70e765c 100644 --- a/pkg/client/clientset/versioned/typed/eventing/v1alpha1/generated_expansion.go +++ b/pkg/client/clientset/versioned/typed/eventing/v1alpha1/generated_expansion.go @@ -20,6 +20,6 @@ package v1alpha1 type ChannelExpansion interface{} -type ClusterProvisionerExpansion interface{} +type ClusterChannelProvisionerExpansion interface{} type SubscriptionExpansion interface{} diff --git a/pkg/client/informers/externalversions/eventing/v1alpha1/clusterprovisioner.go b/pkg/client/informers/externalversions/eventing/v1alpha1/clusterchannelprovisioner.go similarity index 53% rename from pkg/client/informers/externalversions/eventing/v1alpha1/clusterprovisioner.go rename to pkg/client/informers/externalversions/eventing/v1alpha1/clusterchannelprovisioner.go index fabfa09b5a5..fb0aae439aa 100644 --- a/pkg/client/informers/externalversions/eventing/v1alpha1/clusterprovisioner.go +++ b/pkg/client/informers/externalversions/eventing/v1alpha1/clusterchannelprovisioner.go @@ -31,58 +31,58 @@ import ( cache "k8s.io/client-go/tools/cache" ) -// ClusterProvisionerInformer provides access to a shared informer and lister for -// ClusterProvisioners. -type ClusterProvisionerInformer interface { +// ClusterChannelProvisionerInformer provides access to a shared informer and lister for +// ClusterChannelProvisioners. +type ClusterChannelProvisionerInformer interface { Informer() cache.SharedIndexInformer - Lister() v1alpha1.ClusterProvisionerLister + Lister() v1alpha1.ClusterChannelProvisionerLister } -type clusterProvisionerInformer struct { +type clusterChannelProvisionerInformer struct { factory internalinterfaces.SharedInformerFactory tweakListOptions internalinterfaces.TweakListOptionsFunc } -// NewClusterProvisionerInformer constructs a new informer for ClusterProvisioner type. +// NewClusterChannelProvisionerInformer constructs a new informer for ClusterChannelProvisioner type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewClusterProvisionerInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredClusterProvisionerInformer(client, resyncPeriod, indexers, nil) +func NewClusterChannelProvisionerInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { + return NewFilteredClusterChannelProvisionerInformer(client, resyncPeriod, indexers, nil) } -// NewFilteredClusterProvisionerInformer constructs a new informer for ClusterProvisioner type. +// NewFilteredClusterChannelProvisionerInformer constructs a new informer for ClusterChannelProvisioner type. // Always prefer using an informer factory to get a shared informer instead of getting an independent // one. This reduces memory footprint and number of connections to the server. -func NewFilteredClusterProvisionerInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { +func NewFilteredClusterChannelProvisionerInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { return cache.NewSharedIndexInformer( &cache.ListWatch{ ListFunc: func(options v1.ListOptions) (runtime.Object, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.EventingV1alpha1().ClusterProvisioners().List(options) + return client.EventingV1alpha1().ClusterChannelProvisioners().List(options) }, WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { if tweakListOptions != nil { tweakListOptions(&options) } - return client.EventingV1alpha1().ClusterProvisioners().Watch(options) + return client.EventingV1alpha1().ClusterChannelProvisioners().Watch(options) }, }, - &eventing_v1alpha1.ClusterProvisioner{}, + &eventing_v1alpha1.ClusterChannelProvisioner{}, resyncPeriod, indexers, ) } -func (f *clusterProvisionerInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredClusterProvisionerInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) +func (f *clusterChannelProvisionerInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { + return NewFilteredClusterChannelProvisionerInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) } -func (f *clusterProvisionerInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&eventing_v1alpha1.ClusterProvisioner{}, f.defaultInformer) +func (f *clusterChannelProvisionerInformer) Informer() cache.SharedIndexInformer { + return f.factory.InformerFor(&eventing_v1alpha1.ClusterChannelProvisioner{}, f.defaultInformer) } -func (f *clusterProvisionerInformer) Lister() v1alpha1.ClusterProvisionerLister { - return v1alpha1.NewClusterProvisionerLister(f.Informer().GetIndexer()) +func (f *clusterChannelProvisionerInformer) Lister() v1alpha1.ClusterChannelProvisionerLister { + return v1alpha1.NewClusterChannelProvisionerLister(f.Informer().GetIndexer()) } diff --git a/pkg/client/informers/externalversions/eventing/v1alpha1/interface.go b/pkg/client/informers/externalversions/eventing/v1alpha1/interface.go index 1fd4c99c7dc..15bebacbe7f 100644 --- a/pkg/client/informers/externalversions/eventing/v1alpha1/interface.go +++ b/pkg/client/informers/externalversions/eventing/v1alpha1/interface.go @@ -26,8 +26,8 @@ import ( type Interface interface { // Channels returns a ChannelInformer. Channels() ChannelInformer - // ClusterProvisioners returns a ClusterProvisionerInformer. - ClusterProvisioners() ClusterProvisionerInformer + // ClusterChannelProvisioners returns a ClusterChannelProvisionerInformer. + ClusterChannelProvisioners() ClusterChannelProvisionerInformer // Subscriptions returns a SubscriptionInformer. Subscriptions() SubscriptionInformer } @@ -48,9 +48,9 @@ func (v *version) Channels() ChannelInformer { return &channelInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} } -// ClusterProvisioners returns a ClusterProvisionerInformer. -func (v *version) ClusterProvisioners() ClusterProvisionerInformer { - return &clusterProvisionerInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} +// ClusterChannelProvisioners returns a ClusterChannelProvisionerInformer. +func (v *version) ClusterChannelProvisioners() ClusterChannelProvisionerInformer { + return &clusterChannelProvisionerInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} } // Subscriptions returns a SubscriptionInformer. diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 521a45e5029..78530117deb 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -68,8 +68,8 @@ func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource // Group=eventing.knative.dev, Version=v1alpha1 case eventing_v1alpha1.SchemeGroupVersion.WithResource("channels"): return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().Channels().Informer()}, nil - case eventing_v1alpha1.SchemeGroupVersion.WithResource("clusterprovisioners"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().ClusterProvisioners().Informer()}, nil + case eventing_v1alpha1.SchemeGroupVersion.WithResource("clusterchannelprovisioners"): + return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().ClusterChannelProvisioners().Informer()}, nil case eventing_v1alpha1.SchemeGroupVersion.WithResource("subscriptions"): return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().Subscriptions().Informer()}, nil diff --git a/pkg/client/listers/eventing/v1alpha1/clusterchannelprovisioner.go b/pkg/client/listers/eventing/v1alpha1/clusterchannelprovisioner.go new file mode 100644 index 00000000000..daa65a79b81 --- /dev/null +++ b/pkg/client/listers/eventing/v1alpha1/clusterchannelprovisioner.go @@ -0,0 +1,65 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by lister-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/client-go/tools/cache" +) + +// ClusterChannelProvisionerLister helps list ClusterChannelProvisioners. +type ClusterChannelProvisionerLister interface { + // List lists all ClusterChannelProvisioners in the indexer. + List(selector labels.Selector) (ret []*v1alpha1.ClusterChannelProvisioner, err error) + // Get retrieves the ClusterChannelProvisioner from the index for a given name. + Get(name string) (*v1alpha1.ClusterChannelProvisioner, error) + ClusterChannelProvisionerListerExpansion +} + +// clusterChannelProvisionerLister implements the ClusterChannelProvisionerLister interface. +type clusterChannelProvisionerLister struct { + indexer cache.Indexer +} + +// NewClusterChannelProvisionerLister returns a new ClusterChannelProvisionerLister. +func NewClusterChannelProvisionerLister(indexer cache.Indexer) ClusterChannelProvisionerLister { + return &clusterChannelProvisionerLister{indexer: indexer} +} + +// List lists all ClusterChannelProvisioners in the indexer. +func (s *clusterChannelProvisionerLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterChannelProvisioner, err error) { + err = cache.ListAll(s.indexer, selector, func(m interface{}) { + ret = append(ret, m.(*v1alpha1.ClusterChannelProvisioner)) + }) + return ret, err +} + +// Get retrieves the ClusterChannelProvisioner from the index for a given name. +func (s *clusterChannelProvisionerLister) Get(name string) (*v1alpha1.ClusterChannelProvisioner, error) { + obj, exists, err := s.indexer.GetByKey(name) + if err != nil { + return nil, err + } + if !exists { + return nil, errors.NewNotFound(v1alpha1.Resource("clusterchannelprovisioner"), name) + } + return obj.(*v1alpha1.ClusterChannelProvisioner), nil +} diff --git a/pkg/client/listers/eventing/v1alpha1/clusterprovisioner.go b/pkg/client/listers/eventing/v1alpha1/clusterprovisioner.go deleted file mode 100644 index f380ac06d1b..00000000000 --- a/pkg/client/listers/eventing/v1alpha1/clusterprovisioner.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// ClusterProvisionerLister helps list ClusterProvisioners. -type ClusterProvisionerLister interface { - // List lists all ClusterProvisioners in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.ClusterProvisioner, err error) - // Get retrieves the ClusterProvisioner from the index for a given name. - Get(name string) (*v1alpha1.ClusterProvisioner, error) - ClusterProvisionerListerExpansion -} - -// clusterProvisionerLister implements the ClusterProvisionerLister interface. -type clusterProvisionerLister struct { - indexer cache.Indexer -} - -// NewClusterProvisionerLister returns a new ClusterProvisionerLister. -func NewClusterProvisionerLister(indexer cache.Indexer) ClusterProvisionerLister { - return &clusterProvisionerLister{indexer: indexer} -} - -// List lists all ClusterProvisioners in the indexer. -func (s *clusterProvisionerLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterProvisioner, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ClusterProvisioner)) - }) - return ret, err -} - -// Get retrieves the ClusterProvisioner from the index for a given name. -func (s *clusterProvisionerLister) Get(name string) (*v1alpha1.ClusterProvisioner, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("clusterprovisioner"), name) - } - return obj.(*v1alpha1.ClusterProvisioner), nil -} diff --git a/pkg/client/listers/eventing/v1alpha1/expansion_generated.go b/pkg/client/listers/eventing/v1alpha1/expansion_generated.go index abc32a5ee74..ce1c8af2399 100644 --- a/pkg/client/listers/eventing/v1alpha1/expansion_generated.go +++ b/pkg/client/listers/eventing/v1alpha1/expansion_generated.go @@ -26,9 +26,9 @@ type ChannelListerExpansion interface{} // ChannelNamespaceLister. type ChannelNamespaceListerExpansion interface{} -// ClusterProvisionerListerExpansion allows custom methods to be added to -// ClusterProvisionerLister. -type ClusterProvisionerListerExpansion interface{} +// ClusterChannelProvisionerListerExpansion allows custom methods to be added to +// ClusterChannelProvisionerLister. +type ClusterChannelProvisionerListerExpansion interface{} // SubscriptionListerExpansion allows custom methods to be added to // SubscriptionLister. diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 704f8c99330..9d2ea255ac8 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -21,7 +21,7 @@ import ( eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/controller" - cpcontroller "github.com/knative/eventing/pkg/controller/eventing/inmemory/clusterprovisioner" + ccpcontroller "github.com/knative/eventing/pkg/controller/eventing/inmemory/clusterchannelprovisioner" "github.com/knative/eventing/pkg/sidecar/configmap" "github.com/knative/eventing/pkg/sidecar/fanout" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" @@ -109,10 +109,10 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err } // shouldReconcile determines if this Controller should control (and therefore reconcile) a given -// ClusterProvisioner. This Controller only handles in-memory channels. +// ClusterChannelProvisioner. This Controller only handles in-memory channels. func (r *reconciler) shouldReconcile(c *eventingv1alpha1.Channel) bool { if c.Spec.Provisioner != nil { - return cpcontroller.IsControlled(c.Spec.Provisioner, cpcontroller.Channel) + return ccpcontroller.IsControlled(c.Spec.Provisioner) } return false } diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index 1d31a9d02a6..8f77535c2b1 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -43,7 +43,7 @@ import ( ) const ( - cpName = "in-memory-channel" + ccpName = "in-memory-channel" cNamespace = "test-namespace" cName = "test-channel" @@ -113,7 +113,7 @@ var ( Spec: eventingv1alpha1.ChannelSpec{ Provisioner: &eventingv1alpha1.ProvisionerReference{ Ref: &corev1.ObjectReference{ - Name: cpName, + Name: ccpName, }, }, Channelable: &eventingduck.Channelable{ @@ -166,7 +166,7 @@ var ( Spec: eventingv1alpha1.ChannelSpec{ Provisioner: &eventingv1alpha1.ProvisionerReference{ Ref: &corev1.ObjectReference{ - Name: cpName, + Name: ccpName, }, }, Channelable: &eventingduck.Channelable{ @@ -474,7 +474,7 @@ func makeChannel() *eventingv1alpha1.Channel { Spec: eventingv1alpha1.ChannelSpec{ Provisioner: &eventingv1alpha1.ProvisionerReference{ Ref: &corev1.ObjectReference{ - Name: cpName, + Name: ccpName, }, }, }, @@ -569,7 +569,7 @@ func makeK8sService() *corev1.Service { Namespace: cNamespace, Labels: map[string]string{ "channel": cName, - "provisioner": cpName, + "provisioner": ccpName, }, OwnerReferences: []metav1.OwnerReference{ { @@ -610,7 +610,7 @@ func makeVirtualService() *istiov1alpha3.VirtualService { Namespace: cNamespace, Labels: map[string]string{ "channel": cName, - "provisioner": cpName, + "provisioner": ccpName, }, OwnerReferences: []metav1.OwnerReference{ { diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/controller.go similarity index 79% rename from pkg/controller/eventing/inmemory/clusterprovisioner/controller.go rename to pkg/controller/eventing/inmemory/clusterchannelprovisioner/controller.go index 3296a144733..3518b86939c 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/controller.go +++ b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/controller.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clusterprovisioner +package clusterchannelprovisioner import ( eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" @@ -36,7 +36,7 @@ const ( func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Controller, error) { logger = logger.With(zap.String("controller", controllerAgentName)) - // Setup a new controller to Reconcile ClusterProvisioners that are in-memory channels. + // Setup a new controller to Reconcile ClusterChannelProvisioners that are in-memory channels. r := &reconciler{ recorder: mgr.GetRecorder(controllerAgentName), logger: logger, @@ -49,19 +49,19 @@ func ProvideController(mgr manager.Manager, logger *zap.Logger) (controller.Cont return nil, err } - // Watch ClusterProvisioners. + // Watch ClusterChannelProvisioners. err = c.Watch(&source.Kind{ - Type: &eventingv1alpha1.ClusterProvisioner{}, + Type: &eventingv1alpha1.ClusterChannelProvisioner{}, }, &handler.EnqueueRequestForObject{}) if err != nil { - logger.Error("Unable to watch ClusterProvisioners.", zap.Error(err), zap.Any("type", &eventingv1alpha1.ClusterProvisioner{})) + logger.Error("Unable to watch ClusterChannelProvisioners.", zap.Error(err), zap.Any("type", &eventingv1alpha1.ClusterChannelProvisioner{})) return nil, err } - // Watch the K8s Services that are owned by ClusterProvisioners. + // Watch the K8s Services that are owned by ClusterChannelProvisioners. err = c.Watch(&source.Kind{ Type: &corev1.Service{}, - }, &handler.EnqueueRequestForOwner{OwnerType: &eventingv1alpha1.ClusterProvisioner{}, IsController: true}) + }, &handler.EnqueueRequestForOwner{OwnerType: &eventingv1alpha1.ClusterChannelProvisioner{}, IsController: true}) if err != nil { logger.Error("Unable to watch K8s Services.", zap.Error(err)) return nil, err diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile.go similarity index 57% rename from pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go rename to pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile.go index a11cb53ca2f..2fc32debef5 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile.go +++ b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clusterprovisioner +package clusterchannelprovisioner import ( "context" @@ -36,7 +36,7 @@ import ( ) const ( - // Name is the name of the in-memory channel ClusterProvisioner. + // Name is the name of the in-memory channel ClusterChannelProvisioner. Name = "in-memory-channel" // Channel is the name of the Channel resource in eventing.knative.dev/v1alpha1. @@ -62,41 +62,41 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err ctx := context.TODO() logger := r.logger.With(zap.Any("request", request)) - cp := &eventingv1alpha1.ClusterProvisioner{} - err := r.client.Get(ctx, request.NamespacedName, cp) + ccp := &eventingv1alpha1.ClusterChannelProvisioner{} + err := r.client.Get(ctx, request.NamespacedName, ccp) - // The ClusterProvisioner may have been deleted since it was added to the workqueue. If so, + // The ClusterChannelProvisioner may have been deleted since it was added to the workqueue. If so, // there is nothing to be done. if errors.IsNotFound(err) { - logger.Info("Could not find ClusterProvisioner", zap.Error(err)) + logger.Info("Could not find ClusterChannelProvisioner", zap.Error(err)) return reconcile.Result{}, nil } // Any other error should be retried in another reconciliation. if err != nil { - logger.Error("Unable to Get ClusterProvisioner", zap.Error(err)) + logger.Error("Unable to Get ClusterChannelProvisioner", zap.Error(err)) return reconcile.Result{}, err } - // Does this Controller control this ClusterProvisioner? - if !shouldReconcile(cp.Namespace, cp.Name, cp.Spec.Reconciles.Kind) { - logger.Info("Not reconciling ClusterProvisioner, it is not controlled by this Controller", zap.String("APIVersion", cp.APIVersion), zap.String("Kind", cp.Kind), zap.String("Namespace", cp.Namespace), zap.String("name", cp.Name)) + // Does this Controller control this ClusterChannelProvisioner? + if !shouldReconcile(ccp.Namespace, ccp.Name) { + logger.Info("Not reconciling ClusterChannelProvisioner, it is not controlled by this Controller", zap.String("APIVersion", ccp.APIVersion), zap.String("Kind", ccp.Kind), zap.String("Namespace", ccp.Namespace), zap.String("name", ccp.Name)) return reconcile.Result{}, nil } - logger.Info("Reconciling ClusterProvisioner.") + logger.Info("Reconciling ClusterChannelProvisioner.") // Modify a copy of this object, rather than the original. - cp = cp.DeepCopy() + ccp = ccp.DeepCopy() - err = r.reconcile(ctx, cp) + err = r.reconcile(ctx, ccp) if err != nil { - logger.Info("Error reconciling ClusterProvisioner", zap.Error(err)) + logger.Info("Error reconciling ClusterChannelProvisioner", zap.Error(err)) // Note that we do not return the error here, because we want to update the Status // regardless of the error. } - if updateStatusErr := r.updateClusterProvisionerStatus(ctx, cp); updateStatusErr != nil { - logger.Info("Error updating ClusterProvisioner Status", zap.Error(updateStatusErr)) + if updateStatusErr := r.updateClusterChannelProvisionerStatus(ctx, ccp); updateStatusErr != nil { + logger.Info("Error updating ClusterChannelProvisioner Status", zap.Error(updateStatusErr)) return reconcile.Result{}, updateStatusErr } @@ -104,45 +104,44 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err } // IsControlled determines if the in-memory Channel Controller should control (and therefore -// reconcile) a given object, based on that object's ClusterProvisioner reference. kind is the kind -// of that object. -func IsControlled(ref *eventingv1alpha1.ProvisionerReference, kind string) bool { +// reconcile) a given object, based on that object's ClusterChannelProvisioner reference. +func IsControlled(ref *eventingv1alpha1.ProvisionerReference) bool { if ref != nil && ref.Ref != nil { - return shouldReconcile(ref.Ref.Namespace, ref.Ref.Name, kind) + return shouldReconcile(ref.Ref.Namespace, ref.Ref.Name) } return false } // shouldReconcile determines if this Controller should control (and therefore reconcile) a given -// ClusterProvisioner. This Controller only handles in-memory channels. -func shouldReconcile(namespace, name, kind string) bool { - return namespace == "" && name == Name && kind == Channel +// ClusterChannelProvisioner. This Controller only handles in-memory channels. +func shouldReconcile(namespace, name string) bool { + return namespace == "" && name == Name } -func (r *reconciler) reconcile(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { - logger := r.logger.With(zap.Any("clusterProvisioner", cp)) +func (r *reconciler) reconcile(ctx context.Context, ccp *eventingv1alpha1.ClusterChannelProvisioner) error { + logger := r.logger.With(zap.Any("clusterChannelProvisioner", ccp)) // We are syncing one thing. // 1. The K8s Service to talk to all in-memory Channels. // - There is a single K8s Service for all requests going any in-memory Channel. - if cp.DeletionTimestamp != nil { - // K8s garbage collection will delete the dispatcher service, once this ClusterProvisioner + if ccp.DeletionTimestamp != nil { + // K8s garbage collection will delete the dispatcher service, once this ClusterChannelProvisioner // is deleted, so we don't need to do anything. return nil } - if err := r.createDispatcherService(ctx, cp); err != nil { - logger.Info("Error creating the ClusterProvisioner's K8s Service", zap.Error(err)) + if err := r.createDispatcherService(ctx, ccp); err != nil { + logger.Info("Error creating the ClusterChannelProvisioner's K8s Service", zap.Error(err)) return err } - cp.Status.MarkReady() + ccp.Status.MarkReady() return nil } -func (r *reconciler) createDispatcherService(ctx context.Context, cp *eventingv1alpha1.ClusterProvisioner) error { - svcName := controller.ClusterBusDispatcherServiceName(cp.Name) +func (r *reconciler) createDispatcherService(ctx context.Context, ccp *eventingv1alpha1.ClusterChannelProvisioner) error { + svcName := controller.ClusterBusDispatcherServiceName(ccp.Name) svcKey := types.NamespacedName{ Namespace: system.Namespace, Name: svcName, @@ -151,7 +150,7 @@ func (r *reconciler) createDispatcherService(ctx context.Context, cp *eventingv1 err := r.client.Get(ctx, svcKey, svc) if errors.IsNotFound(err) { - svc = newDispatcherService(cp) + svc = newDispatcherService(ccp) err = r.client.Create(ctx, svc) } @@ -160,17 +159,17 @@ func (r *reconciler) createDispatcherService(ctx context.Context, cp *eventingv1 return err } - // Check if this ClusterProvisioner is the owner of the K8s service. - if !metav1.IsControlledBy(svc, cp) { - r.logger.Warn("ClusterProvisioner's K8s Service is not owned by the ClusterProvisioner", zap.Any("clusterProvisioner", cp), zap.Any("service", svc)) + // Check if this ClusterChannelProvisioner is the owner of the K8s service. + if !metav1.IsControlledBy(svc, ccp) { + r.logger.Warn("ClusterChannelProvisioner's K8s Service is not owned by the ClusterChannelProvisioner", zap.Any("clusterChannelProvisioner", ccp), zap.Any("service", svc)) } return nil } -func (r *reconciler) updateClusterProvisionerStatus(ctx context.Context, u *eventingv1alpha1.ClusterProvisioner) error { - o := &eventingv1alpha1.ClusterProvisioner{} +func (r *reconciler) updateClusterChannelProvisionerStatus(ctx context.Context, u *eventingv1alpha1.ClusterChannelProvisioner) error { + o := &eventingv1alpha1.ClusterChannelProvisioner{} if err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o); err != nil { - r.logger.Info("Error getting ClusterProvisioner for status update", zap.Error(err), zap.Any("updatedClusterProvisioner", u)) + r.logger.Info("Error getting ClusterChannelProvisioner for status update", zap.Error(err), zap.Any("updatedClusterChannelProvisioner", u)) return err } @@ -184,18 +183,18 @@ func (r *reconciler) updateClusterProvisionerStatus(ctx context.Context, u *even // newDispatcherService creates a new Service for a ClusterBus resource. It also sets // the appropriate OwnerReferences on the resource so handleObject can discover // the ClusterBus resource that 'owns' it. -func newDispatcherService(cp *eventingv1alpha1.ClusterProvisioner) *corev1.Service { - labels := dispatcherLabels(cp.Name) +func newDispatcherService(ccp *eventingv1alpha1.ClusterChannelProvisioner) *corev1.Service { + labels := dispatcherLabels(ccp.Name) return &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ - Name: controller.ClusterBusDispatcherServiceName(cp.Name), + Name: controller.ClusterBusDispatcherServiceName(ccp.Name), Namespace: system.Namespace, Labels: labels, OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(cp, schema.GroupVersionKind{ + *metav1.NewControllerRef(ccp, schema.GroupVersionKind{ Group: eventingv1alpha1.SchemeGroupVersion.Group, Version: eventingv1alpha1.SchemeGroupVersion.Version, - Kind: "ClusterProvisioner", + Kind: "ClusterChannelProvisioner", }), }, }, @@ -212,9 +211,9 @@ func newDispatcherService(cp *eventingv1alpha1.ClusterProvisioner) *corev1.Servi } } -func dispatcherLabels(cpName string) map[string]string { +func dispatcherLabels(ccpName string) map[string]string { return map[string]string{ - "clusterProvisioner": cpName, - "role": "dispatcher", + "clusterChannelProvisioner": ccpName, + "role": "dispatcher", } } diff --git a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go similarity index 76% rename from pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go rename to pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go index f0bd62dbdce..3d857ec83ff 100644 --- a/pkg/controller/eventing/inmemory/clusterprovisioner/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package clusterprovisioner +package clusterchannelprovisioner import ( "context" @@ -39,7 +39,7 @@ import ( ) const ( - cpUid = "test-uid" + ccpUid = "test-uid" testErrorMessage = "test-induced-error" ) @@ -76,19 +76,16 @@ func TestInjectClient(t *testing.T) { func TestIsControlled(t *testing.T) { testCases := map[string]struct { ref *eventingv1alpha1.ProvisionerReference - kind string isControlled bool }{ "nil": { ref: nil, - kind: "Channel", isControlled: false, }, "ref nil": { ref: &eventingv1alpha1.ProvisionerReference{ Ref: nil, }, - kind: "Channel", isControlled: false, }, "wrong namespace": { @@ -98,7 +95,6 @@ func TestIsControlled(t *testing.T) { Name: Name, }, }, - kind: "Channel", isControlled: false, }, "wrong name": { @@ -107,16 +103,6 @@ func TestIsControlled(t *testing.T) { Name: "other-name", }, }, - kind: "Channel", - isControlled: false, - }, - "wrong kind": { - ref: &eventingv1alpha1.ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: Name, - }, - }, - kind: "Source", isControlled: false, }, "is controlled": { @@ -125,13 +111,12 @@ func TestIsControlled(t *testing.T) { Name: Name, }, }, - kind: "Channel", isControlled: true, }, } for n, tc := range testCases { t.Run(n, func(t *testing.T) { - isControlled := IsControlled(tc.ref, tc.kind) + isControlled := IsControlled(tc.ref) if isControlled != tc.isControlled { t.Errorf("Expected: %v. Actual: %v", tc.isControlled, isControlled) } @@ -142,13 +127,13 @@ func TestIsControlled(t *testing.T) { func TestReconcile(t *testing.T) { testCases := []controllertesting.TestCase{ { - Name: "CP not found", + Name: "CCP not found", }, { - Name: "Unable to get CP", + Name: "Unable to get CCP", Mocks: controllertesting.Mocks{ MockGets: []controllertesting.MockGet{ - errorGettingClusterProvisioner(), + errorGettingClusterChannelProvisioner(), }, }, WantErrMsg: testErrorMessage, @@ -156,7 +141,7 @@ func TestReconcile(t *testing.T) { { Name: "Should not reconcile - namespace", InitialState: []runtime.Object{ - &eventingv1alpha1.ClusterProvisioner{ + &eventingv1alpha1.ClusterChannelProvisioner{ ObjectMeta: metav1.ObjectMeta{ Namespace: "not empty string", Name: Name, @@ -167,7 +152,7 @@ func TestReconcile(t *testing.T) { { Name: "Should not reconcile - name", InitialState: []runtime.Object{ - &eventingv1alpha1.ClusterProvisioner{ + &eventingv1alpha1.ClusterChannelProvisioner{ ObjectMeta: metav1.ObjectMeta{ Name: "wrong-name", }, @@ -179,13 +164,13 @@ func TestReconcile(t *testing.T) { Name: "Delete succeeds", // Deleting does nothing. InitialState: []runtime.Object{ - makeDeletingClusterProvisioner(), + makeDeletingClusterChannelProvisioner(), }, }, { Name: "Create dispatcher fails", InitialState: []runtime.Object{ - makeClusterProvisioner(), + makeClusterChannelProvisioner(), }, Mocks: controllertesting.Mocks{ MockGets: []controllertesting.MockGet{ @@ -197,50 +182,50 @@ func TestReconcile(t *testing.T) { { Name: "Create dispatcher - already exists", InitialState: []runtime.Object{ - makeClusterProvisioner(), + makeClusterChannelProvisioner(), makeK8sService(), }, WantPresent: []runtime.Object{ - makeReadyClusterProvisioner(), + makeReadyClusterChannelProvisioner(), }, }, { - Name: "Create dispatcher - not owned by CP", + Name: "Create dispatcher - not owned by CCP", InitialState: []runtime.Object{ - makeClusterProvisioner(), - makeK8sServiceNotOwnedByClusterProvisioner(), + makeClusterChannelProvisioner(), + makeK8sServiceNotOwnedByClusterChannelProvisioner(), }, WantPresent: []runtime.Object{ - makeReadyClusterProvisioner(), + makeReadyClusterChannelProvisioner(), }, }, { Name: "Create dispatcher succeeds", InitialState: []runtime.Object{ - makeClusterProvisioner(), + makeClusterChannelProvisioner(), }, WantPresent: []runtime.Object{ - makeReadyClusterProvisioner(), + makeReadyClusterChannelProvisioner(), makeK8sService(), }, }, { - Name: "Error getting CP for updating Status", - // Nothing to create or update other than the status of CP itself. + Name: "Error getting CCP for updating Status", + // Nothing to create or update other than the status of CCP itself. InitialState: []runtime.Object{ - makeClusterProvisioner(), + makeClusterChannelProvisioner(), makeK8sService(), }, Mocks: controllertesting.Mocks{ - MockGets: oneSuccessfulClusterProvisionerGet(), + MockGets: oneSuccessfulClusterChannelProvisionerGet(), }, WantErrMsg: testErrorMessage, }, { Name: "Error updating Status", - // Nothing to create or update other than the status of CP itself. + // Nothing to create or update other than the status of CCP itself. InitialState: []runtime.Object{ - makeClusterProvisioner(), + makeClusterChannelProvisioner(), makeK8sService(), }, Mocks: controllertesting.Mocks{ @@ -267,40 +252,35 @@ func TestReconcile(t *testing.T) { } } -func makeClusterProvisioner() *eventingv1alpha1.ClusterProvisioner { - return &eventingv1alpha1.ClusterProvisioner{ +func makeClusterChannelProvisioner() *eventingv1alpha1.ClusterChannelProvisioner { + return &eventingv1alpha1.ClusterChannelProvisioner{ TypeMeta: metav1.TypeMeta{ APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), - Kind: "ClusterProvisioner", + Kind: "ClusterChannelProvisioner", }, ObjectMeta: metav1.ObjectMeta{ Name: Name, - UID: cpUid, - }, - Spec: eventingv1alpha1.ClusterProvisionerSpec{ - Reconciles: metav1.GroupKind{ - Group: "eventing.knative.dev/v1alpha1", - Kind: "Channel", - }, + UID: ccpUid, }, + Spec: eventingv1alpha1.ClusterChannelProvisionerSpec{}, } } -func makeReadyClusterProvisioner() *eventingv1alpha1.ClusterProvisioner { - cp := makeClusterProvisioner() - cp.Status.Conditions = []duckv1alpha1.Condition{ +func makeReadyClusterChannelProvisioner() *eventingv1alpha1.ClusterChannelProvisioner { + ccp := makeClusterChannelProvisioner() + ccp.Status.Conditions = []duckv1alpha1.Condition{ { Type: duckv1alpha1.ConditionReady, Status: corev1.ConditionTrue, }, } - return cp + return ccp } -func makeDeletingClusterProvisioner() *eventingv1alpha1.ClusterProvisioner { - cp := makeClusterProvisioner() - cp.DeletionTimestamp = &deletionTime - return cp +func makeDeletingClusterChannelProvisioner() *eventingv1alpha1.ClusterChannelProvisioner { + ccp := makeClusterChannelProvisioner() + ccp.DeletionTimestamp = &deletionTime + return ccp } func makeK8sService() *corev1.Service { @@ -315,9 +295,9 @@ func makeK8sService() *corev1.Service { OwnerReferences: []metav1.OwnerReference{ { APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), - Kind: "ClusterProvisioner", + Kind: "ClusterChannelProvisioner", Name: Name, - UID: cpUid, + UID: ccpUid, Controller: &truePointer, BlockOwnerDeletion: &truePointer, }, @@ -337,13 +317,13 @@ func makeK8sService() *corev1.Service { } } -func makeK8sServiceNotOwnedByClusterProvisioner() *corev1.Service { +func makeK8sServiceNotOwnedByClusterChannelProvisioner() *corev1.Service { svc := makeK8sService() svc.OwnerReferences = nil return svc } -func errorGettingClusterProvisioner() controllertesting.MockGet { +func errorGettingClusterChannelProvisioner() controllertesting.MockGet { return func(client.Client, context.Context, client.ObjectKey, runtime.Object) (controllertesting.MockHandled, error) { return controllertesting.Handled, errors.New(testErrorMessage) } @@ -358,16 +338,16 @@ func errorGettingK8sService() controllertesting.MockGet { } } -func oneSuccessfulClusterProvisionerGet() []controllertesting.MockGet { +func oneSuccessfulClusterChannelProvisionerGet() []controllertesting.MockGet { return []controllertesting.MockGet{ // The first one is a pass through. func(innerClient client.Client, ctx context.Context, key client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { err := innerClient.Get(ctx, key, obj) return controllertesting.Handled, err }, - // All subsequent ClusterProvisioner Gets fail. + // All subsequent ClusterChannelProvisioner Gets fail. func(_ client.Client, _ context.Context, _ client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { - if _, ok := obj.(*eventingv1alpha1.ClusterProvisioner); ok { + if _, ok := obj.(*eventingv1alpha1.ClusterChannelProvisioner); ok { return controllertesting.Handled, errors.New(testErrorMessage) } return controllertesting.Unhandled, nil diff --git a/pkg/controller/eventing/inmemory/controller/main.go b/pkg/controller/eventing/inmemory/controller/main.go index c5485a9e719..bb0a680a72b 100644 --- a/pkg/controller/eventing/inmemory/controller/main.go +++ b/pkg/controller/eventing/inmemory/controller/main.go @@ -22,7 +22,7 @@ import ( eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/buses" "github.com/knative/eventing/pkg/controller/eventing/inmemory/channel" - "github.com/knative/eventing/pkg/controller/eventing/inmemory/clusterprovisioner" + "github.com/knative/eventing/pkg/controller/eventing/inmemory/clusterchannelprovisioner" istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" "github.com/knative/pkg/signals" "go.uber.org/zap" @@ -35,8 +35,8 @@ func main() { logger := buses.NewBusLoggerFromConfig(logConfig) defer logger.Sync() logger = logger.With( - zap.String("eventing.knative.dev/clusterProvisioner", clusterprovisioner.Name), - zap.String("eventing.knative.dev/clusterProvisionerComponent", "Controller"), + zap.String("eventing.knative.dev/clusterChannelProvisioner", clusterchannelprovisioner.Name), + zap.String("eventing.knative.dev/clusterChannelProvisionerComponent", "Controller"), ) flag.Parse() @@ -49,9 +49,9 @@ func main() { eventingv1alpha1.AddToScheme(mgr.GetScheme()) istiov1alpha3.AddToScheme(mgr.GetScheme()) - // The controllers for both the ClusterProvisioner and the Channels created by that - // ClusterProvisioner run in this process. - _, err = clusterprovisioner.ProvideController(mgr, logger.Desugar()) + // The controllers for both the ClusterChannelProvisioner and the Channels created by that + // ClusterChannelProvisioner run in this process. + _, err = clusterchannelprovisioner.ProvideController(mgr, logger.Desugar()) if err != nil { logger.Fatal("Unable to create Provisioner controller", zap.Error(err)) } diff --git a/pkg/controller/owner_references.go b/pkg/controller/owner_references.go index db5a2a51977..073e2a8ed0f 100644 --- a/pkg/controller/owner_references.go +++ b/pkg/controller/owner_references.go @@ -55,8 +55,8 @@ func kind(obj metav1.Object) schema.GroupVersionKind { // Eventing case *eventingv1alpha.Channel: return eventingv1alpha.SchemeGroupVersion.WithKind("Channel") - case *eventingv1alpha.ClusterProvisioner: - return eventingv1alpha.SchemeGroupVersion.WithKind("ClusterProvisioner") + case *eventingv1alpha.ClusterChannelProvisioner: + return eventingv1alpha.SchemeGroupVersion.WithKind("ClusterChannelProvisioner") case *eventingv1alpha.Subscription: return eventingv1alpha.SchemeGroupVersion.WithKind("Subscription") From 642bfc15da9afa694e8adc9249e70302551aab60 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Fri, 26 Oct 2018 13:33:32 -0400 Subject: [PATCH 05/54] Flatten ProvisionerReference (#544) ProvisionerReference contains a single required field. We can simplify the object model by moving the ObjectRef for a provisioner up one level. --- docs/spec/spec.md | 16 ++--- pkg/apis/eventing/v1alpha1/channel_types.go | 4 +- .../v1alpha1/channel_validation_test.go | 72 +++++++------------ .../v1alpha1/provisioner_reference.go | 29 -------- .../v1alpha1/zz_generated.deepcopy.go | 29 +------- .../eventing/inmemory/channel/reconcile.go | 6 +- .../inmemory/channel/reconcile_test.go | 36 +++------- .../clusterchannelprovisioner/reconcile.go | 6 +- .../reconcile_test.go | 28 +++----- 9 files changed, 57 insertions(+), 169 deletions(-) delete mode 100644 pkg/apis/eventing/v1alpha1/provisioner_reference.go diff --git a/docs/spec/spec.md b/docs/spec/spec.md index d0f4a328bc2..3566b45f7e3 100644 --- a/docs/spec/spec.md +++ b/docs/spec/spec.md @@ -26,7 +26,7 @@ its subscribers._ | Field | Type | Description | Constraints | | ------------- | ---------------------------------- | -------------------------------------------------------------------------- | -------------------------------------- | -| provisioner\* | ProvisionerReference | The name of the provisioner to create the resources that back the Channel. | Immutable. | +| provisioner\* | ObjectReference | The name of the provisioner to create the resources that back the Channel. | Immutable. | | arguments | runtime.RawExtension (JSON object) | Arguments to be passed to the provisioner. | | | subscribers | ChannelSubscriberSpec[] | Information about subscriptions used to implement message forwarding. | Filled out by Subscription Controller. | @@ -125,9 +125,9 @@ or a Channel system that receives and delivers events._ #### Spec -| Field | Type | Description | Constraints | -| ---------- | ---------------------------------- | ------------------------------------------------------------------------------------------------- | ---------------- | -| parameters | runtime.RawExtension (JSON object) | Description of the arguments able to be passed by the provisioned resource (not enforced in 0.1). | JSON Schema | +| Field | Type | Description | Constraints | +| ---------- | ---------------------------------- | ------------------------------------------------------------------------------------------------- | ----------- | +| parameters | runtime.RawExtension (JSON object) | Description of the arguments able to be passed by the provisioned resource (not enforced in 0.1). | JSON Schema | \*: Required @@ -150,14 +150,6 @@ or a Channel system that receives and delivers events._ ## Shared Object Schema -### ProvisionerReference - -| Field | Type | Description | Constraints | -| ----- | --------------- | ----------- | ----------- | -| ref\* | ObjectReference | | | - -\*: Required - ### EndpointSpec | Field | Type | Description | Constraints | diff --git a/pkg/apis/eventing/v1alpha1/channel_types.go b/pkg/apis/eventing/v1alpha1/channel_types.go index 56702f56d00..801e7a4b077 100644 --- a/pkg/apis/eventing/v1alpha1/channel_types.go +++ b/pkg/apis/eventing/v1alpha1/channel_types.go @@ -21,6 +21,7 @@ import ( "github.com/knative/pkg/apis" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" "github.com/knative/pkg/webhook" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) @@ -64,8 +65,7 @@ type ChannelSpec struct { Generation int64 `json:"generation,omitempty"` // Provisioner defines the name of the Provisioner backing this channel. - // TODO: +optional If missing, a default Provisioner may be selected for the Channel. - Provisioner *ProvisionerReference `json:"provisioner,omitempty"` + Provisioner *corev1.ObjectReference `json:"provisioner,omitempty"` // Arguments defines the arguments to pass to the Provisioner which provisions // this Channel. diff --git a/pkg/apis/eventing/v1alpha1/channel_validation_test.go b/pkg/apis/eventing/v1alpha1/channel_validation_test.go index 6255252a45d..9626207cbb3 100644 --- a/pkg/apis/eventing/v1alpha1/channel_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/channel_validation_test.go @@ -33,10 +33,8 @@ func TestChannelValidation(t *testing.T) { name: "valid", cr: &Channel{ Spec: ChannelSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, + Provisioner: &corev1.ObjectReference{ + Name: "foo", }, }, }, @@ -51,10 +49,8 @@ func TestChannelValidation(t *testing.T) { name: "subscribers array", cr: &Channel{ Spec: ChannelSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, + Provisioner: &corev1.ObjectReference{ + Name: "foo", }, Channelable: &eventingduck.Channelable{ Subscribers: []eventingduck.ChannelSubscriberSpec{{ @@ -68,10 +64,8 @@ func TestChannelValidation(t *testing.T) { name: "empty subscriber at index 1", cr: &Channel{ Spec: ChannelSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, + Provisioner: &corev1.ObjectReference{ + Name: "foo", }, Channelable: &eventingduck.Channelable{ Subscribers: []eventingduck.ChannelSubscriberSpec{{ @@ -89,10 +83,8 @@ func TestChannelValidation(t *testing.T) { name: "2 empty subscribers", cr: &Channel{ Spec: ChannelSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, + Provisioner: &corev1.ObjectReference{ + Name: "foo", }, Channelable: &eventingduck.Channelable{ Subscribers: []eventingduck.ChannelSubscriberSpec{{}, {}}, @@ -124,10 +116,8 @@ func TestChannelImmutableFields(t *testing.T) { name: "good (new)", new: &Channel{ Spec: ChannelSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, + Provisioner: &corev1.ObjectReference{ + Name: "foo", }, }, }, @@ -137,19 +127,15 @@ func TestChannelImmutableFields(t *testing.T) { name: "good (no change)", new: &Channel{ Spec: ChannelSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, + Provisioner: &corev1.ObjectReference{ + Name: "foo", }, }, }, old: &Channel{ Spec: ChannelSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, + Provisioner: &corev1.ObjectReference{ + Name: "foo", }, }, }, @@ -158,10 +144,8 @@ func TestChannelImmutableFields(t *testing.T) { name: "good (arguments change)", new: &Channel{ Spec: ChannelSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, + Provisioner: &corev1.ObjectReference{ + Name: "foo", }, Arguments: &runtime.RawExtension{ Raw: []byte("\"foo\":\"bar\""), @@ -170,10 +154,8 @@ func TestChannelImmutableFields(t *testing.T) { }, old: &Channel{ Spec: ChannelSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, + Provisioner: &corev1.ObjectReference{ + Name: "foo", }, Arguments: &runtime.RawExtension{ Raw: []byte(`{"foo":"baz"}`), @@ -185,10 +167,8 @@ func TestChannelImmutableFields(t *testing.T) { name: "bad (not channel)", new: &Channel{ Spec: ChannelSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, + Provisioner: &corev1.ObjectReference{ + Name: "foo", }, }, }, @@ -200,19 +180,15 @@ func TestChannelImmutableFields(t *testing.T) { name: "bad (provisioner changes)", new: &Channel{ Spec: ChannelSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "foo", - }, + Provisioner: &corev1.ObjectReference{ + Name: "foo", }, }, }, old: &Channel{ Spec: ChannelSpec{ - Provisioner: &ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "bar", - }, + Provisioner: &corev1.ObjectReference{ + Name: "bar", }, }, }, diff --git a/pkg/apis/eventing/v1alpha1/provisioner_reference.go b/pkg/apis/eventing/v1alpha1/provisioner_reference.go deleted file mode 100644 index 1d0bbca4cbe..00000000000 --- a/pkg/apis/eventing/v1alpha1/provisioner_reference.go +++ /dev/null @@ -1,29 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package v1alpha1 - -import ( - corev1 "k8s.io/api/core/v1" -) - -// ProvisionerReference defines the strategy for selecting a Provisioner for a -// provisioned resource. Shared by Channel and Source. -type ProvisionerReference struct { - // A reference to a specific Provisioner. - //TODO: +optional add selector - Ref *corev1.ObjectReference `json:"ref,omitempty"` -} diff --git a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go index a472830054a..6929da841c0 100644 --- a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go @@ -96,8 +96,8 @@ func (in *ChannelSpec) DeepCopyInto(out *ChannelSpec) { if *in == nil { *out = nil } else { - *out = new(ProvisionerReference) - (*in).DeepCopyInto(*out) + *out = new(v1.ObjectReference) + **out = **in } } if in.Arguments != nil { @@ -289,31 +289,6 @@ func (in *EndpointSpec) DeepCopy() *EndpointSpec { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ProvisionerReference) DeepCopyInto(out *ProvisionerReference) { - *out = *in - if in.Ref != nil { - in, out := &in.Ref, &out.Ref - if *in == nil { - *out = nil - } else { - *out = new(v1.ObjectReference) - **out = **in - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProvisionerReference. -func (in *ProvisionerReference) DeepCopy() *ProvisionerReference { - if in == nil { - return nil - } - out := new(ProvisionerReference) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ResultStrategy) DeepCopyInto(out *ResultStrategy) { *out = *in diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 9d2ea255ac8..67b1b03ee91 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -240,7 +240,7 @@ func (r *reconciler) createVirtualService(ctx context.Context, c *eventingv1alph func newK8sService(c *eventingv1alpha1.Channel) *corev1.Service { labels := map[string]string{ "channel": c.Name, - "provisioner": c.Spec.Provisioner.Ref.Name, + "provisioner": c.Spec.Provisioner.Name, } return &corev1.Service{ ObjectMeta: metav1.ObjectMeta{ @@ -272,9 +272,9 @@ func newK8sService(c *eventingv1alpha1.Channel) *corev1.Service { func newVirtualService(channel *eventingv1alpha1.Channel) *istiov1alpha3.VirtualService { labels := map[string]string{ "channel": channel.Name, - "provisioner": channel.Spec.Provisioner.Ref.Name, + "provisioner": channel.Spec.Provisioner.Name, } - destinationHost := controller.ServiceHostName(controller.ClusterBusDispatcherServiceName(channel.Spec.Provisioner.Ref.Name), system.Namespace) + destinationHost := controller.ServiceHostName(controller.ClusterBusDispatcherServiceName(channel.Spec.Provisioner.Name), system.Namespace) return &istiov1alpha3.VirtualService{ ObjectMeta: metav1.ObjectMeta{ Name: controller.ChannelVirtualServiceName(channel.Name), diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index 8f77535c2b1..f3a3bda8df0 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -111,10 +111,8 @@ var ( Kind: "Channel", }, Spec: eventingv1alpha1.ChannelSpec{ - Provisioner: &eventingv1alpha1.ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: ccpName, - }, + Provisioner: &corev1.ObjectReference{ + Name: ccpName, }, Channelable: &eventingduck.Channelable{ Subscribers: []eventingduck.ChannelSubscriberSpec{ @@ -141,10 +139,8 @@ var ( Kind: "Channel", }, Spec: eventingv1alpha1.ChannelSpec{ - Provisioner: &eventingv1alpha1.ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "some-other-provisioner", - }, + Provisioner: &corev1.ObjectReference{ + Name: "some-other-provisioner", }, Channelable: &eventingduck.Channelable{ Subscribers: []eventingduck.ChannelSubscriberSpec{ @@ -164,10 +160,8 @@ var ( Kind: "Channel", }, Spec: eventingv1alpha1.ChannelSpec{ - Provisioner: &eventingv1alpha1.ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: ccpName, - }, + Provisioner: &corev1.ObjectReference{ + Name: ccpName, }, Channelable: &eventingduck.Channelable{ Subscribers: []eventingduck.ChannelSubscriberSpec{ @@ -225,7 +219,7 @@ func TestReconcile(t *testing.T) { { Name: "Channel not reconciled - nil ref", InitialState: []runtime.Object{ - makeChannelNilRef(), + makeChannelNilProvisioner(), }, }, { @@ -472,10 +466,8 @@ func makeChannel() *eventingv1alpha1.Channel { UID: cUID, }, Spec: eventingv1alpha1.ChannelSpec{ - Provisioner: &eventingv1alpha1.ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: ccpName, - }, + Provisioner: &corev1.ObjectReference{ + Name: ccpName, }, }, } @@ -502,21 +494,15 @@ func makeChannelNilProvisioner() *eventingv1alpha1.Channel { return c } -func makeChannelNilRef() *eventingv1alpha1.Channel { - c := makeChannel() - c.Spec.Provisioner.Ref = nil - return c -} - func makeChannelWithWrongProvisionerNamespace() *eventingv1alpha1.Channel { c := makeChannel() - c.Spec.Provisioner.Ref.Namespace = "wrong-namespace" + c.Spec.Provisioner.Namespace = "wrong-namespace" return c } func makeChannelWithWrongProvisionerName() *eventingv1alpha1.Channel { c := makeChannel() - c.Spec.Provisioner.Ref.Name = "wrong-name" + c.Spec.Provisioner.Name = "wrong-name" return c } diff --git a/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile.go b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile.go index 2fc32debef5..10a8dac8600 100644 --- a/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile.go +++ b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile.go @@ -105,9 +105,9 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err // IsControlled determines if the in-memory Channel Controller should control (and therefore // reconcile) a given object, based on that object's ClusterChannelProvisioner reference. -func IsControlled(ref *eventingv1alpha1.ProvisionerReference) bool { - if ref != nil && ref.Ref != nil { - return shouldReconcile(ref.Ref.Namespace, ref.Ref.Name) +func IsControlled(ref *corev1.ObjectReference) bool { + if ref != nil { + return shouldReconcile(ref.Namespace, ref.Name) } return false } diff --git a/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go index 3d857ec83ff..b4f9945cc2f 100644 --- a/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go @@ -75,41 +75,29 @@ func TestInjectClient(t *testing.T) { func TestIsControlled(t *testing.T) { testCases := map[string]struct { - ref *eventingv1alpha1.ProvisionerReference + ref *corev1.ObjectReference isControlled bool }{ "nil": { ref: nil, isControlled: false, }, - "ref nil": { - ref: &eventingv1alpha1.ProvisionerReference{ - Ref: nil, - }, - isControlled: false, - }, "wrong namespace": { - ref: &eventingv1alpha1.ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Namespace: "other", - Name: Name, - }, + ref: &corev1.ObjectReference{ + Namespace: "other", + Name: Name, }, isControlled: false, }, "wrong name": { - ref: &eventingv1alpha1.ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: "other-name", - }, + ref: &corev1.ObjectReference{ + Name: "other-name", }, isControlled: false, }, "is controlled": { - ref: &eventingv1alpha1.ProvisionerReference{ - Ref: &corev1.ObjectReference{ - Name: Name, - }, + ref: &corev1.ObjectReference{ + Name: Name, }, isControlled: true, }, From 5f2b4133860c26f46cadeeb16384c024631860ba Mon Sep 17 00:00:00 2001 From: Mark Fisher Date: Mon, 29 Oct 2018 18:10:33 -0400 Subject: [PATCH 06/54] minor edits (#554) --- docs/spec/overview.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/spec/overview.md b/docs/spec/overview.md index f111164d321..d0fd814364f 100644 --- a/docs/spec/overview.md +++ b/docs/spec/overview.md @@ -1,16 +1,16 @@ # Resource Types The API defines and provides a complete implementation for -[Subscription](spec.md#kind-subscription), and abstract resource definitions +[Subscription](spec.md#kind-subscription) and abstract resource definitions for [Channels](spec.md#kind-channel) and [ClusterChannelProvisioners](spec.md#kind-clusterchannelprovisioner) which may be fulfilled by multiple backing implementations (much like the Kubernetes Ingress resource). -With extendibility and compostability as a goal of Knative Eventing, the -eventing API defines several resources that can be reduced down to a well +With extensibility and composability as a goal of Knative Eventing, the +eventing API defines several resources that can be reduced down to well understood contracts. These eventing resource interfaces may be fulfilled by -other Kubernetes objects and then composed in the same way as the concreate +other Kubernetes objects and then composed in the same way as the concrete objects. The interfaces are ([Sinkable](interfaces.md#sinkable), [Subscribable](interfaces.md#Subscribable), [Targetable](interfaces.md#targetable)). For more details, see @@ -19,7 +19,7 @@ objects. The interfaces are ([Sinkable](interfaces.md#sinkable), - A **Subscription** describes the transformation of an event and optional forwarding of a returned event. -- A **Channel** provides event persistance and fanout of events from a +- A **Channel** provides event persistence and fanout of events from a well-known input address to multiple outputs described by _Subscriptions_. @@ -40,7 +40,7 @@ Sources](https://github.com/knative/eventing-sources). ## Subscription -**Subscriptions** describe a flow of events from one _Channel_) to the next +**Subscriptions** describe a flow of events from one _Channel_ to the next Channel\* through transformations (such as a Knative Service which processes CloudEvents over HTTP). A _Subscription_ controller resolves the addresses of transformations (`call`) and destination storage (`result`) through the @@ -56,12 +56,12 @@ For more details, see [Kind: Subscription](spec.md#kind-subscription). ## Channel -**Channel** provides an at least once event delivery mechanism which can fan -out received events to multiple destinations via _Subscriptions_. A _Channel_ -has a single inbound _Sinkable_ interface which may accept events from multiple -_Subscriptions_ or even direct delivery from external systems. Different -_Channels_ may implement different degrees of persistence. Event delivery order -is dependent on the backing implementation of the _Channel_ provided by the +**Channel** provides an event delivery mechanism which can fan out received +events to multiple destinations via _Subscriptions_. A _Channel_ has a single +inbound _Sinkable_ interface which may accept events delivered directly or +forwarded from multiple _Subscriptions_. Different _Channels_ may implement +different degrees of persistence. Event delivery order is dependent on the +backing implementation of the _Channel_ provided by the _ClusterChannelProvisioner_. Event selection on a _Channel_ is 1:N – a single _Channel_ may fan out to From 36b014606445b94c91a9360fff748b40baab0123 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Mon, 29 Oct 2018 17:43:33 -0700 Subject: [PATCH 07/54] Update in-memory-channel.yaml to conform to the changes made in https://github.com/knative/eventing/pull/562. (#564) --- config/provisioners/in-memory-channel/in-memory-channel.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/config/provisioners/in-memory-channel/in-memory-channel.yaml b/config/provisioners/in-memory-channel/in-memory-channel.yaml index 547dbb3ebc9..f0a5de2c42f 100644 --- a/config/provisioners/in-memory-channel/in-memory-channel.yaml +++ b/config/provisioners/in-memory-channel/in-memory-channel.yaml @@ -16,10 +16,7 @@ apiVersion: eventing.knative.dev/v1alpha1 kind: ClusterChannelProvisioner metadata: name: in-memory-channel -spec: - reconciles: - group: eventing.knative.dev/v1alpha1 - kind: Channel +spec: {} --- From f9133db32ae9ee7c4104cf637b39ababc8ccdbfb Mon Sep 17 00:00:00 2001 From: Ville Aikas Date: Tue, 30 Oct 2018 09:38:33 -0700 Subject: [PATCH 08/54] fix typo in the path name (#567) * fix typo in the path name * remove the extra ref from the spec.provisioner --- config/provisioners/in-memory-channel/README.md | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/config/provisioners/in-memory-channel/README.md b/config/provisioners/in-memory-channel/README.md index 81b065f61f1..814a592bce9 100644 --- a/config/provisioners/in-memory-channel/README.md +++ b/config/provisioners/in-memory-channel/README.md @@ -20,7 +20,7 @@ They differ from most Channels in that they have: 1. Setup [Knative Eventing](../../../DEVELOPMENT.md). 1. Apply the 'in-memory-channel' ClusterChannelProvisioner, Controller, and Dispatcher. ```shell - ko apply -f config/providers/in-memory-channel/in-memory-channel.yaml + ko apply -f config/provisioners/in-memory-channel/in-memory-channel.yaml ```` 1. Create Channels that reference the 'in-memory-channel'. @@ -31,10 +31,9 @@ They differ from most Channels in that they have: name: foo spec: provisioner: - ref: - apiVersion: eventing.knative.dev/v1alpha1 - kind: ClusterChannelProvisioner - name: in-memory-channel + apiVersion: eventing.knative.dev/v1alpha1 + kind: ClusterChannelProvisioner + name: in-memory-channel ``` ### Components From 0191960e6bf7cc6d4b99d3e486b109d558f47d64 Mon Sep 17 00:00:00 2001 From: Dave Protasowski Date: Tue, 30 Oct 2018 15:05:34 -0400 Subject: [PATCH 09/54] scan all main packages for when collecting licenses (#556) --- hack/update-deps.sh | 3 +- third_party/VENDOR-LICENSE | 2378 +++++++++++++++++++++++++++++++----- 2 files changed, 2066 insertions(+), 315 deletions(-) diff --git a/hack/update-deps.sh b/hack/update-deps.sh index f95efc0dbd1..e4ef7ab9542 100755 --- a/hack/update-deps.sh +++ b/hack/update-deps.sh @@ -31,4 +31,5 @@ rm -rf $(find vendor/ -name 'BUILD.bazel') # Keep the only dir in knative/test-infra we're interested in find vendor/github.com/knative/test-infra -mindepth 1 -maxdepth 1 ! -name scripts -exec rm -fr {} \; -update_licenses third_party/VENDOR-LICENSE "./cmd/*" +update_licenses third_party/VENDOR-LICENSE \ + $(find . -name "*.go" | grep -v vendor | xargs grep "package main" | cut -d: -f1 | xargs -n1 dirname | uniq) diff --git a/third_party/VENDOR-LICENSE b/third_party/VENDOR-LICENSE index e1969f98375..49d95f12c06 100644 --- a/third_party/VENDOR-LICENSE +++ b/third_party/VENDOR-LICENSE @@ -207,6 +207,32 @@ Import: github.com/knative/eventing/vendor/cloud.google.com/go +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/Shopify/sarama + +Copyright (c) 2013 Shopify + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/beorn7/perks @@ -233,6 +259,34 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/bsm/sarama-cluster + +(The MIT License) + +Copyright (c) 2017 Black Square Media Ltd + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/davecgh/go-spew @@ -254,6 +308,87 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/eapache/go-resiliency + +The MIT License (MIT) + +Copyright (c) 2014 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/eapache/go-xerial-snappy + +The MIT License (MIT) + +Copyright (c) 2016 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/eapache/queue + +The MIT License (MIT) + +Copyright (c) 2014 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/fsnotify/fsnotify @@ -1231,6 +1366,39 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/golang/snappy + +Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/google/btree @@ -1472,6 +1640,386 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/google/go-github + +Copyright (c) 2013 The go-github AUTHORS. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +---------- + +Some documentation is taken from the GitHub Developer site +, which is available under the following Creative +Commons Attribution 3.0 License. This applies only to the go-github source +code and would not apply to any compiled binaries. + +THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE +COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY +COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS +AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. + +BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE +TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY +BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS +CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND +CONDITIONS. + +1. Definitions + + a. "Adaptation" means a work based upon the Work, or upon the Work and + other pre-existing works, such as a translation, adaptation, + derivative work, arrangement of music or other alterations of a + literary or artistic work, or phonogram or performance and includes + cinematographic adaptations or any other form in which the Work may be + recast, transformed, or adapted including in any form recognizably + derived from the original, except that a work that constitutes a + Collection will not be considered an Adaptation for the purpose of + this License. For the avoidance of doubt, where the Work is a musical + work, performance or phonogram, the synchronization of the Work in + timed-relation with a moving image ("synching") will be considered an + Adaptation for the purpose of this License. + b. "Collection" means a collection of literary or artistic works, such as + encyclopedias and anthologies, or performances, phonograms or + broadcasts, or other works or subject matter other than works listed + in Section 1(f) below, which, by reason of the selection and + arrangement of their contents, constitute intellectual creations, in + which the Work is included in its entirety in unmodified form along + with one or more other contributions, each constituting separate and + independent works in themselves, which together are assembled into a + collective whole. A work that constitutes a Collection will not be + considered an Adaptation (as defined above) for the purposes of this + License. + c. "Distribute" means to make available to the public the original and + copies of the Work or Adaptation, as appropriate, through sale or + other transfer of ownership. + d. "Licensor" means the individual, individuals, entity or entities that + offer(s) the Work under the terms of this License. + e. "Original Author" means, in the case of a literary or artistic work, + the individual, individuals, entity or entities who created the Work + or if no individual or entity can be identified, the publisher; and in + addition (i) in the case of a performance the actors, singers, + musicians, dancers, and other persons who act, sing, deliver, declaim, + play in, interpret or otherwise perform literary or artistic works or + expressions of folklore; (ii) in the case of a phonogram the producer + being the person or legal entity who first fixes the sounds of a + performance or other sounds; and, (iii) in the case of broadcasts, the + organization that transmits the broadcast. + f. "Work" means the literary and/or artistic work offered under the terms + of this License including without limitation any production in the + literary, scientific and artistic domain, whatever may be the mode or + form of its expression including digital form, such as a book, + pamphlet and other writing; a lecture, address, sermon or other work + of the same nature; a dramatic or dramatico-musical work; a + choreographic work or entertainment in dumb show; a musical + composition with or without words; a cinematographic work to which are + assimilated works expressed by a process analogous to cinematography; + a work of drawing, painting, architecture, sculpture, engraving or + lithography; a photographic work to which are assimilated works + expressed by a process analogous to photography; a work of applied + art; an illustration, map, plan, sketch or three-dimensional work + relative to geography, topography, architecture or science; a + performance; a broadcast; a phonogram; a compilation of data to the + extent it is protected as a copyrightable work; or a work performed by + a variety or circus performer to the extent it is not otherwise + considered a literary or artistic work. + g. "You" means an individual or entity exercising rights under this + License who has not previously violated the terms of this License with + respect to the Work, or who has received express permission from the + Licensor to exercise rights under this License despite a previous + violation. + h. "Publicly Perform" means to perform public recitations of the Work and + to communicate to the public those public recitations, by any means or + process, including by wire or wireless means or public digital + performances; to make available to the public Works in such a way that + members of the public may access these Works from a place and at a + place individually chosen by them; to perform the Work to the public + by any means or process and the communication to the public of the + performances of the Work, including by public digital performance; to + broadcast and rebroadcast the Work by any means including signs, + sounds or images. + i. "Reproduce" means to make copies of the Work by any means including + without limitation by sound or visual recordings and the right of + fixation and reproducing fixations of the Work, including storage of a + protected performance or phonogram in digital form or other electronic + medium. + +2. Fair Dealing Rights. Nothing in this License is intended to reduce, +limit, or restrict any uses free from copyright or rights arising from +limitations or exceptions that are provided for in connection with the +copyright protection under copyright law or other applicable laws. + +3. License Grant. Subject to the terms and conditions of this License, +Licensor hereby grants You a worldwide, royalty-free, non-exclusive, +perpetual (for the duration of the applicable copyright) license to +exercise the rights in the Work as stated below: + + a. to Reproduce the Work, to incorporate the Work into one or more + Collections, and to Reproduce the Work as incorporated in the + Collections; + b. to create and Reproduce Adaptations provided that any such Adaptation, + including any translation in any medium, takes reasonable steps to + clearly label, demarcate or otherwise identify that changes were made + to the original Work. For example, a translation could be marked "The + original work was translated from English to Spanish," or a + modification could indicate "The original work has been modified."; + c. to Distribute and Publicly Perform the Work including as incorporated + in Collections; and, + d. to Distribute and Publicly Perform Adaptations. + e. For the avoidance of doubt: + + i. Non-waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme cannot be waived, the Licensor + reserves the exclusive right to collect such royalties for any + exercise by You of the rights granted under this License; + ii. Waivable Compulsory License Schemes. In those jurisdictions in + which the right to collect royalties through any statutory or + compulsory licensing scheme can be waived, the Licensor waives the + exclusive right to collect such royalties for any exercise by You + of the rights granted under this License; and, + iii. Voluntary License Schemes. The Licensor waives the right to + collect royalties, whether individually or, in the event that the + Licensor is a member of a collecting society that administers + voluntary licensing schemes, via that society, from any exercise + by You of the rights granted under this License. + +The above rights may be exercised in all media and formats whether now +known or hereafter devised. The above rights include the right to make +such modifications as are technically necessary to exercise the rights in +other media and formats. Subject to Section 8(f), all rights not expressly +granted by Licensor are hereby reserved. + +4. Restrictions. The license granted in Section 3 above is expressly made +subject to and limited by the following restrictions: + + a. You may Distribute or Publicly Perform the Work only under the terms + of this License. You must include a copy of, or the Uniform Resource + Identifier (URI) for, this License with every copy of the Work You + Distribute or Publicly Perform. You may not offer or impose any terms + on the Work that restrict the terms of this License or the ability of + the recipient of the Work to exercise the rights granted to that + recipient under the terms of the License. You may not sublicense the + Work. You must keep intact all notices that refer to this License and + to the disclaimer of warranties with every copy of the Work You + Distribute or Publicly Perform. When You Distribute or Publicly + Perform the Work, You may not impose any effective technological + measures on the Work that restrict the ability of a recipient of the + Work from You to exercise the rights granted to that recipient under + the terms of the License. This Section 4(a) applies to the Work as + incorporated in a Collection, but this does not require the Collection + apart from the Work itself to be made subject to the terms of this + License. If You create a Collection, upon notice from any Licensor You + must, to the extent practicable, remove from the Collection any credit + as required by Section 4(b), as requested. If You create an + Adaptation, upon notice from any Licensor You must, to the extent + practicable, remove from the Adaptation any credit as required by + Section 4(b), as requested. + b. If You Distribute, or Publicly Perform the Work or any Adaptations or + Collections, You must, unless a request has been made pursuant to + Section 4(a), keep intact all copyright notices for the Work and + provide, reasonable to the medium or means You are utilizing: (i) the + name of the Original Author (or pseudonym, if applicable) if supplied, + and/or if the Original Author and/or Licensor designate another party + or parties (e.g., a sponsor institute, publishing entity, journal) for + attribution ("Attribution Parties") in Licensor's copyright notice, + terms of service or by other reasonable means, the name of such party + or parties; (ii) the title of the Work if supplied; (iii) to the + extent reasonably practicable, the URI, if any, that Licensor + specifies to be associated with the Work, unless such URI does not + refer to the copyright notice or licensing information for the Work; + and (iv) , consistent with Section 3(b), in the case of an Adaptation, + a credit identifying the use of the Work in the Adaptation (e.g., + "French translation of the Work by Original Author," or "Screenplay + based on original Work by Original Author"). The credit required by + this Section 4 (b) may be implemented in any reasonable manner; + provided, however, that in the case of a Adaptation or Collection, at + a minimum such credit will appear, if a credit for all contributing + authors of the Adaptation or Collection appears, then as part of these + credits and in a manner at least as prominent as the credits for the + other contributing authors. For the avoidance of doubt, You may only + use the credit required by this Section for the purpose of attribution + in the manner set out above and, by exercising Your rights under this + License, You may not implicitly or explicitly assert or imply any + connection with, sponsorship or endorsement by the Original Author, + Licensor and/or Attribution Parties, as appropriate, of You or Your + use of the Work, without the separate, express prior written + permission of the Original Author, Licensor and/or Attribution + Parties. + c. Except as otherwise agreed in writing by the Licensor or as may be + otherwise permitted by applicable law, if You Reproduce, Distribute or + Publicly Perform the Work either by itself or as part of any + Adaptations or Collections, You must not distort, mutilate, modify or + take other derogatory action in relation to the Work which would be + prejudicial to the Original Author's honor or reputation. Licensor + agrees that in those jurisdictions (e.g. Japan), in which any exercise + of the right granted in Section 3(b) of this License (the right to + make Adaptations) would be deemed to be a distortion, mutilation, + modification or other derogatory action prejudicial to the Original + Author's honor and reputation, the Licensor will waive or not assert, + as appropriate, this Section, to the fullest extent permitted by the + applicable national law, to enable You to reasonably exercise Your + right under Section 3(b) of this License (right to make Adaptations) + but not otherwise. + +5. Representations, Warranties and Disclaimer + +UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR +OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY +KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, +INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, +FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF +LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, +WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION +OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. + +6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE +LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR +ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES +ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS +BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. + +7. Termination + + a. This License and the rights granted hereunder will terminate + automatically upon any breach by You of the terms of this License. + Individuals or entities who have received Adaptations or Collections + from You under this License, however, will not have their licenses + terminated provided such individuals or entities remain in full + compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will + survive any termination of this License. + b. Subject to the above terms and conditions, the license granted here is + perpetual (for the duration of the applicable copyright in the Work). + Notwithstanding the above, Licensor reserves the right to release the + Work under different license terms or to stop distributing the Work at + any time; provided, however that any such election will not serve to + withdraw this License (or any other license that has been, or is + required to be, granted under the terms of this License), and this + License will continue in full force and effect unless terminated as + stated above. + +8. Miscellaneous + + a. Each time You Distribute or Publicly Perform the Work or a Collection, + the Licensor offers to the recipient a license to the Work on the same + terms and conditions as the license granted to You under this License. + b. Each time You Distribute or Publicly Perform an Adaptation, Licensor + offers to the recipient a license to the original Work on the same + terms and conditions as the license granted to You under this License. + c. If any provision of this License is invalid or unenforceable under + applicable law, it shall not affect the validity or enforceability of + the remainder of the terms of this License, and without further action + by the parties to this agreement, such provision shall be reformed to + the minimum extent necessary to make such provision valid and + enforceable. + d. No term or provision of this License shall be deemed waived and no + breach consented to unless such waiver or consent shall be in writing + and signed by the party to be charged with such waiver or consent. + e. This License constitutes the entire agreement between the parties with + respect to the Work licensed here. There are no understandings, + agreements or representations with respect to the Work not specified + here. Licensor shall not be bound by any additional provisions that + may appear in any communication from You. This License may not be + modified without the mutual written agreement of the Licensor and You. + f. The rights granted under, and the subject matter referenced, in this + License were drafted utilizing the terminology of the Berne Convention + for the Protection of Literary and Artistic Works (as amended on + September 28, 1979), the Rome Convention of 1961, the WIPO Copyright + Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 + and the Universal Copyright Convention (as revised on July 24, 1971). + These rights and subject matter take effect in the relevant + jurisdiction in which the License terms are sought to be enforced + according to the corresponding provisions of the implementation of + those treaty provisions in the applicable national law. If the + standard suite of rights granted under applicable copyright law + includes additional rights not granted under this License, such + additional rights are deemed to be included in the License; this + License is not intended to restrict the license of any rights under + applicable law. + + +Creative Commons Notice + + Creative Commons is not a party to this License, and makes no warranty + whatsoever in connection with the Work. Creative Commons will not be + liable to You or any party on any legal theory for any damages + whatsoever, including without limitation any general, special, + incidental or consequential damages arising in connection to this + license. Notwithstanding the foregoing two (2) sentences, if Creative + Commons has expressly identified itself as the Licensor hereunder, it + shall have all rights and obligations of Licensor. + + Except for the limited purpose of indicating to the public that the + Work is licensed under the CCPL, Creative Commons does not authorize + the use by either party of the trademark "Creative Commons" or any + related trademark or logo of Creative Commons without the prior + written consent of Creative Commons. Any permitted use will be in + compliance with Creative Commons' then-current trademark usage + guidelines, as may be published on its website or otherwise made + available upon request from time to time. For the avoidance of doubt, + this trademark restriction does not form part of this License. + + Creative Commons may be contacted at http://creativecommons.org/. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/google/go-querystring + +Copyright (c) 2013 Google. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/google/gofuzz @@ -1713,6 +2261,39 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/googleapis/gax-go + +Copyright 2016, Google Inc. +All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/googleapis/gnostic @@ -2364,7 +2945,8 @@ SOFTWARE. =========================================================== -Import: github.com/knative/eventing/vendor/github.com/knative/pkg +Import: github.com/knative/eventing/vendor/github.com/knative/build + Apache License Version 2.0, January 2004 @@ -2571,7 +3153,7 @@ Import: github.com/knative/eventing/vendor/github.com/knative/pkg =========================================================== -Import: github.com/knative/eventing/vendor/github.com/mattbaird/jsonpatch +Import: github.com/knative/eventing/vendor/github.com/knative/pkg Apache License Version 2.0, January 2004 @@ -2753,7 +3335,7 @@ Import: github.com/knative/eventing/vendor/github.com/mattbaird/jsonpatch APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -2761,7 +3343,7 @@ Import: github.com/knative/eventing/vendor/github.com/mattbaird/jsonpatch same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {yyyy} {name of copyright owner} + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -2777,9 +3359,9 @@ Import: github.com/knative/eventing/vendor/github.com/mattbaird/jsonpatch - =========================================================== -Import: github.com/knative/eventing/vendor/github.com/matttproud/golang_protobuf_extensions +Import: github.com/knative/eventing/vendor/github.com/knative/serving + Apache License Version 2.0, January 2004 @@ -2961,7 +3543,7 @@ Import: github.com/knative/eventing/vendor/github.com/matttproud/golang_protobuf APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -2969,7 +3551,7 @@ Import: github.com/knative/eventing/vendor/github.com/matttproud/golang_protobuf same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {yyyy} {name of copyright owner} + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -2986,7 +3568,7 @@ Import: github.com/knative/eventing/vendor/github.com/matttproud/golang_protobuf =========================================================== -Import: github.com/knative/eventing/vendor/github.com/modern-go/concurrent +Import: github.com/knative/eventing/vendor/github.com/mattbaird/jsonpatch Apache License Version 2.0, January 2004 @@ -3168,7 +3750,7 @@ Import: github.com/knative/eventing/vendor/github.com/modern-go/concurrent APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" + boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -3176,7 +3758,7 @@ Import: github.com/knative/eventing/vendor/github.com/modern-go/concurrent same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -3192,8 +3774,9 @@ Import: github.com/knative/eventing/vendor/github.com/modern-go/concurrent + =========================================================== -Import: github.com/knative/eventing/vendor/github.com/modern-go/reflect2 +Import: github.com/knative/eventing/vendor/github.com/matttproud/golang_protobuf_extensions Apache License Version 2.0, January 2004 @@ -3375,7 +3958,7 @@ Import: github.com/knative/eventing/vendor/github.com/modern-go/reflect2 APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" + boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -3383,7 +3966,7 @@ Import: github.com/knative/eventing/vendor/github.com/modern-go/reflect2 same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -3400,65 +3983,7 @@ Import: github.com/knative/eventing/vendor/github.com/modern-go/reflect2 =========================================================== -Import: github.com/knative/eventing/vendor/github.com/pborman/uuid - -Copyright (c) 2009,2014 Google Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/peterbourgon/diskv - -Copyright (c) 2011-2012 Peter Bourgon - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/prometheus/client_golang +Import: github.com/knative/eventing/vendor/github.com/modern-go/concurrent Apache License Version 2.0, January 2004 @@ -3665,7 +4190,7 @@ Import: github.com/knative/eventing/vendor/github.com/prometheus/client_golang =========================================================== -Import: github.com/knative/eventing/vendor/github.com/prometheus/client_model +Import: github.com/knative/eventing/vendor/github.com/modern-go/reflect2 Apache License Version 2.0, January 2004 @@ -3872,7 +4397,99 @@ Import: github.com/knative/eventing/vendor/github.com/prometheus/client_model =========================================================== -Import: github.com/knative/eventing/vendor/github.com/prometheus/common +Import: github.com/knative/eventing/vendor/github.com/pborman/uuid + +Copyright (c) 2009,2014 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/peterbourgon/diskv + +Copyright (c) 2011-2012 Peter Bourgon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/pierrec/lz4 + +Copyright (c) 2015, Pierre Curto +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of xxHash nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/prometheus/client_golang Apache License Version 2.0, January 2004 @@ -4079,7 +4696,7 @@ Import: github.com/knative/eventing/vendor/github.com/prometheus/common =========================================================== -Import: github.com/knative/eventing/vendor/github.com/prometheus/procfs +Import: github.com/knative/eventing/vendor/github.com/prometheus/client_model Apache License Version 2.0, January 2004 @@ -4286,283 +4903,459 @@ Import: github.com/knative/eventing/vendor/github.com/prometheus/procfs =========================================================== -Import: github.com/knative/eventing/vendor/github.com/spf13/pflag - -Copyright (c) 2012 Alex Ogier. All rights reserved. -Copyright (c) 2012 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +Import: github.com/knative/eventing/vendor/github.com/prometheus/common - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + 1. Definitions. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. -=========================================================== -Import: github.com/knative/eventing/vendor/go.uber.org/atomic + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. -Copyright (c) 2016 Uber Technologies, Inc. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. -=========================================================== -Import: github.com/knative/eventing/vendor/go.uber.org/multierr - -Copyright (c) 2017 Uber Technologies, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. -=========================================================== -Import: github.com/knative/eventing/vendor/go.uber.org/zap + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. -Copyright (c) 2016-2017 Uber Technologies, Inc. + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. -=========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/crypto + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. -Copyright (c) 2009 The Go Authors. All rights reserved. + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + END OF TERMS AND CONDITIONS + APPENDIX: How to apply the Apache License to your work. -=========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/net + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. -Copyright (c) 2009 The Go Authors. All rights reserved. + Copyright [yyyy] [name of copyright owner] -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: + 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 - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + http://www.apache.org/licenses/LICENSE-2.0 -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + 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. =========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/oauth2 +Import: github.com/knative/eventing/vendor/github.com/prometheus/procfs -Copyright (c) 2009 The Go Authors. All rights reserved. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + 1. Definitions. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. -=========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/sync + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. -Copyright (c) 2009 The Go Authors. All rights reserved. + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. =========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/sys +Import: github.com/knative/eventing/vendor/github.com/rcrowley/go-metrics -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright 2012 Richard Crowley. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + 2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +THIS SOFTWARE IS PROVIDED BY RICHARD CROWLEY ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL RICHARD CROWLEY OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation +are those of the authors and should not be interpreted as representing +official policies, either expressed or implied, of Richard Crowley. =========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/text +Import: github.com/knative/eventing/vendor/github.com/spf13/pflag -Copyright (c) 2009 The Go Authors. All rights reserved. +Copyright (c) 2012 Alex Ogier. All rights reserved. +Copyright (c) 2012 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -4593,35 +5386,992 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/time +Import: github.com/knative/eventing/vendor/go.opencensus.io -Copyright (c) 2009 The Go Authors. All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + +=========================================================== +Import: github.com/knative/eventing/vendor/go.uber.org/atomic + +Copyright (c) 2016 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/go.uber.org/multierr + +Copyright (c) 2017 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/go.uber.org/zap + +Copyright (c) 2016-2017 Uber Technologies, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/crypto + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/net + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/oauth2 + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/sync + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/sys + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/text + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/time + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/google.golang.org/api + +Copyright (c) 2011 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/google.golang.org/genproto + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/google.golang.org/grpc + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/gopkg.in/go-playground/webhooks.v3 + +The MIT License (MIT) + +Copyright (c) 2015 Dean Karn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From a2f94178eadf716f0415e3ef8349a7d4c73b7fdc Mon Sep 17 00:00:00 2001 From: Scott Nichols <32305648+n3wscott@users.noreply.github.com> Date: Tue, 30 Oct 2018 12:40:34 -0700 Subject: [PATCH 10/54] update deps. (#566) --- Gopkg.lock | 40 +- Gopkg.toml | 5 +- third_party/VENDOR-LICENSE | 87 ++ vendor/github.com/evanphx/json-patch/LICENSE | 25 + vendor/github.com/evanphx/json-patch/merge.go | 383 ++++++++ vendor/github.com/evanphx/json-patch/patch.go | 682 +++++++++++++ vendor/github.com/gobuffalo/envy/LICENSE.txt | 8 + vendor/github.com/gobuffalo/envy/envy.go | 249 +++++ vendor/github.com/gobuffalo/envy/version.go | 3 + vendor/github.com/joho/godotenv/LICENCE | 23 + vendor/github.com/joho/godotenv/godotenv.go | 346 +++++++ .../github.com/knative/pkg/apis/duck/patch.go | 22 +- .../apis/duck/v1alpha1/channelable_types.go | 10 +- .../knative/pkg/apis/field_error.go | 147 ++- .../knative/pkg/apis/zz_generated.deepcopy.go | 28 + .../knative/pkg/configmap/filter.go | 44 + .../github.com/knative/pkg/configmap/store.go | 174 ++++ .../github.com/knative/pkg/test/e2e_flags.go | 6 +- .../knative/pkg/test/kube_checks.go | 8 +- .../knative/pkg/test/logging/logging.go | 21 +- .../knative/pkg/test/spoof/spoof.go | 17 +- .../github.com/knative/pkg/webhook/certs.go | 7 +- .../github.com/knative/pkg/webhook/webhook.go | 63 +- .../knative/test-infra/scripts/README.md | 196 +++- .../knative/test-infra/scripts/e2e-tests.sh | 57 +- .../knative/test-infra/scripts/library.sh | 65 +- .../test-infra/scripts/presubmit-tests.sh | 54 +- .../knative/test-infra/scripts/release.sh | 105 ++- vendor/github.com/markbates/inflect/LICENCE | 7 + .../github.com/markbates/inflect/helpers.go | 19 + .../github.com/markbates/inflect/inflect.go | 892 ++++++++++++++++++ vendor/github.com/markbates/inflect/name.go | 163 ++++ .../github.com/markbates/inflect/version.go | 3 + 33 files changed, 3773 insertions(+), 186 deletions(-) create mode 100644 vendor/github.com/evanphx/json-patch/LICENSE create mode 100644 vendor/github.com/evanphx/json-patch/merge.go create mode 100644 vendor/github.com/evanphx/json-patch/patch.go create mode 100644 vendor/github.com/gobuffalo/envy/LICENSE.txt create mode 100644 vendor/github.com/gobuffalo/envy/envy.go create mode 100644 vendor/github.com/gobuffalo/envy/version.go create mode 100644 vendor/github.com/joho/godotenv/LICENCE create mode 100644 vendor/github.com/joho/godotenv/godotenv.go create mode 100644 vendor/github.com/knative/pkg/configmap/filter.go create mode 100644 vendor/github.com/knative/pkg/configmap/store.go create mode 100644 vendor/github.com/markbates/inflect/LICENCE create mode 100644 vendor/github.com/markbates/inflect/helpers.go create mode 100644 vendor/github.com/markbates/inflect/inflect.go create mode 100644 vendor/github.com/markbates/inflect/name.go create mode 100644 vendor/github.com/markbates/inflect/version.go diff --git a/Gopkg.lock b/Gopkg.lock index b4e0ecd4bbb..c63559599f7 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -71,6 +71,14 @@ revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98" version = "v1.1.0" +[[projects]] + digest = "1:32598368f409bbee79deb9d43569fcd92b9fb27f39155f5e166b3371217f051f" + name = "github.com/evanphx/json-patch" + packages = ["."] + pruneopts = "NUT" + revision = "72bf35d0ff611848c1dc9df0f976c81192392fa5" + version = "v4.1.0" + [[projects]] digest = "1:1b91ae0dc69a41d4c2ed23ea5cffb721ea63f5037ca4b81e6d6771fbb8f45129" name = "github.com/fsnotify/fsnotify" @@ -103,6 +111,14 @@ pruneopts = "NUT" revision = "7536572e8d55209135cd5e7ccf7fce43dca217ab" +[[projects]] + digest = "1:e885f703ddf302286f6bae494d0602010c6ac8e724503f3306e18f3350802d32" + name = "github.com/gobuffalo/envy" + packages = ["."] + pruneopts = "NUT" + revision = "e0b181c424e7ca1a36f6f2db4bdfd25ef3421e77" + version = "v1.6.7" + [[projects]] digest = "1:1b3dd24f14a5280710fc7a3aa2480b6e4d20fdfc905841de9a3aa2aa2f1d4ee9" name = "github.com/gogo/protobuf" @@ -258,6 +274,14 @@ revision = "9f23e2d6bd2a77f959b2bf6acdbefd708a83a4a4" version = "v0.3.6" +[[projects]] + digest = "1:da62aa6632d04e080b8a8b85a59ed9ed1550842a0099a55f3ae3a20d02a3745a" + name = "github.com/joho/godotenv" + packages = ["."] + pruneopts = "NUT" + revision = "23d116af351c84513e1946b527c88823e476be13" + version = "v1.3.0" + [[projects]] digest = "1:0243cffa4a3410f161ee613dfdd903a636d07e838a42d341da95d81f42cd1d41" name = "github.com/json-iterator/go" @@ -276,7 +300,7 @@ revision = "5c1d8c8469d1ed34b2aecf4c2305b3a57fff2ee3" [[projects]] - digest = "1:aac2721aace98ec22ca32041a292bdb42edec971684e831e3e24c4f5a8d4d893" + digest = "1:840124cac74eca13ec295942bf92693076fba45809f2f46dcb4e83cc92555fae" name = "github.com/knative/pkg" packages = [ "apis", @@ -309,7 +333,7 @@ "webhook", ] pruneopts = "NUT" - revision = "ba1a828cd571f605684f9a4506e85bc13cb65b8d" + revision = "d247efe41d5855f43133f9894f95ce8f9caa195f" [[projects]] digest = "1:63f3974f3afe3dc5b6a115c0d53b0897cd01be6880c4bf5d014fc69a95db6ed1" @@ -330,11 +354,19 @@ [[projects]] branch = "master" - digest = "1:00f434f614520a4b48ef3f6d3ed5c7a1a4f89be1bed98c250711413f6da2eb51" + digest = "1:47acbf79b15512c1c70d952c4c094c91c106ed1d48341e0345b900e7bcecf973" name = "github.com/knative/test-infra" packages = ["."] pruneopts = "T" - revision = "c21d3a832727d5d4334e734c0878eac3a5a07ae7" + revision = "8d72ddd96801f397298519adf1138fee8de1d51e" + +[[projects]] + digest = "1:56dbf15e091bf7926cb33a57cb6bdfc658fc6d3498d2f76f10a97ce7856f1fde" + name = "github.com/markbates/inflect" + packages = ["."] + pruneopts = "NUT" + revision = "24b83195037b3bc61fcda2d28b7b0518bce293b6" + version = "v1.0.4" [[projects]] branch = "master" diff --git a/Gopkg.toml b/Gopkg.toml index acc13ba9695..a03659e1eb1 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -31,6 +31,7 @@ required = [ [[override]] name = "github.com/spf13/cobra" revision = "615425954c3b0d9485a7027d4d451fdcdfdee84e" + [[override]] name = "github.com/spf13/pflag" revision = "583c0c0531f06d5278b7d917446061adc344b5cd" @@ -75,8 +76,8 @@ required = [ [[override]] name = "github.com/knative/pkg" - # HEAD as of 2018-10-09 - revision = "ba1a828cd571f605684f9a4506e85bc13cb65b8d" + # HEAD as of 2018-10-30 + revision = "d247efe41d5855f43133f9894f95ce8f9caa195f" [[constraint]] name = "github.com/knative/serving" diff --git a/third_party/VENDOR-LICENSE b/third_party/VENDOR-LICENSE index 49d95f12c06..95fbb4d3739 100644 --- a/third_party/VENDOR-LICENSE +++ b/third_party/VENDOR-LICENSE @@ -389,6 +389,37 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/evanphx/json-patch + +Copyright (c) 2014, Evan Phoenix +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of the Evan Phoenix nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/fsnotify/fsnotify @@ -893,6 +924,20 @@ Import: github.com/knative/eventing/vendor/github.com/go-logr/zapr +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/gobuffalo/envy + +The MIT License (MIT) +Copyright (c) 2018 Mark Bates + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/gogo/protobuf @@ -2917,6 +2962,35 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/joho/godotenv + +Copyright (c) 2013 John Barton + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/json-iterator/go @@ -3567,6 +3641,19 @@ Import: github.com/knative/eventing/vendor/github.com/knative/serving +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/markbates/inflect + +Copyright (c) 2011 Chris Farmiloe + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/mattbaird/jsonpatch diff --git a/vendor/github.com/evanphx/json-patch/LICENSE b/vendor/github.com/evanphx/json-patch/LICENSE new file mode 100644 index 00000000000..0eb9b72d84d --- /dev/null +++ b/vendor/github.com/evanphx/json-patch/LICENSE @@ -0,0 +1,25 @@ +Copyright (c) 2014, Evan Phoenix +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +* Redistributions in binary form must reproduce the above copyright notice + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. +* Neither the name of the Evan Phoenix nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/evanphx/json-patch/merge.go b/vendor/github.com/evanphx/json-patch/merge.go new file mode 100644 index 00000000000..6806c4c200b --- /dev/null +++ b/vendor/github.com/evanphx/json-patch/merge.go @@ -0,0 +1,383 @@ +package jsonpatch + +import ( + "bytes" + "encoding/json" + "fmt" + "reflect" +) + +func merge(cur, patch *lazyNode, mergeMerge bool) *lazyNode { + curDoc, err := cur.intoDoc() + + if err != nil { + pruneNulls(patch) + return patch + } + + patchDoc, err := patch.intoDoc() + + if err != nil { + return patch + } + + mergeDocs(curDoc, patchDoc, mergeMerge) + + return cur +} + +func mergeDocs(doc, patch *partialDoc, mergeMerge bool) { + for k, v := range *patch { + if v == nil { + if mergeMerge { + (*doc)[k] = nil + } else { + delete(*doc, k) + } + } else { + cur, ok := (*doc)[k] + + if !ok || cur == nil { + pruneNulls(v) + (*doc)[k] = v + } else { + (*doc)[k] = merge(cur, v, mergeMerge) + } + } + } +} + +func pruneNulls(n *lazyNode) { + sub, err := n.intoDoc() + + if err == nil { + pruneDocNulls(sub) + } else { + ary, err := n.intoAry() + + if err == nil { + pruneAryNulls(ary) + } + } +} + +func pruneDocNulls(doc *partialDoc) *partialDoc { + for k, v := range *doc { + if v == nil { + delete(*doc, k) + } else { + pruneNulls(v) + } + } + + return doc +} + +func pruneAryNulls(ary *partialArray) *partialArray { + newAry := []*lazyNode{} + + for _, v := range *ary { + if v != nil { + pruneNulls(v) + newAry = append(newAry, v) + } + } + + *ary = newAry + + return ary +} + +var errBadJSONDoc = fmt.Errorf("Invalid JSON Document") +var errBadJSONPatch = fmt.Errorf("Invalid JSON Patch") +var errBadMergeTypes = fmt.Errorf("Mismatched JSON Documents") + +// MergeMergePatches merges two merge patches together, such that +// applying this resulting merged merge patch to a document yields the same +// as merging each merge patch to the document in succession. +func MergeMergePatches(patch1Data, patch2Data []byte) ([]byte, error) { + return doMergePatch(patch1Data, patch2Data, true) +} + +// MergePatch merges the patchData into the docData. +func MergePatch(docData, patchData []byte) ([]byte, error) { + return doMergePatch(docData, patchData, false) +} + +func doMergePatch(docData, patchData []byte, mergeMerge bool) ([]byte, error) { + doc := &partialDoc{} + + docErr := json.Unmarshal(docData, doc) + + patch := &partialDoc{} + + patchErr := json.Unmarshal(patchData, patch) + + if _, ok := docErr.(*json.SyntaxError); ok { + return nil, errBadJSONDoc + } + + if _, ok := patchErr.(*json.SyntaxError); ok { + return nil, errBadJSONPatch + } + + if docErr == nil && *doc == nil { + return nil, errBadJSONDoc + } + + if patchErr == nil && *patch == nil { + return nil, errBadJSONPatch + } + + if docErr != nil || patchErr != nil { + // Not an error, just not a doc, so we turn straight into the patch + if patchErr == nil { + if mergeMerge { + doc = patch + } else { + doc = pruneDocNulls(patch) + } + } else { + patchAry := &partialArray{} + patchErr = json.Unmarshal(patchData, patchAry) + + if patchErr != nil { + return nil, errBadJSONPatch + } + + pruneAryNulls(patchAry) + + out, patchErr := json.Marshal(patchAry) + + if patchErr != nil { + return nil, errBadJSONPatch + } + + return out, nil + } + } else { + mergeDocs(doc, patch, mergeMerge) + } + + return json.Marshal(doc) +} + +// resemblesJSONArray indicates whether the byte-slice "appears" to be +// a JSON array or not. +// False-positives are possible, as this function does not check the internal +// structure of the array. It only checks that the outer syntax is present and +// correct. +func resemblesJSONArray(input []byte) bool { + input = bytes.TrimSpace(input) + + hasPrefix := bytes.HasPrefix(input, []byte("[")) + hasSuffix := bytes.HasSuffix(input, []byte("]")) + + return hasPrefix && hasSuffix +} + +// CreateMergePatch will return a merge patch document capable of converting +// the original document(s) to the modified document(s). +// The parameters can be bytes of either two JSON Documents, or two arrays of +// JSON documents. +// The merge patch returned follows the specification defined at http://tools.ietf.org/html/draft-ietf-appsawg-json-merge-patch-07 +func CreateMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { + originalResemblesArray := resemblesJSONArray(originalJSON) + modifiedResemblesArray := resemblesJSONArray(modifiedJSON) + + // Do both byte-slices seem like JSON arrays? + if originalResemblesArray && modifiedResemblesArray { + return createArrayMergePatch(originalJSON, modifiedJSON) + } + + // Are both byte-slices are not arrays? Then they are likely JSON objects... + if !originalResemblesArray && !modifiedResemblesArray { + return createObjectMergePatch(originalJSON, modifiedJSON) + } + + // None of the above? Then return an error because of mismatched types. + return nil, errBadMergeTypes +} + +// createObjectMergePatch will return a merge-patch document capable of +// converting the original document to the modified document. +func createObjectMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { + originalDoc := map[string]interface{}{} + modifiedDoc := map[string]interface{}{} + + err := json.Unmarshal(originalJSON, &originalDoc) + if err != nil { + return nil, errBadJSONDoc + } + + err = json.Unmarshal(modifiedJSON, &modifiedDoc) + if err != nil { + return nil, errBadJSONDoc + } + + dest, err := getDiff(originalDoc, modifiedDoc) + if err != nil { + return nil, err + } + + return json.Marshal(dest) +} + +// createArrayMergePatch will return an array of merge-patch documents capable +// of converting the original document to the modified document for each +// pair of JSON documents provided in the arrays. +// Arrays of mismatched sizes will result in an error. +func createArrayMergePatch(originalJSON, modifiedJSON []byte) ([]byte, error) { + originalDocs := []json.RawMessage{} + modifiedDocs := []json.RawMessage{} + + err := json.Unmarshal(originalJSON, &originalDocs) + if err != nil { + return nil, errBadJSONDoc + } + + err = json.Unmarshal(modifiedJSON, &modifiedDocs) + if err != nil { + return nil, errBadJSONDoc + } + + total := len(originalDocs) + if len(modifiedDocs) != total { + return nil, errBadJSONDoc + } + + result := []json.RawMessage{} + for i := 0; i < len(originalDocs); i++ { + original := originalDocs[i] + modified := modifiedDocs[i] + + patch, err := createObjectMergePatch(original, modified) + if err != nil { + return nil, err + } + + result = append(result, json.RawMessage(patch)) + } + + return json.Marshal(result) +} + +// Returns true if the array matches (must be json types). +// As is idiomatic for go, an empty array is not the same as a nil array. +func matchesArray(a, b []interface{}) bool { + if len(a) != len(b) { + return false + } + if (a == nil && b != nil) || (a != nil && b == nil) { + return false + } + for i := range a { + if !matchesValue(a[i], b[i]) { + return false + } + } + return true +} + +// Returns true if the values matches (must be json types) +// The types of the values must match, otherwise it will always return false +// If two map[string]interface{} are given, all elements must match. +func matchesValue(av, bv interface{}) bool { + if reflect.TypeOf(av) != reflect.TypeOf(bv) { + return false + } + switch at := av.(type) { + case string: + bt := bv.(string) + if bt == at { + return true + } + case float64: + bt := bv.(float64) + if bt == at { + return true + } + case bool: + bt := bv.(bool) + if bt == at { + return true + } + case nil: + // Both nil, fine. + return true + case map[string]interface{}: + bt := bv.(map[string]interface{}) + for key := range at { + if !matchesValue(at[key], bt[key]) { + return false + } + } + for key := range bt { + if !matchesValue(at[key], bt[key]) { + return false + } + } + return true + case []interface{}: + bt := bv.([]interface{}) + return matchesArray(at, bt) + } + return false +} + +// getDiff returns the (recursive) difference between a and b as a map[string]interface{}. +func getDiff(a, b map[string]interface{}) (map[string]interface{}, error) { + into := map[string]interface{}{} + for key, bv := range b { + av, ok := a[key] + // value was added + if !ok { + into[key] = bv + continue + } + // If types have changed, replace completely + if reflect.TypeOf(av) != reflect.TypeOf(bv) { + into[key] = bv + continue + } + // Types are the same, compare values + switch at := av.(type) { + case map[string]interface{}: + bt := bv.(map[string]interface{}) + dst := make(map[string]interface{}, len(bt)) + dst, err := getDiff(at, bt) + if err != nil { + return nil, err + } + if len(dst) > 0 { + into[key] = dst + } + case string, float64, bool: + if !matchesValue(av, bv) { + into[key] = bv + } + case []interface{}: + bt := bv.([]interface{}) + if !matchesArray(at, bt) { + into[key] = bv + } + case nil: + switch bv.(type) { + case nil: + // Both nil, fine. + default: + into[key] = bv + } + default: + panic(fmt.Sprintf("Unknown type:%T in key %s", av, key)) + } + } + // Now add all deleted values as nil + for key := range a { + _, found := b[key] + if !found { + into[key] = nil + } + } + return into, nil +} diff --git a/vendor/github.com/evanphx/json-patch/patch.go b/vendor/github.com/evanphx/json-patch/patch.go new file mode 100644 index 00000000000..f26b6824b6d --- /dev/null +++ b/vendor/github.com/evanphx/json-patch/patch.go @@ -0,0 +1,682 @@ +package jsonpatch + +import ( + "bytes" + "encoding/json" + "fmt" + "strconv" + "strings" +) + +const ( + eRaw = iota + eDoc + eAry +) + +var SupportNegativeIndices bool = true + +type lazyNode struct { + raw *json.RawMessage + doc partialDoc + ary partialArray + which int +} + +type operation map[string]*json.RawMessage + +// Patch is an ordered collection of operations. +type Patch []operation + +type partialDoc map[string]*lazyNode +type partialArray []*lazyNode + +type container interface { + get(key string) (*lazyNode, error) + set(key string, val *lazyNode) error + add(key string, val *lazyNode) error + remove(key string) error +} + +func newLazyNode(raw *json.RawMessage) *lazyNode { + return &lazyNode{raw: raw, doc: nil, ary: nil, which: eRaw} +} + +func (n *lazyNode) MarshalJSON() ([]byte, error) { + switch n.which { + case eRaw: + return json.Marshal(n.raw) + case eDoc: + return json.Marshal(n.doc) + case eAry: + return json.Marshal(n.ary) + default: + return nil, fmt.Errorf("Unknown type") + } +} + +func (n *lazyNode) UnmarshalJSON(data []byte) error { + dest := make(json.RawMessage, len(data)) + copy(dest, data) + n.raw = &dest + n.which = eRaw + return nil +} + +func (n *lazyNode) intoDoc() (*partialDoc, error) { + if n.which == eDoc { + return &n.doc, nil + } + + if n.raw == nil { + return nil, fmt.Errorf("Unable to unmarshal nil pointer as partial document") + } + + err := json.Unmarshal(*n.raw, &n.doc) + + if err != nil { + return nil, err + } + + n.which = eDoc + return &n.doc, nil +} + +func (n *lazyNode) intoAry() (*partialArray, error) { + if n.which == eAry { + return &n.ary, nil + } + + if n.raw == nil { + return nil, fmt.Errorf("Unable to unmarshal nil pointer as partial array") + } + + err := json.Unmarshal(*n.raw, &n.ary) + + if err != nil { + return nil, err + } + + n.which = eAry + return &n.ary, nil +} + +func (n *lazyNode) compact() []byte { + buf := &bytes.Buffer{} + + if n.raw == nil { + return nil + } + + err := json.Compact(buf, *n.raw) + + if err != nil { + return *n.raw + } + + return buf.Bytes() +} + +func (n *lazyNode) tryDoc() bool { + if n.raw == nil { + return false + } + + err := json.Unmarshal(*n.raw, &n.doc) + + if err != nil { + return false + } + + n.which = eDoc + return true +} + +func (n *lazyNode) tryAry() bool { + if n.raw == nil { + return false + } + + err := json.Unmarshal(*n.raw, &n.ary) + + if err != nil { + return false + } + + n.which = eAry + return true +} + +func (n *lazyNode) equal(o *lazyNode) bool { + if n.which == eRaw { + if !n.tryDoc() && !n.tryAry() { + if o.which != eRaw { + return false + } + + return bytes.Equal(n.compact(), o.compact()) + } + } + + if n.which == eDoc { + if o.which == eRaw { + if !o.tryDoc() { + return false + } + } + + if o.which != eDoc { + return false + } + + for k, v := range n.doc { + ov, ok := o.doc[k] + + if !ok { + return false + } + + if v == nil && ov == nil { + continue + } + + if !v.equal(ov) { + return false + } + } + + return true + } + + if o.which != eAry && !o.tryAry() { + return false + } + + if len(n.ary) != len(o.ary) { + return false + } + + for idx, val := range n.ary { + if !val.equal(o.ary[idx]) { + return false + } + } + + return true +} + +func (o operation) kind() string { + if obj, ok := o["op"]; ok && obj != nil { + var op string + + err := json.Unmarshal(*obj, &op) + + if err != nil { + return "unknown" + } + + return op + } + + return "unknown" +} + +func (o operation) path() string { + if obj, ok := o["path"]; ok && obj != nil { + var op string + + err := json.Unmarshal(*obj, &op) + + if err != nil { + return "unknown" + } + + return op + } + + return "unknown" +} + +func (o operation) from() string { + if obj, ok := o["from"]; ok && obj != nil { + var op string + + err := json.Unmarshal(*obj, &op) + + if err != nil { + return "unknown" + } + + return op + } + + return "unknown" +} + +func (o operation) value() *lazyNode { + if obj, ok := o["value"]; ok { + return newLazyNode(obj) + } + + return nil +} + +func isArray(buf []byte) bool { +Loop: + for _, c := range buf { + switch c { + case ' ': + case '\n': + case '\t': + continue + case '[': + return true + default: + break Loop + } + } + + return false +} + +func findObject(pd *container, path string) (container, string) { + doc := *pd + + split := strings.Split(path, "/") + + if len(split) < 2 { + return nil, "" + } + + parts := split[1 : len(split)-1] + + key := split[len(split)-1] + + var err error + + for _, part := range parts { + + next, ok := doc.get(decodePatchKey(part)) + + if next == nil || ok != nil { + return nil, "" + } + + if isArray(*next.raw) { + doc, err = next.intoAry() + + if err != nil { + return nil, "" + } + } else { + doc, err = next.intoDoc() + + if err != nil { + return nil, "" + } + } + } + + return doc, decodePatchKey(key) +} + +func (d *partialDoc) set(key string, val *lazyNode) error { + (*d)[key] = val + return nil +} + +func (d *partialDoc) add(key string, val *lazyNode) error { + (*d)[key] = val + return nil +} + +func (d *partialDoc) get(key string) (*lazyNode, error) { + return (*d)[key], nil +} + +func (d *partialDoc) remove(key string) error { + _, ok := (*d)[key] + if !ok { + return fmt.Errorf("Unable to remove nonexistent key: %s", key) + } + + delete(*d, key) + return nil +} + +func (d *partialArray) set(key string, val *lazyNode) error { + if key == "-" { + *d = append(*d, val) + return nil + } + + idx, err := strconv.Atoi(key) + if err != nil { + return err + } + + sz := len(*d) + if idx+1 > sz { + sz = idx + 1 + } + + ary := make([]*lazyNode, sz) + + cur := *d + + copy(ary, cur) + + if idx >= len(ary) { + return fmt.Errorf("Unable to access invalid index: %d", idx) + } + + ary[idx] = val + + *d = ary + return nil +} + +func (d *partialArray) add(key string, val *lazyNode) error { + if key == "-" { + *d = append(*d, val) + return nil + } + + idx, err := strconv.Atoi(key) + if err != nil { + return err + } + + ary := make([]*lazyNode, len(*d)+1) + + cur := *d + + if idx >= len(ary) { + return fmt.Errorf("Unable to access invalid index: %d", idx) + } + + if SupportNegativeIndices { + if idx < -len(ary) { + return fmt.Errorf("Unable to access invalid index: %d", idx) + } + + if idx < 0 { + idx += len(ary) + } + } + + copy(ary[0:idx], cur[0:idx]) + ary[idx] = val + copy(ary[idx+1:], cur[idx:]) + + *d = ary + return nil +} + +func (d *partialArray) get(key string) (*lazyNode, error) { + idx, err := strconv.Atoi(key) + + if err != nil { + return nil, err + } + + if idx >= len(*d) { + return nil, fmt.Errorf("Unable to access invalid index: %d", idx) + } + + return (*d)[idx], nil +} + +func (d *partialArray) remove(key string) error { + idx, err := strconv.Atoi(key) + if err != nil { + return err + } + + cur := *d + + if idx >= len(cur) { + return fmt.Errorf("Unable to access invalid index: %d", idx) + } + + if SupportNegativeIndices { + if idx < -len(cur) { + return fmt.Errorf("Unable to access invalid index: %d", idx) + } + + if idx < 0 { + idx += len(cur) + } + } + + ary := make([]*lazyNode, len(cur)-1) + + copy(ary[0:idx], cur[0:idx]) + copy(ary[idx:], cur[idx+1:]) + + *d = ary + return nil + +} + +func (p Patch) add(doc *container, op operation) error { + path := op.path() + + con, key := findObject(doc, path) + + if con == nil { + return fmt.Errorf("jsonpatch add operation does not apply: doc is missing path: \"%s\"", path) + } + + return con.add(key, op.value()) +} + +func (p Patch) remove(doc *container, op operation) error { + path := op.path() + + con, key := findObject(doc, path) + + if con == nil { + return fmt.Errorf("jsonpatch remove operation does not apply: doc is missing path: \"%s\"", path) + } + + return con.remove(key) +} + +func (p Patch) replace(doc *container, op operation) error { + path := op.path() + + con, key := findObject(doc, path) + + if con == nil { + return fmt.Errorf("jsonpatch replace operation does not apply: doc is missing path: %s", path) + } + + _, ok := con.get(key) + if ok != nil { + return fmt.Errorf("jsonpatch replace operation does not apply: doc is missing key: %s", path) + } + + return con.set(key, op.value()) +} + +func (p Patch) move(doc *container, op operation) error { + from := op.from() + + con, key := findObject(doc, from) + + if con == nil { + return fmt.Errorf("jsonpatch move operation does not apply: doc is missing from path: %s", from) + } + + val, err := con.get(key) + if err != nil { + return err + } + + err = con.remove(key) + if err != nil { + return err + } + + path := op.path() + + con, key = findObject(doc, path) + + if con == nil { + return fmt.Errorf("jsonpatch move operation does not apply: doc is missing destination path: %s", path) + } + + return con.set(key, val) +} + +func (p Patch) test(doc *container, op operation) error { + path := op.path() + + con, key := findObject(doc, path) + + if con == nil { + return fmt.Errorf("jsonpatch test operation does not apply: is missing path: %s", path) + } + + val, err := con.get(key) + + if err != nil { + return err + } + + if val == nil { + if op.value().raw == nil { + return nil + } + return fmt.Errorf("Testing value %s failed", path) + } else if op.value() == nil { + return fmt.Errorf("Testing value %s failed", path) + } + + if val.equal(op.value()) { + return nil + } + + return fmt.Errorf("Testing value %s failed", path) +} + +func (p Patch) copy(doc *container, op operation) error { + from := op.from() + + con, key := findObject(doc, from) + + if con == nil { + return fmt.Errorf("jsonpatch copy operation does not apply: doc is missing from path: %s", from) + } + + val, err := con.get(key) + if err != nil { + return err + } + + path := op.path() + + con, key = findObject(doc, path) + + if con == nil { + return fmt.Errorf("jsonpatch copy operation does not apply: doc is missing destination path: %s", path) + } + + return con.set(key, val) +} + +// Equal indicates if 2 JSON documents have the same structural equality. +func Equal(a, b []byte) bool { + ra := make(json.RawMessage, len(a)) + copy(ra, a) + la := newLazyNode(&ra) + + rb := make(json.RawMessage, len(b)) + copy(rb, b) + lb := newLazyNode(&rb) + + return la.equal(lb) +} + +// DecodePatch decodes the passed JSON document as an RFC 6902 patch. +func DecodePatch(buf []byte) (Patch, error) { + var p Patch + + err := json.Unmarshal(buf, &p) + + if err != nil { + return nil, err + } + + return p, nil +} + +// Apply mutates a JSON document according to the patch, and returns the new +// document. +func (p Patch) Apply(doc []byte) ([]byte, error) { + return p.ApplyIndent(doc, "") +} + +// ApplyIndent mutates a JSON document according to the patch, and returns the new +// document indented. +func (p Patch) ApplyIndent(doc []byte, indent string) ([]byte, error) { + var pd container + if doc[0] == '[' { + pd = &partialArray{} + } else { + pd = &partialDoc{} + } + + err := json.Unmarshal(doc, pd) + + if err != nil { + return nil, err + } + + err = nil + + for _, op := range p { + switch op.kind() { + case "add": + err = p.add(&pd, op) + case "remove": + err = p.remove(&pd, op) + case "replace": + err = p.replace(&pd, op) + case "move": + err = p.move(&pd, op) + case "test": + err = p.test(&pd, op) + case "copy": + err = p.copy(&pd, op) + default: + err = fmt.Errorf("Unexpected kind: %s", op.kind()) + } + + if err != nil { + return nil, err + } + } + + if indent != "" { + return json.MarshalIndent(pd, "", indent) + } + + return json.Marshal(pd) +} + +// From http://tools.ietf.org/html/rfc6901#section-4 : +// +// Evaluation of each reference token begins by decoding any escaped +// character sequence. This is performed by first transforming any +// occurrence of the sequence '~1' to '/', and then transforming any +// occurrence of the sequence '~0' to '~'. + +var ( + rfc6901Decoder = strings.NewReplacer("~1", "/", "~0", "~") +) + +func decodePatchKey(k string) string { + return rfc6901Decoder.Replace(k) +} diff --git a/vendor/github.com/gobuffalo/envy/LICENSE.txt b/vendor/github.com/gobuffalo/envy/LICENSE.txt new file mode 100644 index 00000000000..123ddc0d804 --- /dev/null +++ b/vendor/github.com/gobuffalo/envy/LICENSE.txt @@ -0,0 +1,8 @@ +The MIT License (MIT) +Copyright (c) 2018 Mark Bates + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/gobuffalo/envy/envy.go b/vendor/github.com/gobuffalo/envy/envy.go new file mode 100644 index 00000000000..84c1e726654 --- /dev/null +++ b/vendor/github.com/gobuffalo/envy/envy.go @@ -0,0 +1,249 @@ +/* +package envy makes working with ENV variables in Go trivial. + +* Get ENV variables with default values. +* Set ENV variables safely without affecting the underlying system. +* Temporarily change ENV vars; useful for testing. +* Map all of the key/values in the ENV. +* Loads .env files (by using [godotenv](https://github.com/joho/godotenv/)) +* More! +*/ +package envy + +import ( + "flag" + "fmt" + "os" + "os/exec" + "path/filepath" + "runtime" + "strconv" + "strings" + "sync" + + "github.com/joho/godotenv" +) + +var gil = &sync.RWMutex{} +var env = map[string]string{} + +// GO111MODULE is ENV for turning mods on/off +const GO111MODULE = "GO111MODULE" + +func init() { + Load() + loadEnv() +} + +// Load the ENV variables to the env map +func loadEnv() { + gil.Lock() + defer gil.Unlock() + // Detect the Go version on the user system, not the one that was used to compile the binary + v := "" + out, err := exec.Command("go", "version").Output() + if err == nil { + // This will break when Go 2 lands + v = strings.Split(string(out), " ")[2][4:] + } else { + v = runtime.Version()[4:] + } + + goRuntimeVersion, _ := strconv.ParseFloat(runtime.Version()[4:], 64) + + goVersion, err := strconv.ParseFloat(v, 64) + if err != nil { + goVersion = goRuntimeVersion + } + + if os.Getenv("GO_ENV") == "" { + // if the flag "test.v" is *defined*, we're running as a unit test. Note that we don't care + // about v.Value (verbose test mode); we just want to know if the test environment has defined + // it. It's also possible that the flags are not yet fully parsed (i.e. flag.Parsed() == false), + // so we could not depend on v.Value anyway. + // + if v := flag.Lookup("test.v"); v != nil { + env["GO_ENV"] = "test" + } + } + + // set the GOPATH if using >= 1.8 and the GOPATH isn't set + if goVersion >= 8 && os.Getenv("GOPATH") == "" { + out, err := exec.Command("go", "env", "GOPATH").Output() + if err == nil { + gp := strings.TrimSpace(string(out)) + os.Setenv("GOPATH", gp) + } + } + + for _, e := range os.Environ() { + pair := strings.Split(e, "=") + env[pair[0]] = os.Getenv(pair[0]) + } +} + +func Mods() bool { + return Get(GO111MODULE, "off") == "on" +} + +// Reload the ENV variables. Useful if +// an external ENV manager has been used +func Reload() { + env = map[string]string{} + loadEnv() +} + +// Load .env files. Files will be loaded in the same order that are received. +// Redefined vars will override previously existing values. +// IE: envy.Load(".env", "test_env/.env") will result in DIR=test_env +// If no arg passed, it will try to load a .env file. +func Load(files ...string) error { + + // If no files received, load the default one + if len(files) == 0 { + err := godotenv.Overload() + if err == nil { + Reload() + } + return err + } + + // We received a list of files + for _, file := range files { + + // Check if it exists or we can access + if _, err := os.Stat(file); err != nil { + // It does not exist or we can not access. + // Return and stop loading + return err + } + + // It exists and we have permission. Load it + if err := godotenv.Overload(file); err != nil { + return err + } + + // Reload the env so all new changes are noticed + Reload() + + } + return nil +} + +// Get a value from the ENV. If it doesn't exist the +// default value will be returned. +func Get(key string, value string) string { + gil.RLock() + defer gil.RUnlock() + if v, ok := env[key]; ok { + return v + } + return value +} + +// Get a value from the ENV. If it doesn't exist +// an error will be returned +func MustGet(key string) (string, error) { + gil.RLock() + defer gil.RUnlock() + if v, ok := env[key]; ok { + return v, nil + } + return "", fmt.Errorf("could not find ENV var with %s", key) +} + +// Set a value into the ENV. This is NOT permanent. It will +// only affect values accessed through envy. +func Set(key string, value string) { + gil.Lock() + defer gil.Unlock() + env[key] = value +} + +// MustSet the value into the underlying ENV, as well as envy. +// This may return an error if there is a problem setting the +// underlying ENV value. +func MustSet(key string, value string) error { + gil.Lock() + defer gil.Unlock() + err := os.Setenv(key, value) + if err != nil { + return err + } + env[key] = value + return nil +} + +// Map all of the keys/values set in envy. +func Map() map[string]string { + gil.RLock() + defer gil.RUnlock() + cp := map[string]string{} + for k, v := range env { + cp[k] = v + } + return env +} + +// Temp makes a copy of the values and allows operation on +// those values temporarily during the run of the function. +// At the end of the function run the copy is discarded and +// the original values are replaced. This is useful for testing. +// Warning: This function is NOT safe to use from a goroutine or +// from code which may access any Get or Set function from a goroutine +func Temp(f func()) { + oenv := env + env = map[string]string{} + for k, v := range oenv { + env[k] = v + } + defer func() { env = oenv }() + f() +} + +func GoPath() string { + return Get("GOPATH", "") +} + +func GoBin() string { + return Get("GO_BIN", "go") +} + +// GoPaths returns all possible GOPATHS that are set. +func GoPaths() []string { + gp := Get("GOPATH", "") + if runtime.GOOS == "windows" { + return strings.Split(gp, ";") // Windows uses a different separator + } + return strings.Split(gp, ":") +} + +func importPath(path string) string { + for _, gopath := range GoPaths() { + srcpath := filepath.Join(gopath, "src") + rel, err := filepath.Rel(srcpath, path) + if err == nil { + return filepath.ToSlash(rel) + } + } + + // fallback to trim + rel := strings.TrimPrefix(path, filepath.Join(GoPath(), "src")) + rel = strings.TrimPrefix(rel, string(filepath.Separator)) + return filepath.ToSlash(rel) +} + +func CurrentPackage() string { + pwd, _ := os.Getwd() + return importPath(pwd) +} + +func Environ() []string { + gil.RLock() + defer gil.RUnlock() + var e []string + for k, v := range env { + e = append(e, fmt.Sprintf("%s=%s", k, v)) + } + return e +} diff --git a/vendor/github.com/gobuffalo/envy/version.go b/vendor/github.com/gobuffalo/envy/version.go new file mode 100644 index 00000000000..f41fdc0746c --- /dev/null +++ b/vendor/github.com/gobuffalo/envy/version.go @@ -0,0 +1,3 @@ +package envy + +const Version = "v1.6.7" diff --git a/vendor/github.com/joho/godotenv/LICENCE b/vendor/github.com/joho/godotenv/LICENCE new file mode 100644 index 00000000000..e7ddd51be90 --- /dev/null +++ b/vendor/github.com/joho/godotenv/LICENCE @@ -0,0 +1,23 @@ +Copyright (c) 2013 John Barton + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + diff --git a/vendor/github.com/joho/godotenv/godotenv.go b/vendor/github.com/joho/godotenv/godotenv.go new file mode 100644 index 00000000000..29b436c77c0 --- /dev/null +++ b/vendor/github.com/joho/godotenv/godotenv.go @@ -0,0 +1,346 @@ +// Package godotenv is a go port of the ruby dotenv library (https://github.com/bkeepers/dotenv) +// +// Examples/readme can be found on the github page at https://github.com/joho/godotenv +// +// The TL;DR is that you make a .env file that looks something like +// +// SOME_ENV_VAR=somevalue +// +// and then in your go code you can call +// +// godotenv.Load() +// +// and all the env vars declared in .env will be available through os.Getenv("SOME_ENV_VAR") +package godotenv + +import ( + "bufio" + "errors" + "fmt" + "io" + "os" + "os/exec" + "regexp" + "sort" + "strings" +) + +const doubleQuoteSpecialChars = "\\\n\r\"!$`" + +// Load will read your env file(s) and load them into ENV for this process. +// +// Call this function as close as possible to the start of your program (ideally in main) +// +// If you call Load without any args it will default to loading .env in the current path +// +// You can otherwise tell it which files to load (there can be more than one) like +// +// godotenv.Load("fileone", "filetwo") +// +// It's important to note that it WILL NOT OVERRIDE an env variable that already exists - consider the .env file to set dev vars or sensible defaults +func Load(filenames ...string) (err error) { + filenames = filenamesOrDefault(filenames) + + for _, filename := range filenames { + err = loadFile(filename, false) + if err != nil { + return // return early on a spazout + } + } + return +} + +// Overload will read your env file(s) and load them into ENV for this process. +// +// Call this function as close as possible to the start of your program (ideally in main) +// +// If you call Overload without any args it will default to loading .env in the current path +// +// You can otherwise tell it which files to load (there can be more than one) like +// +// godotenv.Overload("fileone", "filetwo") +// +// It's important to note this WILL OVERRIDE an env variable that already exists - consider the .env file to forcefilly set all vars. +func Overload(filenames ...string) (err error) { + filenames = filenamesOrDefault(filenames) + + for _, filename := range filenames { + err = loadFile(filename, true) + if err != nil { + return // return early on a spazout + } + } + return +} + +// Read all env (with same file loading semantics as Load) but return values as +// a map rather than automatically writing values into env +func Read(filenames ...string) (envMap map[string]string, err error) { + filenames = filenamesOrDefault(filenames) + envMap = make(map[string]string) + + for _, filename := range filenames { + individualEnvMap, individualErr := readFile(filename) + + if individualErr != nil { + err = individualErr + return // return early on a spazout + } + + for key, value := range individualEnvMap { + envMap[key] = value + } + } + + return +} + +// Parse reads an env file from io.Reader, returning a map of keys and values. +func Parse(r io.Reader) (envMap map[string]string, err error) { + envMap = make(map[string]string) + + var lines []string + scanner := bufio.NewScanner(r) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + + if err = scanner.Err(); err != nil { + return + } + + for _, fullLine := range lines { + if !isIgnoredLine(fullLine) { + var key, value string + key, value, err = parseLine(fullLine, envMap) + + if err != nil { + return + } + envMap[key] = value + } + } + return +} + +//Unmarshal reads an env file from a string, returning a map of keys and values. +func Unmarshal(str string) (envMap map[string]string, err error) { + return Parse(strings.NewReader(str)) +} + +// Exec loads env vars from the specified filenames (empty map falls back to default) +// then executes the cmd specified. +// +// Simply hooks up os.Stdin/err/out to the command and calls Run() +// +// If you want more fine grained control over your command it's recommended +// that you use `Load()` or `Read()` and the `os/exec` package yourself. +func Exec(filenames []string, cmd string, cmdArgs []string) error { + Load(filenames...) + + command := exec.Command(cmd, cmdArgs...) + command.Stdin = os.Stdin + command.Stdout = os.Stdout + command.Stderr = os.Stderr + return command.Run() +} + +// Write serializes the given environment and writes it to a file +func Write(envMap map[string]string, filename string) error { + content, error := Marshal(envMap) + if error != nil { + return error + } + file, error := os.Create(filename) + if error != nil { + return error + } + _, err := file.WriteString(content) + return err +} + +// Marshal outputs the given environment as a dotenv-formatted environment file. +// Each line is in the format: KEY="VALUE" where VALUE is backslash-escaped. +func Marshal(envMap map[string]string) (string, error) { + lines := make([]string, 0, len(envMap)) + for k, v := range envMap { + lines = append(lines, fmt.Sprintf(`%s="%s"`, k, doubleQuoteEscape(v))) + } + sort.Strings(lines) + return strings.Join(lines, "\n"), nil +} + +func filenamesOrDefault(filenames []string) []string { + if len(filenames) == 0 { + return []string{".env"} + } + return filenames +} + +func loadFile(filename string, overload bool) error { + envMap, err := readFile(filename) + if err != nil { + return err + } + + currentEnv := map[string]bool{} + rawEnv := os.Environ() + for _, rawEnvLine := range rawEnv { + key := strings.Split(rawEnvLine, "=")[0] + currentEnv[key] = true + } + + for key, value := range envMap { + if !currentEnv[key] || overload { + os.Setenv(key, value) + } + } + + return nil +} + +func readFile(filename string) (envMap map[string]string, err error) { + file, err := os.Open(filename) + if err != nil { + return + } + defer file.Close() + + return Parse(file) +} + +func parseLine(line string, envMap map[string]string) (key string, value string, err error) { + if len(line) == 0 { + err = errors.New("zero length string") + return + } + + // ditch the comments (but keep quoted hashes) + if strings.Contains(line, "#") { + segmentsBetweenHashes := strings.Split(line, "#") + quotesAreOpen := false + var segmentsToKeep []string + for _, segment := range segmentsBetweenHashes { + if strings.Count(segment, "\"") == 1 || strings.Count(segment, "'") == 1 { + if quotesAreOpen { + quotesAreOpen = false + segmentsToKeep = append(segmentsToKeep, segment) + } else { + quotesAreOpen = true + } + } + + if len(segmentsToKeep) == 0 || quotesAreOpen { + segmentsToKeep = append(segmentsToKeep, segment) + } + } + + line = strings.Join(segmentsToKeep, "#") + } + + firstEquals := strings.Index(line, "=") + firstColon := strings.Index(line, ":") + splitString := strings.SplitN(line, "=", 2) + if firstColon != -1 && (firstColon < firstEquals || firstEquals == -1) { + //this is a yaml-style line + splitString = strings.SplitN(line, ":", 2) + } + + if len(splitString) != 2 { + err = errors.New("Can't separate key from value") + return + } + + // Parse the key + key = splitString[0] + if strings.HasPrefix(key, "export") { + key = strings.TrimPrefix(key, "export") + } + key = strings.Trim(key, " ") + + // Parse the value + value = parseValue(splitString[1], envMap) + return +} + +func parseValue(value string, envMap map[string]string) string { + + // trim + value = strings.Trim(value, " ") + + // check if we've got quoted values or possible escapes + if len(value) > 1 { + rs := regexp.MustCompile(`\A'(.*)'\z`) + singleQuotes := rs.FindStringSubmatch(value) + + rd := regexp.MustCompile(`\A"(.*)"\z`) + doubleQuotes := rd.FindStringSubmatch(value) + + if singleQuotes != nil || doubleQuotes != nil { + // pull the quotes off the edges + value = value[1 : len(value)-1] + } + + if doubleQuotes != nil { + // expand newlines + escapeRegex := regexp.MustCompile(`\\.`) + value = escapeRegex.ReplaceAllStringFunc(value, func(match string) string { + c := strings.TrimPrefix(match, `\`) + switch c { + case "n": + return "\n" + case "r": + return "\r" + default: + return match + } + }) + // unescape characters + e := regexp.MustCompile(`\\([^$])`) + value = e.ReplaceAllString(value, "$1") + } + + if singleQuotes == nil { + value = expandVariables(value, envMap) + } + } + + return value +} + +func expandVariables(v string, m map[string]string) string { + r := regexp.MustCompile(`(\\)?(\$)(\()?\{?([A-Z0-9_]+)?\}?`) + + return r.ReplaceAllStringFunc(v, func(s string) string { + submatch := r.FindStringSubmatch(s) + + if submatch == nil { + return s + } + if submatch[1] == "\\" || submatch[2] == "(" { + return submatch[0][1:] + } else if submatch[4] != "" { + return m[submatch[4]] + } + return s + }) +} + +func isIgnoredLine(line string) bool { + trimmedLine := strings.Trim(line, " \n\t") + return len(trimmedLine) == 0 || strings.HasPrefix(trimmedLine, "#") +} + +func doubleQuoteEscape(line string) string { + for _, c := range doubleQuoteSpecialChars { + toReplace := "\\" + string(c) + if c == '\n' { + toReplace = `\n` + } + if c == '\r' { + toReplace = `\r` + } + line = strings.Replace(line, string(c), toReplace, -1) + } + return line +} diff --git a/vendor/github.com/knative/pkg/apis/duck/patch.go b/vendor/github.com/knative/pkg/apis/duck/patch.go index ed06d7da7c6..386aa1f32eb 100644 --- a/vendor/github.com/knative/pkg/apis/duck/patch.go +++ b/vendor/github.com/knative/pkg/apis/duck/patch.go @@ -19,21 +19,37 @@ package duck import ( "encoding/json" + jsonmergepatch "github.com/evanphx/json-patch" "github.com/mattbaird/jsonpatch" ) -func CreatePatch(before, after interface{}) (JSONPatch, error) { - // Marshal the before and after. +func marshallBeforeAfter(before, after interface{}) ([]byte, []byte, error) { rawBefore, err := json.Marshal(before) if err != nil { - return nil, err + return nil, nil, err } rawAfter, err := json.Marshal(after) + if err != nil { + return rawBefore, nil, err + } + + return rawBefore, rawAfter, nil +} + +func CreateMergePatch(before, after interface{}) ([]byte, error) { + rawBefore, rawAfter, err := marshallBeforeAfter(before, after) if err != nil { return nil, err } + return jsonmergepatch.CreateMergePatch(rawBefore, rawAfter) +} +func CreatePatch(before, after interface{}) (JSONPatch, error) { + rawBefore, rawAfter, err := marshallBeforeAfter(before, after) + if err != nil { + return nil, err + } return jsonpatch.CreatePatch(rawBefore, rawAfter) } diff --git a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/channelable_types.go b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/channelable_types.go index 7e7ffd9ada1..ba60d3b8025 100644 --- a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/channelable_types.go +++ b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/channelable_types.go @@ -33,14 +33,14 @@ type Channelable struct { } // ChannelSubscriberSpec defines a single subscriber to a Channel. -// CallableDomain is the endpoint for the call -// SinkableDomain is the endpoint for the result -// One of them must be present +// CallableURI is the endpoint for the call +// SinkableURI is the endpoint for the result +// At least one of them must be present. type ChannelSubscriberSpec struct { // +optional - CallableDomain string `json:"callableDomain,omitempty"` + CallableURI string `json:"callableURI,omitempty"` // +optional - SinkableDomain string `json:"sinkableDomain,omitempty"` + SinkableURI string `json:"sinkableURI,omitempty"` } diff --git a/vendor/github.com/knative/pkg/apis/field_error.go b/vendor/github.com/knative/pkg/apis/field_error.go index 59cd1215a52..023db789efa 100644 --- a/vendor/github.com/knative/pkg/apis/field_error.go +++ b/vendor/github.com/knative/pkg/apis/field_error.go @@ -30,7 +30,7 @@ const CurrentField = "" // specific fields in a manner suitable for use in a recursive walk, so // that errors contain the appropriate field context. // FieldError methods are non-mutating. -// +k8s:deepcopy-gen=false +// +k8s:deepcopy-gen=true type FieldError struct { Message string Paths []string @@ -54,18 +54,21 @@ func (fe *FieldError) ViaField(prefix ...string) *FieldError { if fe == nil { return nil } - newErr := &FieldError{} - for _, e := range fe.getNormalizedErrors() { - // Prepend the Prefix to existing errors. - newPaths := make([]string, 0, len(e.Paths)) - for _, oldPath := range e.Paths { - newPaths = append(newPaths, flatten(append(prefix, oldPath))) - } - sort.Slice(newPaths, func(i, j int) bool { return newPaths[i] < newPaths[j] }) - e.Paths = newPaths + // Copy over message and details, paths will be updated and errors come + // along using .Also(). + newErr := &FieldError{ + Message: fe.Message, + Details: fe.Details, + } - // Append the mutated error to the errors list. - newErr = newErr.Also(&e) + // Prepend the Prefix to existing errors. + newPaths := make([]string, 0, len(fe.Paths)) + for _, oldPath := range fe.Paths { + newPaths = append(newPaths, flatten(append(prefix, oldPath))) + } + newErr.Paths = newPaths + for _, e := range fe.errors { + newErr = newErr.Also(e.ViaField(prefix...)) } return newErr } @@ -104,21 +107,32 @@ func (fe *FieldError) ViaFieldKey(field string, key string) *FieldError { // Also collects errors, returns a new collection of existing errors and new errors. func (fe *FieldError) Also(errs ...*FieldError) *FieldError { - newErr := &FieldError{} + var newErr *FieldError // collect the current objects errors, if it has any - if fe != nil { - newErr.errors = fe.getNormalizedErrors() + if !fe.isEmpty() { + newErr = fe.DeepCopy() + } else { + newErr = &FieldError{} } // and then collect the passed in errors for _, e := range errs { - newErr.errors = append(newErr.errors, e.getNormalizedErrors()...) + if !e.isEmpty() { + newErr.errors = append(newErr.errors, *e) + } } - if len(newErr.errors) == 0 { + if newErr.isEmpty() { return nil } return newErr } +func (fe *FieldError) isEmpty() bool { + if fe == nil { + return true + } + return fe.Message == "" && fe.Details == "" && len(fe.errors) == 0 && len(fe.Paths) == 0 +} + func (fe *FieldError) getNormalizedErrors() []FieldError { // in case we call getNormalizedErrors on a nil object, return just an empty // list. This can happen when .Error() is called on a nil object. @@ -139,14 +153,34 @@ func (fe *FieldError) getNormalizedErrors() []FieldError { for _, e := range fe.errors { errors = append(errors, e.getNormalizedErrors()...) } - sort.Slice(errors, func(i, j int) bool { return errors[i].Message < errors[j].Message }) return errors } +// Error implements error +func (fe *FieldError) Error() string { + var errs []string + // Get the list of errors as a flat merged list. + normedErrors := merge(fe.getNormalizedErrors()) + for _, e := range normedErrors { + if e.Details == "" { + errs = append(errs, fmt.Sprintf("%v: %v", e.Message, strings.Join(e.Paths, ", "))) + } else { + errs = append(errs, fmt.Sprintf("%v: %v\n%v", e.Message, strings.Join(e.Paths, ", "), e.Details)) + } + } + return strings.Join(errs, "\n") +} + +// Helpers --- + func asIndex(index int) string { return fmt.Sprintf("[%d]", index) } +func isIndex(part string) bool { + return strings.HasPrefix(part, "[") && strings.HasSuffix(part, "]") +} + func asKey(key string) string { return fmt.Sprintf("[%s]", key) } @@ -173,23 +207,80 @@ func flatten(path []string) string { return strings.Join(newPath, ".") } -func isIndex(part string) bool { - return strings.HasPrefix(part, "[") && strings.HasSuffix(part, "]") +// mergePaths takes in two string slices and returns the combination of them +// without any duplicate entries. +func mergePaths(a, b []string) []string { + newPaths := make([]string, 0, len(a)+len(b)) + newPaths = append(newPaths, a...) + for _, bi := range b { + if !containsString(newPaths, bi) { + newPaths = append(newPaths, bi) + } + } + return newPaths } -// Error implements error -func (fe *FieldError) Error() string { - var errs []string - for _, e := range fe.getNormalizedErrors() { - if e.Details == "" { - errs = append(errs, fmt.Sprintf("%v: %v", e.Message, strings.Join(e.Paths, ", "))) +// containsString takes in a string slice and looks for the provided string +// within the slice. +func containsString(slice []string, s string) bool { + for _, item := range slice { + if item == s { + return true + } + } + return false +} + +// merge takes in a flat list of FieldErrors and returns back a merged list of +// FiledErrors. FieldErrors have their Paths combined (and de-duped) if their +// Message and Details are the same. Merge will not inspect FieldError.errors. +// Merge will also sort the .Path slice, and the errors slice before returning. +func merge(errs []FieldError) []FieldError { + // make a map big enough for all the errors. + m := make(map[string]FieldError, len(errs)) + + // Convert errs to a map where the key is -
and the value + // is the error. If an error already exists in the map with the same key, + // then the paths will be merged. + for _, e := range errs { + k := key(&e) + if v, ok := m[k]; ok { + // Found a match, merge the keys. + v.Paths = mergePaths(v.Paths, e.Paths) + m[k] = v } else { - errs = append(errs, fmt.Sprintf("%v: %v\n%v", e.Message, strings.Join(e.Paths, ", "), e.Details)) + // Does not exist in the map, save the error. + m[k] = e } } - return strings.Join(errs, "\n") + + // Take the map made previously and flatten it back out again. + newErrs := make([]FieldError, 0, len(m)) + for _, v := range m { + // While we have access to the merged paths, sort them too. + sort.Slice(v.Paths, func(i, j int) bool { return v.Paths[i] < v.Paths[j] }) + newErrs = append(newErrs, v) + } + + // Sort the flattened map. + sort.Slice(newErrs, func(i, j int) bool { + if newErrs[i].Message == newErrs[j].Message { + return newErrs[i].Details < newErrs[j].Details + } + return newErrs[i].Message < newErrs[j].Message + }) + + // return back the merged list of sorted errors. + return newErrs } +// key returns the key using the fields .Message and .Details. +func key(err *FieldError) string { + return fmt.Sprintf("%s-%s", err.Message, err.Details) +} + +// Public helpers --- + // ErrMissingField is a variadic helper method for constructing a FieldError for // a set of missing fields. func ErrMissingField(fieldPaths ...string) *FieldError { diff --git a/vendor/github.com/knative/pkg/apis/zz_generated.deepcopy.go b/vendor/github.com/knative/pkg/apis/zz_generated.deepcopy.go index bacb0b708e4..76db35ce157 100644 --- a/vendor/github.com/knative/pkg/apis/zz_generated.deepcopy.go +++ b/vendor/github.com/knative/pkg/apis/zz_generated.deepcopy.go @@ -20,6 +20,34 @@ limitations under the License. package apis +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FieldError) DeepCopyInto(out *FieldError) { + *out = *in + if in.Paths != nil { + in, out := &in.Paths, &out.Paths + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.errors != nil { + in, out := &in.errors, &out.errors + *out = make([]FieldError, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FieldError. +func (in *FieldError) DeepCopy() *FieldError { + if in == nil { + return nil + } + out := new(FieldError) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *VolatileTime) DeepCopyInto(out *VolatileTime) { *out = *in diff --git a/vendor/github.com/knative/pkg/configmap/filter.go b/vendor/github.com/knative/pkg/configmap/filter.go new file mode 100644 index 00000000000..27bf13df98b --- /dev/null +++ b/vendor/github.com/knative/pkg/configmap/filter.go @@ -0,0 +1,44 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://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 configmap + +import "reflect" + +// TypeFilter accepts instances of types to check against and returns a function transformer that would only let +// the call to f through if value is assignable to any one of types of ts. Example: +// +// F := configmap.TypeFilter(&config.Domain{})(f) +// +// The result is a function F(name string, value interface{}) that will call the underlying function +// f(name, value) iff value is a *config.Domain +func TypeFilter(ts ...interface{}) func(func(string, interface{})) func(string, interface{}) { + return func(f func(string, interface{})) func(string, interface{}) { + return func(name string, value interface{}) { + satisfies := false + for _, t := range ts { + t := reflect.TypeOf(t) + if reflect.TypeOf(value).AssignableTo(t) { + satisfies = true + break + } + } + if satisfies { + f(name, value) + } + } + } +} diff --git a/vendor/github.com/knative/pkg/configmap/store.go b/vendor/github.com/knative/pkg/configmap/store.go new file mode 100644 index 00000000000..62cab432452 --- /dev/null +++ b/vendor/github.com/knative/pkg/configmap/store.go @@ -0,0 +1,174 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://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 configmap + +import ( + "reflect" + "sync/atomic" + + corev1 "k8s.io/api/core/v1" +) + +// Logger is the interface that UntypedStore expects its logger to conform to. +// UntypedStore will log when updates succeed or fail. +type Logger interface { + Infof(string, ...interface{}) + Fatalf(string, ...interface{}) + Errorf(string, ...interface{}) +} + +// Constructors is a map for specifying config names to +// their function constructors +// +// The values of this map must be functions with the definition +// +// func(*k8s.io/api/core/v1.ConfigMap) (... , error) +// +// These functions can return any type along with an error +type Constructors map[string]interface{} + +// An UntypedStore is a responsible for storing and +// constructing configs from Kubernetes ConfigMaps +// +// WatchConfigs should be used with a configmap,Watcher +// in order for this store to remain up to date +type UntypedStore struct { + name string + logger Logger + + storages map[string]*atomic.Value + constructors map[string]reflect.Value + + onAfterStore []func(name string, value interface{}) +} + +// NewUntypedStore creates an UntypedStore with given name, +// Logger and Constructors +// +// The Logger must not be nil +// +// The values in the Constructors map must be functions with +// the definition +// +// func(*k8s.io/api/core/v1.ConfigMap) (... , error) +// +// These functions can return any type along with an error. +// If the function definition differs then NewUntypedStore +// will panic. +// +// onAfterStore is a variadic list of callbacks to run +// after the ConfigMap has been transformed (via the appropriate Constructor) +// and stored. These callbacks run sequentially (in the argument order) in a +// separate go-routine and are of type func(name string, value interface{}) +// where name is the config-map name and value is the object that has been +// constructed from the config-map and stored. +func NewUntypedStore( + name string, + logger Logger, + constructors Constructors, + onAfterStore ...func(name string, value interface{})) *UntypedStore { + + store := &UntypedStore{ + name: name, + logger: logger, + storages: make(map[string]*atomic.Value), + constructors: make(map[string]reflect.Value), + onAfterStore: onAfterStore, + } + + for configName, constructor := range constructors { + store.registerConfig(configName, constructor) + } + + return store +} + +func (s *UntypedStore) registerConfig(name string, constructor interface{}) { + cType := reflect.TypeOf(constructor) + + if cType.Kind() != reflect.Func { + panic("config constructor must be a function") + } + + if cType.NumIn() != 1 || cType.In(0) != reflect.TypeOf(&corev1.ConfigMap{}) { + panic("config constructor must be of the type func(*k8s.io/api/core/v1/ConfigMap) (..., error)") + } + + errorType := reflect.TypeOf((*error)(nil)).Elem() + + if cType.NumOut() != 2 || !cType.Out(1).Implements(errorType) { + panic("config constructor must be of the type func(*k8s.io/api/core/v1/ConfigMap) (..., error)") + } + + s.storages[name] = &atomic.Value{} + s.constructors[name] = reflect.ValueOf(constructor) +} + +// WatchConfigs uses the provided configmap.Watcher +// to setup watches for the config names provided in the +// Constructors map +func (s *UntypedStore) WatchConfigs(w Watcher) { + for configMapName := range s.constructors { + w.Watch(configMapName, s.OnConfigChanged) + } +} + +// UntypedLoad will return the constructed value for a given +// ConfigMap name +func (s *UntypedStore) UntypedLoad(name string) interface{} { + storage := s.storages[name] + return storage.Load() +} + +// OnConfigChanged will invoke the mapped constructor against +// a Kubernetes ConfigMap. If successful it will be stored. +// If construction fails during the first appearance the store +// will log a fatal error. If construction fails while updating +// the store will log an error message. +func (s *UntypedStore) OnConfigChanged(c *corev1.ConfigMap) { + name := c.ObjectMeta.Name + + storage := s.storages[name] + constructor := s.constructors[name] + + inputs := []reflect.Value{ + reflect.ValueOf(c), + } + + outputs := constructor.Call(inputs) + result := outputs[0].Interface() + errVal := outputs[1] + + if !errVal.IsNil() { + err := errVal.Interface() + if storage.Load() != nil { + s.logger.Errorf("Error updating %s config %q: %q", s.name, name, err) + } else { + s.logger.Fatalf("Error initializing %s config %q: %q", s.name, name, err) + } + return + } + + s.logger.Infof("%s config %q config was added or updated: %v", s.name, name, result) + storage.Store(result) + + go func() { + for _, f := range s.onAfterStore { + f(name, result) + } + }() +} diff --git a/vendor/github.com/knative/pkg/test/e2e_flags.go b/vendor/github.com/knative/pkg/test/e2e_flags.go index 7d8261accd1..1cd0fada634 100644 --- a/vendor/github.com/knative/pkg/test/e2e_flags.go +++ b/vendor/github.com/knative/pkg/test/e2e_flags.go @@ -45,8 +45,10 @@ func initializeFlags() *EnvironmentFlags { flag.StringVar(&f.Cluster, "cluster", defaultCluster, "Provide the cluster to test against. Defaults to $K8S_CLUSTER_OVERRIDE, then current cluster in kubeconfig if $K8S_CLUSTER_OVERRIDE is unset.") - usr, _ := user.Current() - defaultKubeconfig := path.Join(usr.HomeDir, ".kube/config") + var defaultKubeconfig string + if usr, err := user.Current(); err == nil { + defaultKubeconfig = path.Join(usr.HomeDir, ".kube/config") + } flag.StringVar(&f.Kubeconfig, "kubeconfig", defaultKubeconfig, "Provide the path to the `kubeconfig` file you'd like to use for these tests. The `current-context` will be used.") diff --git a/vendor/github.com/knative/pkg/test/kube_checks.go b/vendor/github.com/knative/pkg/test/kube_checks.go index 6d3a828cd50..74691b362e9 100644 --- a/vendor/github.com/knative/pkg/test/kube_checks.go +++ b/vendor/github.com/knative/pkg/test/kube_checks.go @@ -32,15 +32,15 @@ import ( ) const ( - interval = 1 * time.Second - timeout = 5 * time.Minute + interval = 1 * time.Second + podTimeout = 5 * time.Minute ) // WaitForDeploymentState polls the status of the Deployment called name // from client every interval until inState returns `true` indicating it // is done, returns an error or timeout. desc will be used to name the metric // that is emitted to track how long it took for name to get into the state checked by inState. -func WaitForDeploymentState(client *KubeClient, name string, inState func(d *apiv1beta1.Deployment) (bool, error), desc string, namespace string) error { +func WaitForDeploymentState(client *KubeClient, name string, inState func(d *apiv1beta1.Deployment) (bool, error), desc string, namespace string, timeout time.Duration) error { d := client.Kube.ExtensionsV1beta1().Deployments(namespace) metricName := fmt.Sprintf("WaitForDeploymentState/%s/%s", name, desc) _, span := trace.StartSpan(context.Background(), metricName) @@ -65,7 +65,7 @@ func WaitForPodListState(client *KubeClient, inState func(p *corev1.PodList) (bo _, span := trace.StartSpan(context.Background(), metricName) defer span.End() - return wait.PollImmediate(interval, timeout, func() (bool, error) { + return wait.PollImmediate(interval, podTimeout, func() (bool, error) { p, err := p.List(metav1.ListOptions{}) if err != nil { return true, err diff --git a/vendor/github.com/knative/pkg/test/logging/logging.go b/vendor/github.com/knative/pkg/test/logging/logging.go index 4bf52f7bdfe..b5166b207ec 100644 --- a/vendor/github.com/knative/pkg/test/logging/logging.go +++ b/vendor/github.com/knative/pkg/test/logging/logging.go @@ -77,17 +77,18 @@ func newLogger(logLevel string) *BaseLogger { "outputPaths": ["stdout"], "errorOutputPaths": ["stderr"], "encoderConfig": { + "timeKey": "ts", "messageKey": "message", - "levelKey": "level", - "nameKey": "logger", - "callerKey": "caller", - "messageKey": "msg", - "stacktraceKey": "stacktrace", - "lineEnding": "", - "levelEncoder": "", - "timeEncoder": "", - "durationEncoder": "", - "callerEncoder": "" + "levelKey": "level", + "nameKey": "logger", + "callerKey": "caller", + "messageKey": "msg", + "stacktraceKey": "stacktrace", + "lineEnding": "", + "levelEncoder": "", + "timeEncoder": "iso8601", + "durationEncoder": "", + "callerEncoder": "" } }` configJSON := fmt.Sprintf(configJSONTemplate, logLevel) diff --git a/vendor/github.com/knative/pkg/test/spoof/spoof.go b/vendor/github.com/knative/pkg/test/spoof/spoof.go index 21f5b83bb4a..a8bb1f4ac48 100644 --- a/vendor/github.com/knative/pkg/test/spoof/spoof.go +++ b/vendor/github.com/knative/pkg/test/spoof/spoof.go @@ -101,16 +101,21 @@ func New(kubeClientset *kubernetes.Clientset, logger *logging.BaseLogger, domain if err != nil { return nil, err } + ingresses := ingress.Status.LoadBalancer.Ingress - if len(ingress.Status.LoadBalancer.Ingress) != 1 { - return nil, fmt.Errorf("Expected exactly one ingress load balancer, instead had %d: %s", len(ingress.Status.LoadBalancer.Ingress), ingress.Status.LoadBalancer.Ingress) + if len(ingresses) != 1 { + return nil, fmt.Errorf("Expected exactly one ingress load balancer, instead had %d: %s", len(ingresses), ingresses) } - if ingress.Status.LoadBalancer.Ingress[0].IP == "" { - return nil, fmt.Errorf("Expected ingress loadbalancer IP for %s to be set, instead was empty", ingressName) + ingressToUse := ingresses[0] + if ingressToUse.IP == "" { + if ingressToUse.Hostname == "" { + return nil, fmt.Errorf("Expected ingress loadbalancer IP or hostname for %s to be set, instead was empty", ingressName) + } + sc.endpoint = ingressToUse.Hostname + } else { + sc.endpoint = ingressToUse.IP } - - sc.endpoint = ingress.Status.LoadBalancer.Ingress[0].IP sc.domain = domain } else { // If the domain is resolvable, we can use it directly when we make requests. diff --git a/vendor/github.com/knative/pkg/webhook/certs.go b/vendor/github.com/knative/pkg/webhook/certs.go index 8bc80b4f4ef..510783bbeca 100644 --- a/vendor/github.com/knative/pkg/webhook/certs.go +++ b/vendor/github.com/knative/pkg/webhook/certs.go @@ -47,7 +47,12 @@ func createCertTemplate(name, namespace string) (*x509.Certificate, error) { } serviceName := name + "." + namespace - serviceNames := []string{serviceName, serviceName + ".svc", serviceName + ".svc.cluster.local"} + serviceNames := []string{ + name, + serviceName, + serviceName + ".svc", + serviceName + ".svc.cluster.local", + } tmpl := x509.Certificate{ SerialNumber: serialNumber, diff --git a/vendor/github.com/knative/pkg/webhook/webhook.go b/vendor/github.com/knative/pkg/webhook/webhook.go index 8727dca7741..6ca41821419 100644 --- a/vendor/github.com/knative/pkg/webhook/webhook.go +++ b/vendor/github.com/knative/pkg/webhook/webhook.go @@ -38,6 +38,7 @@ import ( "github.com/knative/pkg/logging" "github.com/knative/pkg/logging/logkey" + "github.com/markbates/inflect" "github.com/mattbaird/jsonpatch" admissionv1beta1 "k8s.io/api/admission/v1beta1" admissionregistrationv1beta1 "k8s.io/api/admissionregistration/v1beta1" @@ -94,6 +95,11 @@ type ControllerOptions struct { // potential races where registration completes and k8s apiserver // invokes the webhook before the HTTP server is started. RegistrationDelay time.Duration + + // ClientAuthType declares the policy the webhook server will follow for + // TLS Client Authentication. + // The default value is tls.NoClientCert. + ClientAuth tls.ClientAuthType } // ResourceCallback defines a signature for resource specific (Route, Configuration, etc.) @@ -134,15 +140,16 @@ func getAPIServerExtensionCACert(cl kubernetes.Interface) ([]byte, error) { if err != nil { return nil, err } - pem, ok := c.Data["requestheader-client-ca-file"] + const caFileName = "requestheader-client-ca-file" + pem, ok := c.Data[caFileName] if !ok { - return nil, fmt.Errorf("cannot find ca.crt in %v: ConfigMap.Data is %#v", name, c.Data) + return nil, fmt.Errorf("cannot find %s in ConfigMap %s: ConfigMap.Data is %#v", caFileName, name, c.Data) } return []byte(pem), nil } // MakeTLSConfig makes a TLS configuration suitable for use with the server -func makeTLSConfig(serverCert, serverKey, caCert []byte) (*tls.Config, error) { +func makeTLSConfig(serverCert, serverKey, caCert []byte, clientAuthType tls.ClientAuthType) (*tls.Config, error) { caCertPool := x509.NewCertPool() caCertPool.AppendCertsFromPEM(caCert) cert, err := tls.X509KeyPair(serverCert, serverKey) @@ -152,11 +159,7 @@ func makeTLSConfig(serverCert, serverKey, caCert []byte) (*tls.Config, error) { return &tls.Config{ Certificates: []tls.Certificate{cert}, ClientCAs: caCertPool, - ClientAuth: tls.NoClientCert, - // Note on GKE there apparently is no client cert sent, so this - // does not work on GKE. - // TODO: make this into a configuration option. - // ClientAuth: tls.RequireAndVerifyClientCert, + ClientAuth: clientAuthType, }, nil } @@ -234,16 +237,20 @@ func SetDefaults(ctx context.Context) ResourceDefaulter { } func configureCerts(ctx context.Context, client kubernetes.Interface, options *ControllerOptions) (*tls.Config, []byte, error) { - apiServerCACert, err := getAPIServerExtensionCACert(client) - if err != nil { - return nil, nil, err + var apiServerCACert []byte + if options.ClientAuth >= tls.VerifyClientCertIfGiven { + var err error + apiServerCACert, err = getAPIServerExtensionCACert(client) + if err != nil { + return nil, nil, err + } } - serverKey, serverCert, caCert, err := getOrGenerateKeyCertsFromSecret( - ctx, client, options) + + serverKey, serverCert, caCert, err := getOrGenerateKeyCertsFromSecret(ctx, client, options) if err != nil { return nil, nil, err } - tlsConfig, err := makeTLSConfig(serverCert, serverKey, apiServerCACert) + tlsConfig, err := makeTLSConfig(serverCert, serverKey, apiServerCACert, options.ClientAuth) if err != nil { return nil, nil, err } @@ -287,32 +294,25 @@ func (ac *AdmissionController) Run(stop <-chan struct{}) error { logger.Error("Failed to register webhook", zap.Error(err)) return err } - defer func() { - if err := ac.unregister(ctx, cl); err != nil { - logger.Error("Failed to unregister webhook", zap.Error(err)) - } - }() logger.Info("Successfully registered webhook") case <-stop: return nil } + serverBootstrapErrCh := make(chan struct{}) go func() { if err := server.ListenAndServeTLS("", ""); err != nil { logger.Error("ListenAndServeTLS for admission webhook returned error", zap.Error(err)) + close(serverBootstrapErrCh) } }() - <-stop - server.Close() // nolint: errcheck - return nil -} -// Unregister unregisters the external admission webhook -func (ac *AdmissionController) unregister( - ctx context.Context, client clientadmissionregistrationv1beta1.MutatingWebhookConfigurationInterface) error { - logger := logging.FromContext(ctx) - logger.Info("Exiting..") - return nil + select { + case <-stop: + return server.Close() + case <-serverBootstrapErrCh: + return errors.New("webhook server bootstrap failed") + } } // Register registers the external admission webhook for pilot @@ -324,8 +324,7 @@ func (ac *AdmissionController) register( var rules []admissionregistrationv1beta1.RuleWithOperations for gvk := range ac.Handlers { - // Lousy pluralizer - plural := strings.ToLower(gvk.Kind) + "s" + plural := strings.ToLower(inflect.Pluralize(gvk.Kind)) rules = append(rules, admissionregistrationv1beta1.RuleWithOperations{ Operations: []admissionregistrationv1beta1.OperationType{ @@ -502,7 +501,6 @@ func (ac *AdmissionController) mutate(ctx context.Context, kind metav1.GroupVers if len(newBytes) != 0 { newDecoder := json.NewDecoder(bytes.NewBuffer(newBytes)) - newDecoder.DisallowUnknownFields() if err := newDecoder.Decode(&newObj); err != nil { return nil, fmt.Errorf("cannot decode incoming new object: %v", err) } @@ -513,7 +511,6 @@ func (ac *AdmissionController) mutate(ctx context.Context, kind metav1.GroupVers if len(oldBytes) != 0 { oldDecoder := json.NewDecoder(bytes.NewBuffer(oldBytes)) - oldDecoder.DisallowUnknownFields() if err := oldDecoder.Decode(&oldObj); err != nil { return nil, fmt.Errorf("cannot decode incoming old object: %v", err) } diff --git a/vendor/github.com/knative/test-infra/scripts/README.md b/vendor/github.com/knative/test-infra/scripts/README.md index 5ff9ccbce21..083f2353293 100644 --- a/vendor/github.com/knative/test-infra/scripts/README.md +++ b/vendor/github.com/knative/test-infra/scripts/README.md @@ -1,3 +1,197 @@ # Helper scripts -This directory contains helper scripts used by Prow test jobs, as well and local development scripts. +This directory contains helper scripts used by Prow test jobs, as well and +local development scripts. + +## Using the `presubmit-tests.sh` helper script + +This is a helper script to run the presubmit tests. To use it: + +1. Source this script. + +1. Define the functions `build_tests()` and `unit_tests()`. They should run all +tests (i.e., not fail fast), and return 0 if all passed, 1 if a failure +occurred. The environment variables `RUN_BUILD_TESTS`, `RUN_UNIT_TESTS` and +`RUN_INTEGRATION_TESTS` are set to 0 (false) or 1 (true) accordingly. If +`--emit-metrics` is passed, `EMIT_METRICS` will be set to 1. + +1. [optional] Define the function `integration_tests()`, just like the previous +ones. If you don't define this function, the default action for running the +integration tests is to call the `./test/e2e-tests.sh` script (passing the +`--emit-metrics` flag if necessary). + +1. [optional] Define the functions `pre_integration_tests()` or +`post_integration_tests()`. These functions will be called before or after the +integration tests (either your custom one or the default action) and will cause +the test to fail if they don't return success. + +1. Call the `main()` function passing `$@` (without quotes). + +Running the script without parameters, or with the `--all-tests` flag causes +all tests to be executed, in the right order (i.e., build, then unit, then +integration tests). + +Use the flags `--build-tests`, `--unit-tests` and `--integration-tests` to run +a specific set of tests. The flag `--emit-metrics` is used to emit metrics when +running the tests, and is automatically handled by the default action (see +above). + +### Sample presubmit test script + +```bash +source vendor/github.com/knative/test-infra/scripts/presubmit-tests.sh + +function build_tests() { + go build . +} + +function unit_tests() { + report_go_test . +} + +function pre_integration_tests() { + echo "Cleaning up before integration tests" + rm -fr ./staging-area +} + +# We use the default integration test runner. + +main $@ +``` + +## Using the `e2e-tests.sh` helper script + +This is a helper script for Knative E2E test scripts. To use it: + +1. Source the script. + +1. [optional] Write the `teardown()` function, which will tear down your test +resources. + +1. [optional] Write the `dump_extra_cluster_state()` function. It will be +called when a test fails, and can dump extra information about the current state +of the cluster (tipically using `kubectl`). + +1. [optional] Write the `parse_flags()` function. It will be called whenever an +unrecognized flag is passed to the script, allowing you to define your own flags. +The function must return 0 if the flag is unrecognized, or the number of items +to skip in the command line if the flag was parsed successfully. For example, +return 1 for a simple flag, and 2 for a flag with a parameter. + +1. Call the `initialize()` function passing `$@` (without quotes). + +1. Write logic for the end-to-end tests. Run all go tests using `go_test_e2e()` +(or `report_go_test()` if you need a more fine-grained control) and call +`fail_test()` or `success()` if any of them failed. The environment variables +`DOCKER_REPO_OVERRIDE`, `K8S_CLUSTER_OVERRIDE` and `K8S_USER_OVERRIDE` will be set +according to the test cluster. You can also use the following boolean (0 is false, +1 is true) environment variables for the logic: + * `EMIT_METRICS`: true if `--emit-metrics` was passed. + * `USING_EXISTING_CLUSTER`: true if the test cluster is an already existing one, +and not a temporary cluster created by `kubetest`. + + All environment variables above are marked read-only. + +**Notes:** + +1. Calling your script without arguments will create a new cluster in the GCP +project `$PROJECT_ID` and run the tests against it. + +1. Calling your script with `--run-tests` and the variables `K8S_CLUSTER_OVERRIDE`, +`K8S_USER_OVERRIDE` and `DOCKER_REPO_OVERRIDE` set will immediately start the +tests against the cluster. + +### Sample end-to-end test script + +This script will test that the latest Knative Serving nightly release works. It +defines a special flag (`--no-knative-wait`) that causes the script not to +wait for Knative Serving to be up before running the tests. + +```bash +source vendor/github.com/knative/test-infra/scripts/e2e-tests.sh + +function teardown() { + echo "TODO: tear down test resources" +} + +function parse_flags() { + if [[ "$1" == "--no-knative-wait" ]]; then + WAIT_FOR_KNATIVE=0 + return 1 + fi + return 0 +} + +WAIT_FOR_KNATIVE=1 + +initialize $@ + +start_latest_knative_serving + +if (( WAIT_FOR_KNATIVE )); then + wait_until_pods_running knative-serving || fail_test "Knative Serving is not up" +fi + +# TODO: use go_test_e2e to run the tests. +kubectl get pods || fail_test + +success +``` + +## Using the `release.sh` helper script + +This is a helper script for Knative release scripts. To use it: + +1. Source the script. + +1. Call the `initialize()` function passing `$@` (without quotes). + +1. Call the `run_validation_tests()` function passing the script or executable that +runs the release validation tests. It will call the script to run the tests unless +`--skip_tests` was passed. + +1. Write logic for the release process. Call `publish_yaml()` to publish the manifest(s), +`tag_releases_in_yaml()` to tag the generated images, `branch_release()` to branch +named releases. Use the following boolean (0 is false, 1 is true) and string environment +variables for the logic: + * `RELEASE_VERSION`: contains the release version if `--version` was passed. This +also overrides the value of the `TAG` variable as `v`. + * `RELEASE_BRANCH`: contains the release branch if `--branch` was passed. Otherwise +it's empty and `master` HEAD will be considered the release branch. + * `RELEASE_NOTES`: contains the filename with the release notes if `--release-notes` +was passed. The release notes is a simple markdown file. + * `SKIP_TESTS`: true if `--skip-tests` was passed. This is handled automatically +by the run_validation_tests() function. + * `TAG_RELEASE`: true if `--tag-release` was passed. In this case, the environment +variable `TAG` will contain the release tag in the form `vYYYYMMDD-`. + * `PUBLISH_RELEASE`: true if `--publish` was passed. In this case, the environment +variable `KO_FLAGS` will be updated with the `-L` option. + * `BRANCH_RELEASE`: true if both `--version` and `--publish-release` were passed. + + All boolean environment variables default to false for safety. + + All environment variables above, except `KO_FLAGS`, are marked read-only once +`initialize()` is called. + +### Sample release script + +```bash +source vendor/github.com/knative/test-infra/scripts/release.sh + +initialize $@ + +run_validation_tests ./test/presubmit-tests.sh + +# config/ contains the manifests +KO_DOCKER_REPO=gcr.io/knative-foo +ko resolve ${KO_FLAGS} -f config/ > release.yaml + +tag_images_in_yaml release.yaml $KO_DOCKER_REPO $TAG + +if (( PUBLISH_RELEASE )); then + # gs://knative-foo hosts the manifest + publish_yaml release.yaml knative-foo $TAG +fi + +branch_release "Knative Foo" release.yaml +``` diff --git a/vendor/github.com/knative/test-infra/scripts/e2e-tests.sh b/vendor/github.com/knative/test-infra/scripts/e2e-tests.sh index 7c921048254..ae88e1b12e0 100755 --- a/vendor/github.com/knative/test-infra/scripts/e2e-tests.sh +++ b/vendor/github.com/knative/test-infra/scripts/e2e-tests.sh @@ -14,29 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This is a helper script for Knative E2E test scripts. To use it: -# 1. Source this script. -# 2. [optional] Write the teardown() function, which will tear down your test -# resources. -# 3. [optional] Write the dump_extra_cluster_state() function. It will be called -# when a test fails, and can dump extra information about the current state of -# the cluster (tipically using kubectl). -# 4. Call the initialize() function passing $@ (without quotes). -# 5. Write logic for the end-to-end tests. Run all go tests using report_go_test() -# and call fail_test() or success() if any of them failed. The envitronment -# variables DOCKER_REPO_OVERRIDE, K8S_CLUSTER_OVERRIDE and K8S_USER_OVERRIDE -# will be set accordingly to the test cluster. You can also use the following -# boolean (0 is false, 1 is true) environment variables for the logic: -# EMIT_METRICS: true if --emit-metrics is passed. -# USING_EXISTING_CLUSTER: true if the test cluster is an already existing one, -# and not a temporary cluster created by kubetest. -# All environment variables above are marked read-only. -# Notes: -# 1. Calling your script without arguments will create a new cluster in the GCP -# project $PROJECT_ID and run the tests against it. -# 2. Calling your script with --run-tests and the variables K8S_CLUSTER_OVERRIDE, -# K8S_USER_OVERRIDE and DOCKER_REPO_OVERRIDE set will immediately start the -# tests against the cluster. +# This is a helper script for Knative E2E test scripts. +# See README.md for instructions on how to use it. source $(dirname ${BASH_SOURCE})/library.sh @@ -51,7 +30,9 @@ function build_resource_name() { if [[ -n "${suffix}" ]]; then suffix=${suffix:${#suffix}<20?0:-20} fi - echo "${prefix:0:20}${suffix}" + local name="${prefix:0:20}${suffix}" + # Ensure name doesn't end with "-" + echo "${name%-}" } # Test cluster parameters @@ -68,7 +49,7 @@ readonly TEST_RESULT_FILE=/tmp/${E2E_BASE_NAME}-e2e-result function teardown_test_resources() { header "Tearing down test environment" # Free resources in GCP project. - if (( ! USING_EXISTING_CLUSTER )) && [[ "$(type -t teardown)" == "function" ]]; then + if (( ! USING_EXISTING_CLUSTER )) && function_exists teardown; then teardown fi @@ -91,6 +72,14 @@ function fail_test() { exit 1 } +# Run the given E2E tests (must be tagged as such). +# Parameters: $1..$n - any go test flags, then directories containing the tests to run. +function go_test_e2e() { + local options="" + (( EMIT_METRICS )) && options="-emitmetrics" + report_go_test -v -tags=e2e -count=1 $@ ${options} +} + # Download the k8s binaries required by kubetest. function download_k8s() { local version=${SERVING_GKE_VERSION} @@ -143,7 +132,7 @@ function dump_cluster_state() { kubectl get services --all-namespaces echo ">>> Events:" kubectl get events --all-namespaces - [[ "$(type -t dump_extra_cluster_state)" == "function" ]] && dump_extra_cluster_state + function_exists dump_extra_cluster_state && dump_extra_cluster_state echo "***************************************" echo "*** TEST FAILED ***" echo "*** End of information dump ***" @@ -259,7 +248,7 @@ function setup_test_cluster() { trap teardown_test_resources EXIT - if (( USING_EXISTING_CLUSTER )) && [[ "$(type -t teardown)" == "function" ]]; then + if (( USING_EXISTING_CLUSTER )) && function_exists teardown; then echo "Deleting any previous SUT instance" teardown fi @@ -300,7 +289,19 @@ function initialize() { readonly E2E_SCRIPT cd ${REPO_ROOT_DIR} - for parameter in $@; do + while [[ $# -ne 0 ]]; do + local parameter=$1 + # Try parsing flag as a custom one. + if function_exists parse_flags; then + parse_flags $@ + local skip=$? + if [[ ${skip} -ne 0 ]]; then + # Skip parsed flag (and possibly argument) and continue + shift ${skip} + continue + fi + fi + # Try parsing flag as a standard one. case $parameter in --run-tests) RUN_TESTS=1 ;; --emit-metrics) EMIT_METRICS=1 ;; diff --git a/vendor/github.com/knative/test-infra/scripts/library.sh b/vendor/github.com/knative/test-infra/scripts/library.sh index a330f4dbc3d..084c9d354a7 100755 --- a/vendor/github.com/knative/test-infra/scripts/library.sh +++ b/vendor/github.com/knative/test-infra/scripts/library.sh @@ -66,6 +66,11 @@ function warning() { make_banner "!" "$1" } +# Checks whether the given function exists. +function function_exists() { + [[ "$(type -t $1)" == "function" ]] +} + # Remove ALL images in the given GCR repository. # Parameters: $1 - GCR repository. function delete_gcr_images() { @@ -94,7 +99,7 @@ function wait_until_object_does_not_exist() { fi echo -n "Waiting until ${DESCRIPTION} does not exist" for i in {1..150}; do # timeout after 5 minutes - kubectl ${KUBECTL_ARGS} 2>&1 > /dev/null || return 0 + kubectl ${KUBECTL_ARGS} > /dev/null 2>&1 || return 0 echo -n "." sleep 2 done @@ -235,9 +240,11 @@ function report_go_test() { echo "Finished run, return code is ${failed}" # Tests didn't run. [[ ! -s ${report} ]] && return 1 - # Create WORKSPACE file, required to use bazel + # Create WORKSPACE file, required to use bazel, if necessary. touch WORKSPACE local targets="" + local last_run="" + local test_files="" # Parse the report and generate fake tests for each passing/failing test. echo "Start parsing results, summary:" while read line ; do @@ -245,16 +252,35 @@ function report_go_test() { local field0="${fields[0]}" local field1="${fields[1]}" local name="${fields[2]}" + # Deal with a SIGQUIT log entry (usually a test timeout). + # This is a fallback in case there's no kill signal log entry. + # SIGQUIT: quit + if [[ "${field0}" == "SIGQUIT:" ]]; then + name="${last_run}" + field1="FAIL:" + error="${fields[@]}" + fi # Ignore subtests (those containing slashes) if [[ -n "${name##*/*}" ]]; then local error="" + # Deal with a kill signal log entry (usually a test timeout). + # *** Test killed with quit: ran too long (10m0s). + if [[ "${field0}" == "***" ]]; then + name="${last_run}" + field1="FAIL:" + error="${fields[@]:1}" + fi # Deal with a fatal log entry, which has a different format: # fatal TestFoo foo_test.go:275 Expected "foo" but got "bar" if [[ "${field0}" == "fatal" ]]; then - name="${fields[1]}" + name="${field1}" field1="FAIL:" error="${fields[@]:3}" fi + # Keep track of the test currently running. + if [[ "${field1}" == "RUN" ]]; then + last_run="${name}" + fi # Handle regular go test pass/fail entry for a test. if [[ "${field1}" == "PASS:" || "${field1}" == "FAIL:" ]]; then echo "- ${name} :${field1}" @@ -269,13 +295,14 @@ function report_go_test() { echo "exit 1" >> ${src} fi chmod +x ${src} + test_files="${test_files} ${src}" # Populate BUILD.bazel echo "sh_test(name=\"${name}\", srcs=[\"${src}\"])" >> BUILD.bazel elif [[ "${field0}" == "FAIL" || "${field0}" == "ok" ]] && [[ -n "${field1}" ]]; then echo "- ${field0} ${field1}" # Create the package structure, move tests and BUILD file local package=${field1/github.com\//} - local bazel_files="$(ls -1 *.sh BUILD.bazel 2> /dev/null)" + local bazel_files="$(ls -1 ${test_files} BUILD.bazel 2> /dev/null)" if [[ -n "${bazel_files}" ]]; then mkdir -p ${package} targets="${targets} //${package}/..." @@ -283,6 +310,7 @@ function report_go_test() { else echo "*** INTERNAL ERROR: missing tests for ${package}, got [${bazel_files/$'\n'/, }]" fi + test_files="" fi fi done < ${report} @@ -291,11 +319,15 @@ function report_go_test() { # Otherwise, we already shown the summary. # Exception: when emitting metrics, dump the full report. if (( failed )) || [[ "$@" == *" -emitmetrics"* ]]; then - echo "At least one test failed, full log:" + if (( failed )); then + echo "At least one test failed, full log:" + else + echo "Dumping full log as metrics were requested:" + fi cat ${report} fi # Always generate the junit summary. - bazel test ${targets} > /dev/null 2>&1 + bazel test ${targets} > /dev/null 2>&1 || true return ${failed} } @@ -323,14 +355,17 @@ function start_latest_knative_build() { wait_until_pods_running knative-build || return 1 } -# Run dep-collector, installing it first if necessary. -# Parameters: $1..$n - parameters passed to dep-collector. -function run_dep_collector() { - local local_dep_collector="$(which dep-collector)" - if [[ -z ${local_dep_collector} ]]; then - go get -u github.com/mattmoor/dep-collector +# Run a go tool, installing it first if necessary. +# Parameters: $1 - tool to run. +# $2 - tool package for go get. +# $3..$n - parameters passed to the tool. +function run_go_tool() { + local tool=$1 + if [[ -z "$(which ${tool})" ]]; then + go install $2 fi - dep-collector $@ + shift 2 + ${tool} $@ } # Run dep-collector to update licenses. @@ -340,7 +375,7 @@ function update_licenses() { cd ${REPO_ROOT_DIR} || return 1 local dst=$1 shift - run_dep_collector $@ > ./${dst} + run_go_tool dep-collector ./vendor/github.com/knative/test-infra/tools/dep-collector $@ > ./${dst} } # Run dep-collector to check for forbidden liceses. @@ -349,7 +384,7 @@ function check_licenses() { # Fetch the google/licenseclassifier for its license db go get -u github.com/google/licenseclassifier # Check that we don't have any forbidden licenses in our images. - run_dep_collector -check $@ + run_go_tool dep-collector ./vendor/github.com/knative/test-infra/tools/dep-collector -check $@ } # Run the given linter on the given files, checking it exists first. diff --git a/vendor/github.com/knative/test-infra/scripts/presubmit-tests.sh b/vendor/github.com/knative/test-infra/scripts/presubmit-tests.sh index b6116817ca6..db78f3d0ca1 100755 --- a/vendor/github.com/knative/test-infra/scripts/presubmit-tests.sh +++ b/vendor/github.com/knative/test-infra/scripts/presubmit-tests.sh @@ -14,21 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This is a helper script to run the presubmit tests. To use it: -# 1. Source this script. -# 2. Define the functions build_tests(), unit_tests() and -# integration_tests(). They should run all tests (i.e., not fail -# fast), and return 0 if all passed, 1 if a failure occurred. -# The environment variables RUN_BUILD_TESTS, RUN_UNIT_TESTS and -# RUN_INTEGRATION_TESTS are set to 0 (false) or 1 (true) accordingly. -# If --emit-metrics is passed, EMIT_METRICS will be set to 1. -# 3. Call the main() function passing $@ (without quotes). -# -# Running the script without parameters, or with the --all-tests -# flag, causes all tests to be executed, in the right order. -# Use the flags --build-tests, --unit-tests and --integration-tests -# to run a specific set of tests. The flag --emit-metrics is used -# to emit metrics when running the tests. +# This is a helper script for Knative presubmit test scripts. +# See README.md for instructions on how to use it. source $(dirname ${BASH_SOURCE})/library.sh @@ -65,6 +52,19 @@ function exit_if_presubmit_not_required() { function main() { exit_if_presubmit_not_required + # Show the version of the tools we're using + if (( IS_PROW )); then + # Disable gcloud update notifications + gcloud config set component_manager/disable_update_check true + header "Current test setup" + echo ">> gcloud SDK version" + gcloud version + echo ">> kubectl version" + kubectl version + echo ">> go version" + go version + fi + local all_parameters=$@ [[ -z $1 ]] && all_parameters="--all-tests" @@ -108,15 +108,29 @@ function main() { # Tests to be performed, in the right order if --all-tests is passed. - local result=0 + local failed=0 if (( RUN_BUILD_TESTS )); then - build_tests || result=1 + build_tests || failed=1 fi if (( RUN_UNIT_TESTS )); then - unit_tests || result=1 + unit_tests || failed=1 fi if (( RUN_INTEGRATION_TESTS )); then - integration_tests || result=1 + if function_exists pre_integration_tests; then + pre_integration_tests || failed=1 + fi + if (( ! failed )); then + if function_exists integration_tests; then + integration_tests || failed=1 + else + local options="" + (( EMIT_METRICS )) && options="--emit-metrics" + ./test/e2e-tests.sh ${options} || failed=1 + fi + fi + if (( ! failed )) && function_exists post_integration_tests; then + post_integration_tests || failed=1 + fi fi - exit ${result} + exit ${failed} } diff --git a/vendor/github.com/knative/test-infra/scripts/release.sh b/vendor/github.com/knative/test-infra/scripts/release.sh index abf89ddf418..3ddf02922be 100755 --- a/vendor/github.com/knative/test-infra/scripts/release.sh +++ b/vendor/github.com/knative/test-infra/scripts/release.sh @@ -14,27 +14,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -# This is a helper script for Knative release scripts. To use it: -# 1. Source this script. -# 2. Call the parse_flags() function passing $@ (without quotes). -# 3. Call the run_validation_tests() passing the script or executable that -# runs the release validation tests. -# 4. Write logic for the release process. Use the following boolean (0 is -# false, 1 is true) environment variables for the logic: -# SKIP_TESTS: true if --skip-tests is passed. This is handled automatically -# by the run_validation_tests() function. -# TAG_RELEASE: true if --tag-release is passed. In this case, the -# environment variable TAG will contain the release tag in the -# form vYYYYMMDD-. -# PUBLISH_RELEASE: true if --publish is passed. In this case, the environment -# variable KO_FLAGS will be updated with the -L option. -# SKIP_TESTS, TAG_RELEASE and PUBLISH_RELEASE default to false for safety. -# All environment variables above, except KO_FLAGS, are marked read-only once -# parse_flags() is called. +# This is a helper script for Knative release scripts. +# See README.md for instructions on how to use it. source $(dirname ${BASH_SOURCE})/library.sh # Simple banner for logging purposes. +# Parameters: $1 - message to display. function banner() { make_banner "@" "$1" } @@ -57,19 +43,35 @@ function tag_images_in_yaml() { # $3 - tag to apply (optional). function publish_yaml() { gsutil cp $1 gs://$2/latest/ - [[ -n $3 ]] && gsutil cp $1 gs://$2/previous/$3/ + [[ -n $3 ]] && gsutil cp $1 gs://$2/previous/$3/ || true } +# These are global environment variables. SKIP_TESTS=0 TAG_RELEASE=0 PUBLISH_RELEASE=0 +BRANCH_RELEASE=0 TAG="" -KO_FLAGS="-P -L" +RELEASE_VERSION="" +RELEASE_NOTES="" +RELEASE_BRANCH="" +KO_FLAGS="" + +function abort() { + echo "error: $@" + exit 1 +} # Parses flags and sets environment variables accordingly. function parse_flags() { + TAG="" + RELEASE_VERSION="" + RELEASE_NOTES="" + RELEASE_BRANCH="" + KO_FLAGS="-P -L" cd ${REPO_ROOT_DIR} - for parameter in $@; do + while [[ $# -ne 0 ]]; do + local parameter=$1 case $parameter in --skip-tests) SKIP_TESTS=1 ;; --tag-release) TAG_RELEASE=1 ;; @@ -83,17 +85,30 @@ function parse_flags() { PUBLISH_RELEASE=0 # Add -L to ko flags KO_FLAGS="-L ${KO_FLAGS}" + ;; + --version) shift + [[ $# -ge 1 ]] || abort "missing version after --version" + [[ $1 =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]] || abort "version format must be '[0-9].[0-9].[0-9]'" + RELEASE_VERSION=$1 ;; - *) - echo "error: unknown option ${parameter}" - exit 1 + --branch) + shift + [[ $# -ge 1 ]] || abort "missing branch after --commit" + [[ $1 =~ ^release-[0-9]+\.[0-9]+$ ]] || abort "branch name must be 'release-[0-9].[0-9]'" + RELEASE_BRANCH=$1 ;; + --release-notes) + shift + [[ $# -ge 1 ]] || abort "missing release notes file after --release-notes" + [[ ! -f "$1" ]] && abort "file $1 doesn't exist" + RELEASE_NOTES=$1 + ;; + *) abort "unknown option ${parameter}" ;; esac shift done - TAG="" if (( TAG_RELEASE )); then # Currently we're not considering the tags in refs/tags namespace. commit=$(git describe --always --dirty) @@ -101,10 +116,20 @@ function parse_flags() { TAG="v$(date +%Y%m%d)-${commit}" fi + if [[ -n "${RELEASE_VERSION}" ]]; then + TAG="v${RELEASE_VERSION}" + fi + + [[ -n "${RELEASE_VERSION}" ]] && (( PUBLISH_RELEASE )) && BRANCH_RELEASE=1 + readonly SKIP_TESTS readonly TAG_RELEASE readonly PUBLISH_RELEASE + readonly BRANCH_RELEASE readonly TAG + readonly RELEASE_VERSION + readonly RELEASE_NOTES + readonly RELEASE_BRANCH } # Run tests (unless --skip-tests was passed). Conveniently displays a banner indicating so. @@ -116,3 +141,35 @@ function run_validation_tests() { $1 fi } + +# Initialize everything (flags, workspace, etc) for a release. +function initialize() { + parse_flags $@ + # Checkout specific branch, if necessary + if (( BRANCH_RELEASE )); then + git checkout --track upstream/${RELEASE_BRANCH} || abort "cannot checkout branch ${RELEASE_BRANCH}" + fi +} + +# Create a new release on GitHub, also git tagging it (unless this is not a versioned release). +# Parameters: $1 - Module name (e.g., "Knative Serving"). +# $2 - YAML files to add to the release, space separated. +function branch_release() { + (( BRANCH_RELEASE )) || return 0 + local title="$1 release ${TAG}" + local yamls="$2" + local attach="--attach=${yamls// / --attach=}" + local description="$(mktemp)" + echo -e "${title}\n" > ${description} + if [[ -n "${RELEASE_NOTES}" ]]; then + cat ${RELEASE_NOTES} >> ${description} + fi + git tag -a ${TAG} -m "${title}" + git push $(git remote get-url upstream) tag ${TAG} + run_go_tool hub github.com/github/hub release create \ + --prerelease \ + ${attach} \ + --file=${description} \ + --commitish=${RELEASE_BRANCH} \ + ${TAG} +} diff --git a/vendor/github.com/markbates/inflect/LICENCE b/vendor/github.com/markbates/inflect/LICENCE new file mode 100644 index 00000000000..8a36b944a5e --- /dev/null +++ b/vendor/github.com/markbates/inflect/LICENCE @@ -0,0 +1,7 @@ +Copyright (c) 2011 Chris Farmiloe + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/markbates/inflect/helpers.go b/vendor/github.com/markbates/inflect/helpers.go new file mode 100644 index 00000000000..24050c70a04 --- /dev/null +++ b/vendor/github.com/markbates/inflect/helpers.go @@ -0,0 +1,19 @@ +package inflect + +//Helpers is a map of the helper names with its corresponding inflect function +var Helpers = map[string]interface{}{ + "asciffy": Asciify, + "camelize": Camelize, + "camelize_down_first": CamelizeDownFirst, + "capitalize": Capitalize, + "dasherize": Dasherize, + "humanize": Humanize, + "ordinalize": Ordinalize, + "parameterize": Parameterize, + "pluralize": Pluralize, + "pluralize_with_size": PluralizeWithSize, + "singularize": Singularize, + "tableize": Tableize, + "typeify": Typeify, + "underscore": Underscore, +} diff --git a/vendor/github.com/markbates/inflect/inflect.go b/vendor/github.com/markbates/inflect/inflect.go new file mode 100644 index 00000000000..9b6776c191c --- /dev/null +++ b/vendor/github.com/markbates/inflect/inflect.go @@ -0,0 +1,892 @@ +package inflect + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strconv" + "strings" + "unicode" + "unicode/utf8" +) + +// baseAcronyms comes from https://en.wikipedia.org/wiki/List_of_information_technology_acronymss +const baseAcronyms = `JSON,JWT,ID,UUID,SQL,ACK,ACL,ADSL,AES,ANSI,API,ARP,ATM,BGP,BSS,CAT,CCITT,CHAP,CIDR,CIR,CLI,CPE,CPU,CRC,CRT,CSMA,CMOS,DCE,DEC,DES,DHCP,DNS,DRAM,DSL,DSLAM,DTE,DMI,EHA,EIA,EIGRP,EOF,ESS,FCC,FCS,FDDI,FTP,GBIC,gbps,GEPOF,HDLC,HTTP,HTTPS,IANA,ICMP,IDF,IDS,IEEE,IETF,IMAP,IP,IPS,ISDN,ISP,kbps,LACP,LAN,LAPB,LAPF,LLC,MAC,MAN,Mbps,MC,MDF,MIB,MoCA,MPLS,MTU,NAC,NAT,NBMA,NIC,NRZ,NRZI,NVRAM,OSI,OSPF,OUI,PAP,PAT,PC,PIM,PIM,PCM,PDU,POP3,POP,POTS,PPP,PPTP,PTT,PVST,RADIUS,RAM,RARP,RFC,RIP,RLL,ROM,RSTP,RTP,RCP,SDLC,SFD,SFP,SLARP,SLIP,SMTP,SNA,SNAP,SNMP,SOF,SRAM,SSH,SSID,STP,SYN,TDM,TFTP,TIA,TOFU,UDP,URL,URI,USB,UTP,VC,VLAN,VLSM,VPN,W3C,WAN,WEP,WiFi,WPA,WWW` + +// Rule used by rulesets +type Rule struct { + suffix string + replacement string + exact bool +} + +// Ruleset a Ruleset is the config of pluralization rules +// you can extend the rules with the Add* methods +type Ruleset struct { + uncountables map[string]bool + plurals []*Rule + singulars []*Rule + humans []*Rule + acronyms []*Rule +} + +// NewRuleset creates a blank ruleset. Unless you are going to +// build your own rules from scratch you probably +// won't need this and can just use the defaultRuleset +// via the global inflect.* methods +func NewRuleset() *Ruleset { + rs := new(Ruleset) + rs.uncountables = make(map[string]bool) + rs.plurals = make([]*Rule, 0) + rs.singulars = make([]*Rule, 0) + rs.humans = make([]*Rule, 0) + rs.acronyms = make([]*Rule, 0) + return rs +} + +// NewDefaultRuleset creates a new ruleset and load it with the default +// set of common English pluralization rules +func NewDefaultRuleset() *Ruleset { + rs := NewRuleset() + rs.AddPlural("movie", "movies") + rs.AddPlural("s", "s") + rs.AddPlural("testis", "testes") + rs.AddPlural("axis", "axes") + rs.AddPlural("octopus", "octopi") + rs.AddPlural("virus", "viri") + rs.AddPlural("octopi", "octopi") + rs.AddPlural("viri", "viri") + rs.AddPlural("alias", "aliases") + rs.AddPlural("status", "statuses") + rs.AddPlural("Status", "Statuses") + rs.AddPlural("campus", "campuses") + rs.AddPlural("bus", "buses") + rs.AddPlural("buffalo", "buffaloes") + rs.AddPlural("tomato", "tomatoes") + rs.AddPlural("tum", "ta") + rs.AddPlural("ium", "ia") + rs.AddPlural("ta", "ta") + rs.AddPlural("ia", "ia") + rs.AddPlural("sis", "ses") + rs.AddPlural("lf", "lves") + rs.AddPlural("rf", "rves") + rs.AddPlural("afe", "aves") + rs.AddPlural("bfe", "bves") + rs.AddPlural("cfe", "cves") + rs.AddPlural("dfe", "dves") + rs.AddPlural("efe", "eves") + rs.AddPlural("gfe", "gves") + rs.AddPlural("hfe", "hves") + rs.AddPlural("ife", "ives") + rs.AddPlural("jfe", "jves") + rs.AddPlural("kfe", "kves") + rs.AddPlural("lfe", "lves") + rs.AddPlural("mfe", "mves") + rs.AddPlural("nfe", "nves") + rs.AddPlural("ofe", "oves") + rs.AddPlural("pfe", "pves") + rs.AddPlural("qfe", "qves") + rs.AddPlural("rfe", "rves") + rs.AddPlural("sfe", "sves") + rs.AddPlural("tfe", "tves") + rs.AddPlural("ufe", "uves") + rs.AddPlural("vfe", "vves") + rs.AddPlural("wfe", "wves") + rs.AddPlural("xfe", "xves") + rs.AddPlural("yfe", "yves") + rs.AddPlural("zfe", "zves") + rs.AddPlural("hive", "hives") + rs.AddPlural("quy", "quies") + rs.AddPlural("by", "bies") + rs.AddPlural("cy", "cies") + rs.AddPlural("dy", "dies") + rs.AddPlural("fy", "fies") + rs.AddPlural("gy", "gies") + rs.AddPlural("hy", "hies") + rs.AddPlural("jy", "jies") + rs.AddPlural("ky", "kies") + rs.AddPlural("ly", "lies") + rs.AddPlural("my", "mies") + rs.AddPlural("ny", "nies") + rs.AddPlural("py", "pies") + rs.AddPlural("qy", "qies") + rs.AddPlural("ry", "ries") + rs.AddPlural("sy", "sies") + rs.AddPlural("ty", "ties") + rs.AddPlural("vy", "vies") + rs.AddPlural("wy", "wies") + rs.AddPlural("xy", "xies") + rs.AddPlural("zy", "zies") + rs.AddPlural("x", "xes") + rs.AddPlural("ch", "ches") + rs.AddPlural("ss", "sses") + rs.AddPlural("sh", "shes") + rs.AddPlural("matrix", "matrices") + rs.AddPlural("vertix", "vertices") + rs.AddPlural("indix", "indices") + rs.AddPlural("matrex", "matrices") + rs.AddPlural("vertex", "vertices") + rs.AddPlural("index", "indices") + rs.AddPlural("mouse", "mice") + rs.AddPlural("louse", "lice") + rs.AddPlural("mice", "mice") + rs.AddPlural("lice", "lice") + rs.AddPlural("ress", "resses") + rs.AddPluralExact("ox", "oxen", true) + rs.AddPluralExact("oxen", "oxen", true) + rs.AddPluralExact("quiz", "quizzes", true) + rs.AddSingular("s", "") + rs.AddSingular("ss", "ss") + rs.AddSingular("news", "news") + rs.AddSingular("ta", "tum") + rs.AddSingular("ia", "ium") + rs.AddSingular("analyses", "analysis") + rs.AddSingular("bases", "basis") + rs.AddSingularExact("basis", "basis", true) + rs.AddSingular("diagnoses", "diagnosis") + rs.AddSingularExact("diagnosis", "diagnosis", true) + rs.AddSingular("parentheses", "parenthesis") + rs.AddSingular("prognoses", "prognosis") + rs.AddSingular("synopses", "synopsis") + rs.AddSingular("theses", "thesis") + rs.AddSingular("analyses", "analysis") + rs.AddSingularExact("analysis", "analysis", true) + rs.AddSingular("ovies", "ovie") + rs.AddSingular("aves", "afe") + rs.AddSingular("bves", "bfe") + rs.AddSingular("cves", "cfe") + rs.AddSingular("dves", "dfe") + rs.AddSingular("eves", "efe") + rs.AddSingular("gves", "gfe") + rs.AddSingular("hves", "hfe") + rs.AddSingular("ives", "ife") + rs.AddSingular("jves", "jfe") + rs.AddSingular("kves", "kfe") + rs.AddSingular("lves", "lfe") + rs.AddSingular("mves", "mfe") + rs.AddSingular("nves", "nfe") + rs.AddSingular("oves", "ofe") + rs.AddSingular("pves", "pfe") + rs.AddSingular("qves", "qfe") + rs.AddSingular("rves", "rfe") + rs.AddSingular("sves", "sfe") + rs.AddSingular("tves", "tfe") + rs.AddSingular("uves", "ufe") + rs.AddSingular("vves", "vfe") + rs.AddSingular("wves", "wfe") + rs.AddSingular("xves", "xfe") + rs.AddSingular("yves", "yfe") + rs.AddSingular("zves", "zfe") + rs.AddSingular("hives", "hive") + rs.AddSingular("tives", "tive") + rs.AddSingular("lves", "lf") + rs.AddSingular("rves", "rf") + rs.AddSingular("quies", "quy") + rs.AddSingular("bies", "by") + rs.AddSingular("cies", "cy") + rs.AddSingular("dies", "dy") + rs.AddSingular("fies", "fy") + rs.AddSingular("gies", "gy") + rs.AddSingular("hies", "hy") + rs.AddSingular("jies", "jy") + rs.AddSingular("kies", "ky") + rs.AddSingular("lies", "ly") + rs.AddSingular("mies", "my") + rs.AddSingular("nies", "ny") + rs.AddSingular("pies", "py") + rs.AddSingular("qies", "qy") + rs.AddSingular("ries", "ry") + rs.AddSingular("sies", "sy") + rs.AddSingular("ties", "ty") + // rs.AddSingular("vies", "vy") + rs.AddSingular("wies", "wy") + rs.AddSingular("xies", "xy") + rs.AddSingular("zies", "zy") + rs.AddSingular("series", "series") + rs.AddSingular("xes", "x") + rs.AddSingular("ches", "ch") + rs.AddSingular("sses", "ss") + rs.AddSingular("shes", "sh") + rs.AddSingular("mice", "mouse") + rs.AddSingular("lice", "louse") + rs.AddSingular("buses", "bus") + rs.AddSingularExact("bus", "bus", true) + rs.AddSingular("oes", "o") + rs.AddSingular("shoes", "shoe") + rs.AddSingular("crises", "crisis") + rs.AddSingularExact("crisis", "crisis", true) + rs.AddSingular("axes", "axis") + rs.AddSingularExact("axis", "axis", true) + rs.AddSingular("testes", "testis") + rs.AddSingularExact("testis", "testis", true) + rs.AddSingular("octopi", "octopus") + rs.AddSingularExact("octopus", "octopus", true) + rs.AddSingular("viri", "virus") + rs.AddSingularExact("virus", "virus", true) + rs.AddSingular("statuses", "status") + rs.AddSingular("Statuses", "Status") + rs.AddSingular("campuses", "campus") + rs.AddSingularExact("status", "status", true) + rs.AddSingularExact("Status", "Status", true) + rs.AddSingularExact("campus", "campus", true) + rs.AddSingular("aliases", "alias") + rs.AddSingularExact("alias", "alias", true) + rs.AddSingularExact("oxen", "ox", true) + rs.AddSingular("vertices", "vertex") + rs.AddSingular("indices", "index") + rs.AddSingular("matrices", "matrix") + rs.AddSingularExact("quizzes", "quiz", true) + rs.AddSingular("databases", "database") + rs.AddSingular("resses", "ress") + rs.AddSingular("ress", "ress") + rs.AddIrregular("person", "people") + rs.AddIrregular("man", "men") + rs.AddIrregular("child", "children") + rs.AddIrregular("sex", "sexes") + rs.AddIrregular("move", "moves") + rs.AddIrregular("zombie", "zombies") + rs.AddIrregular("Status", "Statuses") + rs.AddIrregular("status", "statuses") + rs.AddIrregular("campus", "campuses") + rs.AddIrregular("human", "humans") + rs.AddUncountable("equipment") + rs.AddUncountable("information") + rs.AddUncountable("rice") + rs.AddUncountable("money") + rs.AddUncountable("species") + rs.AddUncountable("series") + rs.AddUncountable("fish") + rs.AddUncountable("sheep") + rs.AddUncountable("jeans") + rs.AddUncountable("police") + + acronyms := strings.Split(baseAcronyms, ",") + for _, acr := range acronyms { + rs.AddAcronym(acr) + } + + return rs +} + +// Uncountables returns a map of uncountables in the ruleset +func (rs *Ruleset) Uncountables() map[string]bool { + return rs.uncountables +} + +// AddPlural add a pluralization rule +func (rs *Ruleset) AddPlural(suffix, replacement string) { + rs.AddPluralExact(suffix, replacement, false) +} + +// AddPluralExact add a pluralization rule with full string match +func (rs *Ruleset) AddPluralExact(suffix, replacement string, exact bool) { + // remove uncountable + delete(rs.uncountables, suffix) + // create rule + r := new(Rule) + r.suffix = suffix + r.replacement = replacement + r.exact = exact + // prepend + rs.plurals = append([]*Rule{r}, rs.plurals...) +} + +// AddSingular add a singular rule +func (rs *Ruleset) AddSingular(suffix, replacement string) { + rs.AddSingularExact(suffix, replacement, false) +} + +// AddSingularExact same as AddSingular but you can set `exact` to force +// a full string match +func (rs *Ruleset) AddSingularExact(suffix, replacement string, exact bool) { + // remove from uncountable + delete(rs.uncountables, suffix) + // create rule + r := new(Rule) + r.suffix = suffix + r.replacement = replacement + r.exact = exact + rs.singulars = append([]*Rule{r}, rs.singulars...) +} + +// AddHuman Human rules are applied by humanize to show more friendly +// versions of words +func (rs *Ruleset) AddHuman(suffix, replacement string) { + r := new(Rule) + r.suffix = suffix + r.replacement = replacement + rs.humans = append([]*Rule{r}, rs.humans...) +} + +// AddIrregular Add any inconsistent pluralizing/singularizing rules +// to the set here. +func (rs *Ruleset) AddIrregular(singular, plural string) { + delete(rs.uncountables, singular) + delete(rs.uncountables, plural) + rs.AddPlural(singular, plural) + rs.AddPlural(plural, plural) + rs.AddSingular(plural, singular) +} + +// AddAcronym if you use acronym you may need to add them to the ruleset +// to prevent Underscored words of things like "HTML" coming out +// as "h_t_m_l" +func (rs *Ruleset) AddAcronym(word string) { + r := new(Rule) + r.suffix = word + r.replacement = rs.Titleize(strings.ToLower(word)) + rs.acronyms = append(rs.acronyms, r) +} + +// AddUncountable add a word to this ruleset that has the same singular and plural form +// for example: "rice" +func (rs *Ruleset) AddUncountable(word string) { + rs.uncountables[strings.ToLower(word)] = true +} + +func (rs *Ruleset) isUncountable(word string) bool { + // handle multiple words by using the last one + words := strings.Split(word, " ") + if _, exists := rs.uncountables[strings.ToLower(words[len(words)-1])]; exists { + return true + } + return false +} + +//isAcronym returns if a word is acronym or not. +func (rs *Ruleset) isAcronym(word string) bool { + for _, rule := range rs.acronyms { + if strings.ToUpper(rule.suffix) == strings.ToUpper(word) { + return true + } + } + + return false +} + +//PluralizeWithSize pluralize with taking number into account +func (rs *Ruleset) PluralizeWithSize(word string, size int) string { + if size == 1 { + return rs.Singularize(word) + } + return rs.Pluralize(word) +} + +// Pluralize returns the plural form of a singular word +func (rs *Ruleset) Pluralize(word string) string { + if len(word) == 0 { + return word + } + lWord := strings.ToLower(word) + if rs.isUncountable(lWord) { + return word + } + + var candidate string + for _, rule := range rs.plurals { + if rule.exact { + if lWord == rule.suffix { + // Capitalized word + if lWord[0] != word[0] && lWord[1:] == word[1:] { + return rs.Capitalize(rule.replacement) + } + return rule.replacement + } + continue + } + + if strings.EqualFold(word, rule.suffix) { + candidate = rule.replacement + } + + if strings.HasSuffix(word, rule.suffix) { + return replaceLast(word, rule.suffix, rule.replacement) + } + } + + if candidate != "" { + return candidate + } + return word + "s" +} + +//Singularize returns the singular form of a plural word +func (rs *Ruleset) Singularize(word string) string { + if len(word) <= 1 { + return word + } + lWord := strings.ToLower(word) + if rs.isUncountable(lWord) { + return word + } + + var candidate string + + for _, rule := range rs.singulars { + if rule.exact { + if lWord == rule.suffix { + // Capitalized word + if lWord[0] != word[0] && lWord[1:] == word[1:] { + return rs.Capitalize(rule.replacement) + } + return rule.replacement + } + continue + } + + if strings.EqualFold(word, rule.suffix) { + candidate = rule.replacement + } + + if strings.HasSuffix(word, rule.suffix) { + return replaceLast(word, rule.suffix, rule.replacement) + } + } + + if candidate != "" { + return candidate + } + + return word +} + +//Capitalize uppercase first character +func (rs *Ruleset) Capitalize(word string) string { + if rs.isAcronym(word) { + return strings.ToUpper(word) + } + return strings.ToUpper(word[:1]) + word[1:] +} + +//Camelize "dino_party" -> "DinoParty" +func (rs *Ruleset) Camelize(word string) string { + if rs.isAcronym(word) { + return strings.ToUpper(word) + } + words := splitAtCaseChangeWithTitlecase(word) + return strings.Join(words, "") +} + +//CamelizeDownFirst same as Camelcase but with first letter downcased +func (rs *Ruleset) CamelizeDownFirst(word string) string { + word = Camelize(word) + return strings.ToLower(word[:1]) + word[1:] +} + +//Titleize Capitalize every word in sentence "hello there" -> "Hello There" +func (rs *Ruleset) Titleize(word string) string { + words := splitAtCaseChangeWithTitlecase(word) + result := strings.Join(words, " ") + + var acronymWords []string + for index, word := range words { + if len(word) == 1 { + acronymWords = append(acronymWords, word) + } + + if len(word) > 1 || index == len(words)-1 || len(acronymWords) > 1 { + acronym := strings.Join(acronymWords, "") + if !rs.isAcronym(acronym) { + acronymWords = acronymWords[:len(acronymWords)] + continue + } + + result = strings.Replace(result, strings.Join(acronymWords, " "), acronym, 1) + acronymWords = []string{} + } + } + + return result +} + +func (rs *Ruleset) safeCaseAcronyms(word string) string { + // convert an acronym like HTML into Html + for _, rule := range rs.acronyms { + word = strings.Replace(word, rule.suffix, rule.replacement, -1) + } + return word +} + +func (rs *Ruleset) separatedWords(word, sep string) string { + word = rs.safeCaseAcronyms(word) + words := splitAtCaseChange(word) + return strings.Join(words, sep) +} + +//Underscore lowercase underscore version "BigBen" -> "big_ben" +func (rs *Ruleset) Underscore(word string) string { + return rs.separatedWords(word, "_") +} + +//Humanize First letter of sentence capitalized +// Uses custom friendly replacements via AddHuman() +func (rs *Ruleset) Humanize(word string) string { + word = replaceLast(word, "_id", "") // strip foreign key kinds + // replace and strings in humans list + for _, rule := range rs.humans { + word = strings.Replace(word, rule.suffix, rule.replacement, -1) + } + sentence := rs.separatedWords(word, " ") + + r, n := utf8.DecodeRuneInString(sentence) + return string(unicode.ToUpper(r)) + sentence[n:] +} + +//ForeignKey an underscored foreign key name "Person" -> "person_id" +func (rs *Ruleset) ForeignKey(word string) string { + return rs.Underscore(rs.Singularize(word)) + "_id" +} + +//ForeignKeyCondensed a foreign key (with an underscore) "Person" -> "personid" +func (rs *Ruleset) ForeignKeyCondensed(word string) string { + return rs.Underscore(word) + "id" +} + +//Tableize Rails style pluralized table names: "SuperPerson" -> "super_people" +func (rs *Ruleset) Tableize(word string) string { + return rs.Pluralize(rs.Underscore(rs.Typeify(word))) +} + +var notUrlSafe *regexp.Regexp = regexp.MustCompile(`[^\w\d\-_ ]`) + +//Parameterize param safe dasherized names like "my-param" +func (rs *Ruleset) Parameterize(word string) string { + return ParameterizeJoin(word, "-") +} + +//ParameterizeJoin param safe dasherized names with custom separator +func (rs *Ruleset) ParameterizeJoin(word, sep string) string { + word = strings.ToLower(word) + word = rs.Asciify(word) + word = notUrlSafe.ReplaceAllString(word, "") + word = strings.Replace(word, " ", sep, -1) + if len(sep) > 0 { + squash, err := regexp.Compile(sep + "+") + if err == nil { + word = squash.ReplaceAllString(word, sep) + } + } + word = strings.Trim(word, sep+" ") + return word +} + +var lookalikes = map[string]*regexp.Regexp{ + "A": regexp.MustCompile(`À|Á|Â|Ã|Ä|Å`), + "AE": regexp.MustCompile(`Æ`), + "C": regexp.MustCompile(`Ç`), + "E": regexp.MustCompile(`È|É|Ê|Ë`), + "G": regexp.MustCompile(`Ğ`), + "I": regexp.MustCompile(`Ì|Í|Î|Ï|İ`), + "N": regexp.MustCompile(`Ñ`), + "O": regexp.MustCompile(`Ò|Ó|Ô|Õ|Ö|Ø`), + "S": regexp.MustCompile(`Ş`), + "U": regexp.MustCompile(`Ù|Ú|Û|Ü`), + "Y": regexp.MustCompile(`Ý`), + "ss": regexp.MustCompile(`ß`), + "a": regexp.MustCompile(`à|á|â|ã|ä|å`), + "ae": regexp.MustCompile(`æ`), + "c": regexp.MustCompile(`ç`), + "e": regexp.MustCompile(`è|é|ê|ë`), + "g": regexp.MustCompile(`ğ`), + "i": regexp.MustCompile(`ì|í|î|ï|ı`), + "n": regexp.MustCompile(`ñ`), + "o": regexp.MustCompile(`ò|ó|ô|õ|ö|ø`), + "s": regexp.MustCompile(`ş`), + "u": regexp.MustCompile(`ù|ú|û|ü|ũ|ū|ŭ|ů|ű|ų`), + "y": regexp.MustCompile(`ý|ÿ`), +} + +//Asciify transforms Latin characters like é -> e +func (rs *Ruleset) Asciify(word string) string { + for repl, regex := range lookalikes { + word = regex.ReplaceAllString(word, repl) + } + return word +} + +var tablePrefix = regexp.MustCompile(`^[^.]*\.`) + +//Typeify "something_like_this" -> "SomethingLikeThis" +func (rs *Ruleset) Typeify(word string) string { + word = tablePrefix.ReplaceAllString(word, "") + return rs.Camelize(rs.Singularize(word)) +} + +//Dasherize "SomeText" -> "some-text" +func (rs *Ruleset) Dasherize(word string) string { + return rs.separatedWords(word, "-") +} + +//Ordinalize "1031" -> "1031st" +func (rs *Ruleset) Ordinalize(str string) string { + number, err := strconv.Atoi(str) + if err != nil { + return str + } + switch abs(number) % 100 { + case 11, 12, 13: + return fmt.Sprintf("%dth", number) + default: + switch abs(number) % 10 { + case 1: + return fmt.Sprintf("%dst", number) + case 2: + return fmt.Sprintf("%dnd", number) + case 3: + return fmt.Sprintf("%drd", number) + } + } + return fmt.Sprintf("%dth", number) +} + +//ForeignKeyToAttribute returns the attribute name from the foreign key +func (rs *Ruleset) ForeignKeyToAttribute(str string) string { + w := rs.Camelize(str) + if strings.HasSuffix(w, "Id") { + return strings.TrimSuffix(w, "Id") + "ID" + } + return w +} + +//LoadReader loads rules from io.Reader param +func (rs *Ruleset) LoadReader(r io.Reader) error { + m := map[string]string{} + err := json.NewDecoder(r).Decode(&m) + if err != nil { + return fmt.Errorf("could not decode inflection JSON from reader: %s", err) + } + for s, p := range m { + defaultRuleset.AddIrregular(s, p) + } + return nil +} + +///////////////////////////////////////// +// the default global ruleset +////////////////////////////////////////// + +var defaultRuleset *Ruleset + +//LoadReader loads rules from io.Reader param +func LoadReader(r io.Reader) error { + return defaultRuleset.LoadReader(r) +} + +func init() { + defaultRuleset = NewDefaultRuleset() + + pwd, _ := os.Getwd() + cfg := filepath.Join(pwd, "inflections.json") + if p := os.Getenv("INFLECT_PATH"); p != "" { + cfg = p + } + if _, err := os.Stat(cfg); err == nil { + b, err := ioutil.ReadFile(cfg) + if err != nil { + fmt.Printf("could not read inflection file %s (%s)\n", cfg, err) + return + } + if err = defaultRuleset.LoadReader(bytes.NewReader(b)); err != nil { + fmt.Println(err) + } + } +} + +//Uncountables returns a list of uncountables rules +func Uncountables() map[string]bool { + return defaultRuleset.Uncountables() +} + +//AddPlural adds plural to the ruleset +func AddPlural(suffix, replacement string) { + defaultRuleset.AddPlural(suffix, replacement) +} + +//AddSingular adds singular to the ruleset +func AddSingular(suffix, replacement string) { + defaultRuleset.AddSingular(suffix, replacement) +} + +//AddHuman adds human +func AddHuman(suffix, replacement string) { + defaultRuleset.AddHuman(suffix, replacement) +} + +func AddIrregular(singular, plural string) { + defaultRuleset.AddIrregular(singular, plural) +} + +func AddAcronym(word string) { + defaultRuleset.AddAcronym(word) +} + +func AddUncountable(word string) { + defaultRuleset.AddUncountable(word) +} + +func Pluralize(word string) string { + return defaultRuleset.Pluralize(word) +} + +func PluralizeWithSize(word string, size int) string { + return defaultRuleset.PluralizeWithSize(word, size) +} + +func Singularize(word string) string { + return defaultRuleset.Singularize(word) +} + +func Capitalize(word string) string { + return defaultRuleset.Capitalize(word) +} + +func Camelize(word string) string { + return defaultRuleset.Camelize(word) +} + +func CamelizeDownFirst(word string) string { + return defaultRuleset.CamelizeDownFirst(word) +} + +func Titleize(word string) string { + return defaultRuleset.Titleize(word) +} + +func Underscore(word string) string { + return defaultRuleset.Underscore(word) +} + +func Humanize(word string) string { + return defaultRuleset.Humanize(word) +} + +func ForeignKey(word string) string { + return defaultRuleset.ForeignKey(word) +} + +func ForeignKeyCondensed(word string) string { + return defaultRuleset.ForeignKeyCondensed(word) +} + +func Tableize(word string) string { + return defaultRuleset.Tableize(word) +} + +func Parameterize(word string) string { + return defaultRuleset.Parameterize(word) +} + +func ParameterizeJoin(word, sep string) string { + return defaultRuleset.ParameterizeJoin(word, sep) +} + +func Typeify(word string) string { + return defaultRuleset.Typeify(word) +} + +func Dasherize(word string) string { + return defaultRuleset.Dasherize(word) +} + +func Ordinalize(word string) string { + return defaultRuleset.Ordinalize(word) +} + +func Asciify(word string) string { + return defaultRuleset.Asciify(word) +} + +func ForeignKeyToAttribute(word string) string { + return defaultRuleset.ForeignKeyToAttribute(word) +} + +// helper funcs + +func reverse(s string) string { + o := make([]rune, utf8.RuneCountInString(s)) + i := len(o) + for _, c := range s { + i-- + o[i] = c + } + return string(o) +} + +func isSpacerChar(c rune) bool { + switch { + case c == rune("_"[0]): + return true + case c == rune(" "[0]): + return true + case c == rune(":"[0]): + return true + case c == rune("-"[0]): + return true + } + return false +} + +func splitAtCaseChange(s string) []string { + words := make([]string, 0) + word := make([]rune, 0) + for _, c := range s { + spacer := isSpacerChar(c) + if len(word) > 0 { + if unicode.IsUpper(c) || spacer { + words = append(words, string(word)) + word = make([]rune, 0) + } + } + if !spacer { + word = append(word, unicode.ToLower(c)) + } + } + words = append(words, string(word)) + return words +} + +func splitAtCaseChangeWithTitlecase(s string) []string { + words := make([]string, 0) + word := make([]rune, 0) + + for _, c := range s { + spacer := isSpacerChar(c) + if len(word) > 0 { + if unicode.IsUpper(c) || spacer { + words = append(words, string(word)) + word = make([]rune, 0) + } + } + if !spacer { + if len(word) > 0 { + word = append(word, unicode.ToLower(c)) + } else { + word = append(word, unicode.ToUpper(c)) + } + } + } + + words = append(words, string(word)) + return words +} + +func replaceLast(s, match, repl string) string { + // reverse strings + srev := reverse(s) + mrev := reverse(match) + rrev := reverse(repl) + // match first and reverse back + return reverse(strings.Replace(srev, mrev, rrev, 1)) +} + +func abs(x int) int { + if x < 0 { + return -x + } + return x +} diff --git a/vendor/github.com/markbates/inflect/name.go b/vendor/github.com/markbates/inflect/name.go new file mode 100644 index 00000000000..e6863e28a68 --- /dev/null +++ b/vendor/github.com/markbates/inflect/name.go @@ -0,0 +1,163 @@ +package inflect + +import ( + "fmt" + "path/filepath" + "strings" + + "github.com/gobuffalo/envy" +) + +// Name is a string that represents the "name" of a thing, like an app, model, etc... +type Name string + +// Title version of a name. ie. "foo_bar" => "Foo Bar" +func (n Name) Title() string { + x := strings.Split(string(n), "/") + for i, s := range x { + x[i] = Titleize(s) + } + + return strings.Join(x, " ") +} + +// Underscore version of a name. ie. "FooBar" => "foo_bar" +func (n Name) Underscore() string { + w := string(n) + if strings.ToUpper(w) == w { + return strings.ToLower(w) + } + return Underscore(w) +} + +// Plural version of a name +func (n Name) Plural() string { + return Pluralize(string(n)) +} + +// Singular version of a name +func (n Name) Singular() string { + return Singularize(string(n)) +} + +// Camel version of a name +func (n Name) Camel() string { + c := Camelize(string(n)) + if strings.HasSuffix(c, "Id") { + c = strings.TrimSuffix(c, "Id") + c += "ID" + } + return c +} + +// Model version of a name. ie. "user" => "User" +func (n Name) Model() string { + x := strings.Split(string(n), "/") + for i, s := range x { + x[i] = Camelize(Singularize(s)) + } + + return strings.Join(x, "") +} + +// Resource version of a name +func (n Name) Resource() string { + name := n.Underscore() + x := strings.FieldsFunc(name, func(r rune) bool { + return r == '_' || r == '/' + }) + + for i, w := range x { + if i == len(x)-1 { + x[i] = Camelize(Pluralize(strings.ToLower(w))) + continue + } + + x[i] = Camelize(w) + } + + return strings.Join(x, "") +} + +// ModelPlural version of a name. ie. "user" => "Users" +func (n Name) ModelPlural() string { + return Camelize(Pluralize(n.Model())) +} + +// File version of a name +func (n Name) File() string { + return Underscore(Camelize(string(n))) +} + +// Table version of a name +func (n Name) Table() string { + return Underscore(Pluralize(string(n))) +} + +// UnderSingular version of a name +func (n Name) UnderSingular() string { + return Underscore(Singularize(string(n))) +} + +// PluralCamel version of a name +func (n Name) PluralCamel() string { + return Pluralize(Camelize(string(n))) +} + +// PluralUnder version of a name +func (n Name) PluralUnder() string { + return Pluralize(Underscore(string(n))) +} + +// URL version of a name +func (n Name) URL() string { + return n.PluralUnder() +} + +// CamelSingular version of a name +func (n Name) CamelSingular() string { + return Camelize(Singularize(string(n))) +} + +// VarCaseSingular version of a name. ie. "FooBar" => "fooBar" +func (n Name) VarCaseSingular() string { + return CamelizeDownFirst(Singularize(Underscore(n.Resource()))) +} + +// VarCasePlural version of a name. ie. "FooBar" => "fooBar" +func (n Name) VarCasePlural() string { + return CamelizeDownFirst(n.Resource()) +} + +// Lower case version of a string +func (n Name) Lower() string { + return strings.ToLower(string(n)) +} + +// ParamID returns foo_bar_id +func (n Name) ParamID() string { + return fmt.Sprintf("%s_id", strings.Replace(n.UnderSingular(), "/", "_", -1)) +} + +// Package returns go package +func (n Name) Package() string { + key := string(n) + + for _, gp := range envy.GoPaths() { + key = strings.TrimPrefix(key, filepath.Join(gp, "src")) + key = strings.TrimPrefix(key, gp) + } + key = strings.TrimPrefix(key, string(filepath.Separator)) + + key = strings.Replace(key, "\\", "/", -1) + return key +} + +// Char returns first character in lower case, this is useful for methods inside a struct. +func (n Name) Char() string { + return strings.ToLower(string(n[0])) +} + +func (n Name) String() string { + return string(n) +} diff --git a/vendor/github.com/markbates/inflect/version.go b/vendor/github.com/markbates/inflect/version.go new file mode 100644 index 00000000000..a1674498419 --- /dev/null +++ b/vendor/github.com/markbates/inflect/version.go @@ -0,0 +1,3 @@ +package inflect + +const Version = "v1.0.4" From fbb9a6b54b2c117efd48d9e12c55c272cdc16644 Mon Sep 17 00:00:00 2001 From: Matthias Wessendorf Date: Wed, 31 Oct 2018 13:15:33 +0100 Subject: [PATCH 11/54] use the full name for the clusterchannelprovisioner kind CRD file (#569) --- ...clusterprovisioner.yaml => 300-clusterchannelprovisioner.yaml} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename config/{300-clusterprovisioner.yaml => 300-clusterchannelprovisioner.yaml} (100%) diff --git a/config/300-clusterprovisioner.yaml b/config/300-clusterchannelprovisioner.yaml similarity index 100% rename from config/300-clusterprovisioner.yaml rename to config/300-clusterchannelprovisioner.yaml From 9761d89af4a6e09ebfd0ab5bb748c274172ca98e Mon Sep 17 00:00:00 2001 From: Grant Rodgers Date: Wed, 31 Oct 2018 07:03:35 -0700 Subject: [PATCH 12/54] Add Channels with no subscribers to configmap (#568) If a channel has no subscribers, it should still be in the configmap. This allows the dispatcher to respond with a 202 to a publish request (then drop the published event). --- .../eventing/inmemory/channel/reconcile.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 67b1b03ee91..1ea43ceffb5 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -385,16 +385,16 @@ func (r *reconciler) createNewConfigMap(data map[string]string) *corev1.ConfigMa func multiChannelFanoutConfig(channels []eventingv1alpha1.Channel) *multichannelfanout.Config { cc := make([]multichannelfanout.ChannelConfig, 0) for _, c := range channels { - channelable := c.Spec.Channelable - if channelable != nil { - cc = append(cc, multichannelfanout.ChannelConfig{ - Namespace: c.Namespace, - Name: c.Name, - FanoutConfig: fanout.Config{ - Subscriptions: c.Spec.Channelable.Subscribers, - }, - }) + channelConfig := multichannelfanout.ChannelConfig{ + Namespace: c.Namespace, + Name: c.Name, + } + if c.Spec.Channelable != nil { + channelConfig.FanoutConfig = fanout.Config{ + Subscriptions: c.Spec.Channelable.Subscribers, + } } + cc = append(cc, channelConfig) } return &multichannelfanout.Config{ ChannelConfigs: cc, From cb510fce32bb82cfcdaeb782ac9ab8debf0281e1 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Wed, 31 Oct 2018 07:52:34 -0700 Subject: [PATCH 13/54] Add a unit test to prevent regressions of https://github.com/knative/eventing/pull/568. (#570) --- .../inmemory/channel/reconcile_test.go | 46 +++++++++++++++++-- 1 file changed, 43 insertions(+), 3 deletions(-) diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index f3a3bda8df0..90d2929e884 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -423,7 +423,47 @@ func TestReconcile(t *testing.T) { // not string equality, so it can't be done in WantPresent. Instead, we verify // during the update call, swapping out the data and WantPresent with that inserted // data. - MockUpdates: verifyConfigMapData(), + MockUpdates: verifyConfigMapData(channelsConfig), + }, + WantPresent: []runtime.Object{ + makeReadyChannel(), + makeK8sService(), + makeVirtualService(), + makeConfigMapWithVerifyConfigMapData(), + }, + }, + { + Name: "Channel reconcile successful - Channel has no subscribers", + InitialState: []runtime.Object{ + makeChannel(), + makeConfigMap(), + }, + Mocks: controllertesting.Mocks{ + MockLists: (&paginatedChannelsListStruct{channels: []eventingv1alpha1.Channel{ + { + ObjectMeta: metav1.ObjectMeta{ + Namespace: "high-consul", + Name: "duarte", + }, + Spec: eventingv1alpha1.ChannelSpec{ + Provisioner: &corev1.ObjectReference{ + Name: ccpName, + }, + }, + }, + }}).MockLists(), + // This is more accurate to be in WantPresent, but we need to check JSON equality, + // not string equality, so it can't be done in WantPresent. Instead, we verify + // during the update call, swapping out the data and WantPresent with that inserted + // data. + MockUpdates: verifyConfigMapData(multichannelfanout.Config{ + ChannelConfigs: []multichannelfanout.ChannelConfig{ + { + Namespace: "high-consul", + Name: "duarte", + }, + }, + }), }, WantPresent: []runtime.Object{ makeReadyChannel(), @@ -777,7 +817,7 @@ func (p *paginatedChannelsListStruct) MockLists() []controllertesting.MockList { } } -func verifyConfigMapData() []controllertesting.MockUpdate { +func verifyConfigMapData(expected multichannelfanout.Config) []controllertesting.MockUpdate { return []controllertesting.MockUpdate{ func(innerClient client.Client, ctx context.Context, obj runtime.Object) (controllertesting.MockHandled, error) { if cm, ok := obj.(*corev1.ConfigMap); ok { @@ -788,7 +828,7 @@ func verifyConfigMapData() []controllertesting.MockUpdate { return controllertesting.Handled, fmt.Errorf("test is unable to unmarshal ConfigMap data: %v", err) } - if diff := cmp.Diff(c, channelsConfig); diff != "" { + if diff := cmp.Diff(c, expected); diff != "" { return controllertesting.Handled, fmt.Errorf("test got unwanted ChannelsConfig (-want +got) %s", diff) } From 8589a29daeb6048af300694895c5bf93f1b96909 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Thu, 1 Nov 2018 17:56:34 -0400 Subject: [PATCH 14/54] Subscription names (#563) * Rename SubscriptionSpec.{From -> Channel} * Rename SubscriptionSpec.{Call -> Subscriber} ChannelSubscriberSpec.{CallableURI -> SubscriberURI} SubscriptionStatusPhysicalSubscription.{CallURI -> SubscriberURI} * Rename EndpointSpec -> SubscriberSpec SubscriberSpec.{TargetRef -> Ref} * Reanme SubscriptionSpec.{Result -> ReplyTo} ChannelSubscriberSpec.{SinkableURI -> ReplyToURI} SubscriptionStatusPhysicalSubscription.{ResultURI -> ReplyToURI} ResultStrategy -> ReplyStrategy ReplyStrategy.{Target -> Channel} * Rename SubscriptionSpec.{ReplyTo -> Reply} --- docs/spec/spec.md | 52 +-- pkg/apis/duck/v1alpha1/channelable_types.go | 8 +- .../eventing/v1alpha1/channel_validation.go | 4 +- .../v1alpha1/channel_validation_test.go | 14 +- .../eventing/v1alpha1/subscription_types.go | 68 ++-- .../v1alpha1/subscription_validation.go | 88 ++--- .../v1alpha1/subscription_validation_test.go | 352 +++++++++--------- .../v1alpha1/zz_generated.deepcopy.go | 56 +-- pkg/buses/message_dispatcher.go | 14 +- pkg/buses/message_dispatcher_test.go | 13 +- .../inmemory/channel/reconcile_test.go | 22 +- .../eventing/subscription/reconcile.go | 88 ++--- .../eventing/subscription/reconcile_test.go | 120 +++--- .../filesystem/filesystem_watcher_test.go | 32 +- pkg/sidecar/configmap/parse_test.go | 32 +- pkg/sidecar/configmap/watcher/watcher_test.go | 8 +- pkg/sidecar/fanout/fanout_handler.go | 2 +- pkg/sidecar/fanout/fanout_handler_test.go | 68 ++-- .../multi_channel_fanout_handler_test.go | 22 +- pkg/sidecar/multichannelfanout/parse_test.go | 24 +- pkg/sidecar/swappable/swappable_test.go | 14 +- 21 files changed, 545 insertions(+), 556 deletions(-) diff --git a/docs/spec/spec.md b/docs/spec/spec.md index 3566b45f7e3..c4f0abe9d03 100644 --- a/docs/spec/spec.md +++ b/docs/spec/spec.md @@ -76,15 +76,15 @@ _Describes a linkage between a Channel and a Targetable and/or Sinkable._ #### Spec -| Field | Type | Description | Constraints | -| ------------------ | -------------- | ---------------------------------------------------------------------------- | ------------------ | -| from\* | ObjectRef | The originating _Subscribable_ for the link. | Must be a Channel. | -| call1 | EndpointSpec | Optional processing on the event. The result of call will be sent to result. | | -| result1 | ResultStrategy | The continuation for the link. | | +| Field | Type | Description | Constraints | +| ---------------------- | -------------- | --------------------------------------------------------------------------------- | ------------------ | +| channel\* | ObjectRef | The originating _Subscribable_ for the link. | Must be a Channel. | +| subscriber1 | SubscriberSpec | Optional processing on the event. The result of subscriber will be sent to reply. | | +| reply1 | ReplyStrategy | The continuation for the link. | | \*: Required -1: At Least One(call, result) +1: At Least One(subscriber, reply) #### Metadata @@ -97,7 +97,7 @@ _Describes a linkage between a Channel and a Targetable and/or Sinkable._ - **Ready.** - **FromReady.** -- **Resolved.** True if `from`, `call`, and `result` all resolve into valid object references which implement the appropriate spec. +- **Resolved.** True if `channel`, `subscriber`, and `reply` all resolve into valid object references which implement the appropriate spec. #### Events @@ -106,11 +106,11 @@ _Describes a linkage between a Channel and a Targetable and/or Sinkable._ ### Life Cycle -| Action | Reactions | Constraints | -| ------ | ------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | -| Create | The subscription controller adds the resolved URIs of `call` and `result` to the `subscribers` field in the `from` _Subscribable_ resource. | | -| Update | | | -| Delete | | | +| Action | Reactions | Constraints | +| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| Create | The subscription controller adds the resolved URIs of `subscriber` and `reply` to the `subscribers` field in the `channel` _Subscribable_ resource. | | +| Update | | | +| Delete | | | --- @@ -150,26 +150,26 @@ or a Channel system that receives and delivers events._ ## Shared Object Schema -### EndpointSpec +### SubscriberSpec -| Field | Type | Description | Constraints | -| --------------------- | --------------- | ----------- | -------------------------- | -| targetRef1 | ObjectReference | | Must adhere to Targetable. | -| dnsName1 | String | | | +| Field | Type | Description | Constraints | +| ------------------- | --------------- | ----------- | -------------------------- | +| ref1 | ObjectReference | | Must adhere to Targetable. | +| dnsName1 | String | | | -1: One of (targetRef, dnsName), Required. +1: One of (ref, dnsName), Required. ### ChannelSubscriberSpec -| Field | Type | Description | Constraints | -| ----------- | ------ | -------------------------------------------- | -------------- | -| callableURI | String | The URI name of the endpoint for the call. | Must be a URL. | -| sinkableURI | String | The URI name of the endpoint for the result. | Must be a URL. | +| Field | Type | Description | Constraints | +| ------------- | ------ | ------------------------------------------------ | -------------- | +| subscriberURI | String | The URI name of the endpoint for the subscriber. | Must be a URL. | +| replyURI | String | The URI name of the endpoint for the reply. | Must be a URL. | -### ResultStrategy +### ReplyStrategy -| Field | Type | Description | Constraints | -| -------- | --------- | -------------------------------------- | ------------------ | -| target\* | ObjectRef | The continuation Channel for the link. | Must be a Channel. | +| Field | Type | Description | Constraints | +| --------- | --------- | -------------------------------------- | ------------------ | +| channel\* | ObjectRef | The continuation Channel for the link. | Must be a Channel. | \*: Required diff --git a/pkg/apis/duck/v1alpha1/channelable_types.go b/pkg/apis/duck/v1alpha1/channelable_types.go index ab33f4db72b..f208377aa81 100644 --- a/pkg/apis/duck/v1alpha1/channelable_types.go +++ b/pkg/apis/duck/v1alpha1/channelable_types.go @@ -31,14 +31,14 @@ type Channelable struct { } // ChannelSubscriberSpec defines a single subscriber to a Channel. -// CallableURI is the endpoint for the call -// SinkableURI is the endpoint for the result +// SubscriberURI is the endpoint for the subscriber +// ReplyURI is the endpoint for the reply // At least one of them must be present type ChannelSubscriberSpec struct { // +optional - CallableURI string `json:"callableURI,omitempty"` + SubscriberURI string `json:"subscriberURI,omitempty"` // +optional - SinkableURI string `json:"sinkableURI,omitempty"` + ReplyURI string `json:"replyURI,omitempty"` } // DuckChannel is a skeleton type wrapping Channelable in the manner we expect resource writers diff --git a/pkg/apis/eventing/v1alpha1/channel_validation.go b/pkg/apis/eventing/v1alpha1/channel_validation.go index 0840dbec25c..d686a8c7bfd 100644 --- a/pkg/apis/eventing/v1alpha1/channel_validation.go +++ b/pkg/apis/eventing/v1alpha1/channel_validation.go @@ -36,8 +36,8 @@ func (cs *ChannelSpec) Validate() *apis.FieldError { if cs.Channelable != nil { for i, subscriber := range cs.Channelable.Subscribers { - if subscriber.SinkableURI == "" && subscriber.CallableURI == "" { - fe := apis.ErrMissingField("sinkableURI", "callableURI") + if subscriber.ReplyURI == "" && subscriber.SubscriberURI == "" { + fe := apis.ErrMissingField("replyURI", "subscriberURI") fe.Details = "expected at least one of, got none" errs = errs.Also(fe.ViaField(fmt.Sprintf("subscriber[%d]", i)).ViaField("channelable")) } diff --git a/pkg/apis/eventing/v1alpha1/channel_validation_test.go b/pkg/apis/eventing/v1alpha1/channel_validation_test.go index 9626207cbb3..0e4545bb943 100644 --- a/pkg/apis/eventing/v1alpha1/channel_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/channel_validation_test.go @@ -54,8 +54,8 @@ func TestChannelValidation(t *testing.T) { }, Channelable: &eventingduck.Channelable{ Subscribers: []eventingduck.ChannelSubscriberSpec{{ - CallableURI: "callableendpoint", - SinkableURI: "resultendpoint", + SubscriberURI: "subscriberendpoint", + ReplyURI: "resultendpoint", }}, }}, }, @@ -69,13 +69,13 @@ func TestChannelValidation(t *testing.T) { }, Channelable: &eventingduck.Channelable{ Subscribers: []eventingduck.ChannelSubscriberSpec{{ - CallableURI: "callableendpoint", - SinkableURI: "callableendpoint", + SubscriberURI: "subscriberendpoint", + ReplyURI: "replyendpoint", }, {}}, }}, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("spec.channelable.subscriber[1].callableURI", "spec.channelable.subscriber[1].sinkableURI") + fe := apis.ErrMissingField("spec.channelable.subscriber[1].replyURI", "spec.channelable.subscriber[1].subscriberURI") fe.Details = "expected at least one of, got none" return fe }(), @@ -93,10 +93,10 @@ func TestChannelValidation(t *testing.T) { }, want: func() *apis.FieldError { var errs *apis.FieldError - fe := apis.ErrMissingField("spec.channelable.subscriber[0].callableURI", "spec.channelable.subscriber[0].sinkableURI") + fe := apis.ErrMissingField("spec.channelable.subscriber[0].replyURI", "spec.channelable.subscriber[0].subscriberURI") fe.Details = "expected at least one of, got none" errs = errs.Also(fe) - fe = apis.ErrMissingField("spec.channelable.subscriber[1].callableURI", "spec.channelable.subscriber[1].sinkableURI") + fe = apis.ErrMissingField("spec.channelable.subscriber[1].replyURI", "spec.channelable.subscriber[1].subscriberURI") fe.Details = "expected at least one of, got none" errs = errs.Also(fe) return errs diff --git a/pkg/apis/eventing/v1alpha1/subscription_types.go b/pkg/apis/eventing/v1alpha1/subscription_types.go index 226707d5f38..e101e1ec6dc 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_types.go +++ b/pkg/apis/eventing/v1alpha1/subscription_types.go @@ -46,19 +46,19 @@ var _ apis.Immutable = (*Subscription)(nil) var _ runtime.Object = (*Subscription)(nil) var _ webhook.GenericCRD = (*Subscription)(nil) -// SubscriptionSpec specifies the Channel for incoming events, a Call target for -// processing those events and where to put the result of the processing. Only +// SubscriptionSpec specifies the Channel for incoming events, a Subscriber target +// for processing those events and where to put the result of the processing. Only // From (where the events are coming from) is always required. You can optionally // only Process the events (results in no output events) by leaving out the Result. // You can also perform an identity transformation on the invoming events by leaving -// out the Call and only specifying Result. +// out the Subscriber and only specifying Result. // // The following are all valid specifications: -// from --[call]--> result +// channel --[subscriber]--> reply // Sink, no outgoing events: -// from -- call +// channel -- subscriber // no-op function (identity transformation): -// from --> result +// channel --> reply type SubscriptionSpec struct { // TODO: Generation used to not work correctly with CRD. They were scrubbed // by the APIserver (https://github.com/kubernetes/kubernetes/issues/58778) @@ -67,16 +67,16 @@ type SubscriptionSpec struct { // +optional Generation int64 `json:"generation,omitempty"` - // Reference to an object that will be used to create the subscription - // for receiving events. The object must have spec.subscriptions + // Reference to a channel that will be used to create the subscription + // for receiving events. The channel must have spec.subscriptions // list which will then be modified accordingly. // // You can specify only the following fields of the ObjectReference: // - Kind // - APIVersion // - Name - // Currently Kind must be "Channel" and - // APIVersion must be "eventing.knative.dev/v1alpha1" + // Kind must be "Channel" and APIVersion must be + // "eventing.knative.dev/v1alpha1" // // This field is immutable. We have no good answer on what happens to // the events that are currently in the channel being consumed from @@ -85,21 +85,21 @@ type SubscriptionSpec struct { // channel, giving the user more control over what semantics should // be used (drain the channel first, possibly have events dropped, // etc.) - From corev1.ObjectReference `json:"from"` + Channel corev1.ObjectReference `json:"channel"` - // Call is reference to (optional) function for processing events. - // Events from the From channel will be delivered here and replies - // are sent to a channel as specified by the Result. + // Subscriber is reference to (optional) function for processing events. + // Events from the Channel will be delivered here and replies are + // sent to a channel as specified by the Reply. // +optional - Call *EndpointSpec `json:"call,omitempty"` + Subscriber *SubscriberSpec `json:"subscriber,omitempty"` - // Result specifies (optionally) how to handle events returned from - // the Call target. + // Reply specifies (optionally) how to handle events returned from + // the Subscriber target. // +optional - Result *ResultStrategy `json:"result,omitempty"` + Reply *ReplyStrategy `json:"reply,omitempty"` } -// EndpointSpec specifies the reference to an object that's expected to +// SubscriberSpec specifies the reference to an object that's expected to // provide the resolved target of the action. // Currently we inspect the objects Status and see if there's a predefined // Status field that we will then use to dispatch events to be processed by @@ -114,13 +114,13 @@ type SubscriptionSpec struct { // // This ensures that we can support external targets and for ease of use // we also allow for an URI to be specified. -// There of course is also a requirement for the resolved EndpointSpec to +// There of course is also a requirement for the resolved SubscriberSpec to // behave properly at the data plane level. // TODO: Add a pointer to a real spec for this. // For now, this means: Receive an event payload, and respond with one of: // success and an optional response event, or failure. -// Delivery failures may be retried by the from Channel -type EndpointSpec struct { +// Delivery failures may be retried by the channel +type SubscriberSpec struct { // Only one of these can be specified // Reference to an object that will be used to find the target @@ -134,7 +134,7 @@ type EndpointSpec struct { // - APIVersion // - Name // +optional - TargetRef *corev1.ObjectReference `json:"targetRef,omitempty"` + Ref *corev1.ObjectReference `json:"ref,omitempty"` // Reference to a 'known' endpoint where no resolving is done. // http://k8s-service for example @@ -143,19 +143,19 @@ type EndpointSpec struct { DNSName *string `json:"dnsName,omitempty"` } -// ResultStrategy specifies the handling of the EndpointSpec's returned result. -// If no EndpointSpec is specified, the identity function is assumed. -type ResultStrategy struct { - // This object must fulfill the Sinkable contract. +// ReplyStrategy specifies the handling of the SubscriberSpec's returned replies. +// If no SubscriberSpec is specified, the identity function is assumed. +type ReplyStrategy struct { + // This object must be a Channel. // - // TODO: Specify the required fields the target object must - // have in the status. // You can specify only the following fields of the ObjectReference: // - Kind // - APIVersion // - Name + // Kind must be "Channel" and APIVersion must be + // "eventing.knative.dev/v1alpha1" // +optional - Target *corev1.ObjectReference `json:"target,omitempty"` + Channel *corev1.ObjectReference `json:"target,omitempty"` } // subCondSet is a condition set with Ready as the happy condition and @@ -176,11 +176,11 @@ type SubscriptionStatus struct { // SubscriptionStatusPhysicalSubscription represents the fully resolved values for this // Subscription. type SubscriptionStatusPhysicalSubscription struct { - // CallURI is the fully resolved URI for spec.callable. - CallURI string `json:"callURI,omitEmpty"` + // SubscriberURI is the fully resolved URI for spec.subscriber. + SubscriberURI string `json:"subscriberURI,omitEmpty"` - // ResultURI is the fully resolved URI for the spec.result. - ResultURI string `json:"resultURI,omitEmpty"` + // ReplyURI is the fully resolved URI for the spec.reply. + ReplyURI string `json:"replyURI,omitEmpty"` } const ( diff --git a/pkg/apis/eventing/v1alpha1/subscription_validation.go b/pkg/apis/eventing/v1alpha1/subscription_validation.go index 20d17797005..7b9d088baa1 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_validation.go +++ b/pkg/apis/eventing/v1alpha1/subscription_validation.go @@ -28,92 +28,80 @@ func (s *Subscription) Validate() *apis.FieldError { return s.Spec.Validate().ViaField("spec") } -// We require always From -// Also at least one of 'call' and 'result' must be defined (non-nill and non-empty) +// We require always Channel +// Also at least one of 'subscriber' and 'reply' must be defined (non-nill and non-empty) func (ss *SubscriptionSpec) Validate() *apis.FieldError { var errs *apis.FieldError - if isFromEmpty(ss.From) { - fe := apis.ErrMissingField("from") - fe.Details = "the Subscription must reference a from channel" + if isChannelEmpty(ss.Channel) { + fe := apis.ErrMissingField("channel") + fe.Details = "the Subscription must reference a channel" return fe - } else if fe := isValidFrom(ss.From); fe != nil { - errs = errs.Also(fe.ViaField("from")) + } else if fe := isValidChannel(ss.Channel); fe != nil { + errs = errs.Also(fe.ViaField("channel")) } - missingCall := isEndpointSpecNilOrEmpty(ss.Call) - missingResultStrategy := isResultStrategyNilOrEmpty(ss.Result) - if missingCall && missingResultStrategy { - fe := apis.ErrMissingField("result", "call") - fe.Details = "the Subscription must reference at least one of (result channel or a call)" + missingSubscriber := isSubscriberSpecNilOrEmpty(ss.Subscriber) + missingReply := isReplyStrategyNilOrEmpty(ss.Reply) + if missingSubscriber && missingReply { + fe := apis.ErrMissingField("reply", "subscriber") + fe.Details = "the Subscription must reference at least one of (reply or a subscriber)" errs = errs.Also(fe) } - if !missingCall { - if fe := isValidEndpointSpec(*ss.Call); fe != nil { - errs = errs.Also(fe.ViaField("call")) + if !missingSubscriber { + if fe := isValidSubscriberSpec(*ss.Subscriber); fe != nil { + errs = errs.Also(fe.ViaField("subscriber")) } } - if !missingResultStrategy { - if fe := isValidResultStrategy(*ss.Result); fe != nil { - errs = errs.Also(fe.ViaField("result")) + if !missingReply { + if fe := isValidReply(*ss.Reply); fe != nil { + errs = errs.Also(fe.ViaField("reply")) } } return errs } -func isEndpointSpecNilOrEmpty(e *EndpointSpec) bool { - return e == nil || equality.Semantic.DeepEqual(e, &EndpointSpec{}) || - (equality.Semantic.DeepEqual(e.TargetRef, &corev1.ObjectReference{}) && e.DNSName == nil) +func isSubscriberSpecNilOrEmpty(s *SubscriberSpec) bool { + return s == nil || equality.Semantic.DeepEqual(s, &SubscriberSpec{}) || + (equality.Semantic.DeepEqual(s.Ref, &corev1.ObjectReference{}) && s.DNSName == nil) } -func isValidEndpointSpec(e EndpointSpec) *apis.FieldError { +func isValidSubscriberSpec(s SubscriberSpec) *apis.FieldError { var errs *apis.FieldError - if e.DNSName != nil && *e.DNSName != "" && e.TargetRef != nil && !equality.Semantic.DeepEqual(e.TargetRef, &corev1.ObjectReference{}) { - errs = errs.Also(apis.ErrMultipleOneOf("targetRef", "dnsName")) + if s.DNSName != nil && *s.DNSName != "" && s.Ref != nil && !equality.Semantic.DeepEqual(s.Ref, &corev1.ObjectReference{}) { + errs = errs.Also(apis.ErrMultipleOneOf("ref", "dnsName")) } - // If TargetRef given, check the fields. - if e.TargetRef != nil && !equality.Semantic.DeepEqual(e.TargetRef, &corev1.ObjectReference{}) { - fe := isValidObjectReference(*e.TargetRef) + // If Ref given, check the fields. + if s.Ref != nil && !equality.Semantic.DeepEqual(s.Ref, &corev1.ObjectReference{}) { + fe := isValidObjectReference(*s.Ref) if fe != nil { - errs = errs.Also(fe.ViaField("targetRef")) + errs = errs.Also(fe.ViaField("ref")) } } return errs } -func isFromEmpty(f corev1.ObjectReference) bool { - return isChannelEmpty(f) +func isReplyStrategyNilOrEmpty(r *ReplyStrategy) bool { + return r == nil || equality.Semantic.DeepEqual(r, &ReplyStrategy{}) || equality.Semantic.DeepEqual(r.Channel, &corev1.ObjectReference{}) } -// Valid from only contains the following fields: -// - Kind == 'Channel' -// - APIVersion == 'eventing.knative.dev/v1alpha1' -// - Name == not empty -func isValidFrom(f corev1.ObjectReference) *apis.FieldError { - return isValidChannel(f) -} - -func isResultStrategyNilOrEmpty(r *ResultStrategy) bool { - return r == nil || equality.Semantic.DeepEqual(r, &ResultStrategy{}) || equality.Semantic.DeepEqual(r.Target, &corev1.ObjectReference{}) -} - -func isValidResultStrategy(r ResultStrategy) *apis.FieldError { - fe := isValidObjectReference(*r.Target) +func isValidReply(r ReplyStrategy) *apis.FieldError { + fe := isValidObjectReference(*r.Channel) if fe != nil { return fe.ViaField("target") } - if r.Target.Kind != "Channel" { - fe := apis.ErrInvalidValue(r.Target.Kind, "kind") + if r.Channel.Kind != "Channel" { + fe := apis.ErrInvalidValue(r.Channel.Kind, "kind") fe.Paths = []string{"kind"} fe.Details = "only 'Channel' kind is allowed" return fe } - if r.Target.APIVersion != "eventing.knative.dev/v1alpha1" { - fe := apis.ErrInvalidValue(r.Target.APIVersion, "apiVersion") + if r.Channel.APIVersion != "eventing.knative.dev/v1alpha1" { + fe := apis.ErrInvalidValue(r.Channel.APIVersion, "apiVersion") fe.Details = "only eventing.knative.dev/v1alpha1 is allowed for apiVersion" return fe } @@ -129,8 +117,8 @@ func (current *Subscription) CheckImmutableFields(og apis.Immutable) *apis.Field return nil } - // Only Call and Result are mutable. - ignoreArguments := cmpopts.IgnoreFields(SubscriptionSpec{}, "Call", "Result") + // Only Subscriber and Reply are mutable. + ignoreArguments := cmpopts.IgnoreFields(SubscriptionSpec{}, "Subscriber", "Reply") if diff := cmp.Diff(original.Spec, current.Spec, ignoreArguments); diff != "" { return &apis.FieldError{ Message: "Immutable fields changed (-old +new)", diff --git a/pkg/apis/eventing/v1alpha1/subscription_validation_test.go b/pkg/apis/eventing/v1alpha1/subscription_validation_test.go index 66117c09500..a386297da2c 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/subscription_validation_test.go @@ -28,33 +28,33 @@ const ( channelAPIVersion = "eventing.knative.dev/v1alpha1" routeKind = "Route" routeAPIVersion = "serving.knative.dev/v1alpha1" - fromChannelName = "fromChannel" - resultChannelName = "toChannel" - callName = "call" + channelName = "subscribedChannel" + replyChannelName = "toChannel" + subscriberName = "subscriber" ) -func getValidFromRef() corev1.ObjectReference { +func getValidChannelRef() corev1.ObjectReference { return corev1.ObjectReference{ - Name: fromChannelName, + Name: channelName, Kind: channelKind, APIVersion: channelAPIVersion, } } -func getValidResultStrategy() *ResultStrategy { - return &ResultStrategy{ - Target: &corev1.ObjectReference{ - Name: resultChannelName, +func getValidReplyStrategy() *ReplyStrategy { + return &ReplyStrategy{ + Channel: &corev1.ObjectReference{ + Name: replyChannelName, Kind: channelKind, APIVersion: channelAPIVersion, }, } } -func getValidEndpointSpec() *EndpointSpec { - return &EndpointSpec{ - TargetRef: &corev1.ObjectReference{ - Name: callName, +func getValidSubscriberSpec() *SubscriberSpec { + return &SubscriberSpec{ + Ref: &corev1.ObjectReference{ + Name: subscriberName, Kind: routeKind, APIVersion: routeAPIVersion, }, @@ -68,16 +68,16 @@ func (d *DummyImmutableType) CheckImmutableFields(og apis.Immutable) *apis.Field } func TestSubscriptionValidation(t *testing.T) { - name := "empty from" + name := "empty channel" c := &Subscription{ Spec: SubscriptionSpec{ - From: corev1.ObjectReference{}, + Channel: corev1.ObjectReference{}, }, } want := &apis.FieldError{ - Paths: []string{"spec.from"}, + Paths: []string{"spec.channel"}, Message: "missing field(s)", - Details: "the Subscription must reference a from channel", + Details: "the Subscription must reference a channel", } t.Run(name, func(t *testing.T) { @@ -97,134 +97,134 @@ func TestSubscriptionSpecValidation(t *testing.T) { }{{ name: "valid", c: &SubscriptionSpec{ - From: getValidFromRef(), - Call: getValidEndpointSpec(), + Channel: getValidChannelRef(), + Subscriber: getValidSubscriberSpec(), }, want: nil, }, { - name: "empty From", + name: "empty Channel", c: &SubscriptionSpec{ - From: corev1.ObjectReference{}, + Channel: corev1.ObjectReference{}, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("from") - fe.Details = "the Subscription must reference a from channel" + fe := apis.ErrMissingField("channel") + fe.Details = "the Subscription must reference a channel" return fe }(), }, { - name: "missing name in From", + name: "missing name in Channel", c: &SubscriptionSpec{ - From: corev1.ObjectReference{ + Channel: corev1.ObjectReference{ Kind: channelKind, APIVersion: channelAPIVersion, }, - Call: getValidEndpointSpec(), + Subscriber: getValidSubscriberSpec(), }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("from.name") + fe := apis.ErrMissingField("channel.name") return fe }(), }, { - name: "missing Call and Result", + name: "missing Subscriber and Reply", c: &SubscriptionSpec{ - From: getValidFromRef(), + Channel: getValidChannelRef(), }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("result", "call") - fe.Details = "the Subscription must reference at least one of (result channel or a call)" + fe := apis.ErrMissingField("reply", "subscriber") + fe.Details = "the Subscription must reference at least one of (reply or a subscriber)" return fe }(), }, { - name: "empty Call and Result", + name: "empty Subscriber and Reply", c: &SubscriptionSpec{ - From: getValidFromRef(), - Call: &EndpointSpec{}, - Result: &ResultStrategy{}, + Channel: getValidChannelRef(), + Subscriber: &SubscriberSpec{}, + Reply: &ReplyStrategy{}, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("result", "call") - fe.Details = "the Subscription must reference at least one of (result channel or a call)" + fe := apis.ErrMissingField("reply", "subscriber") + fe.Details = "the Subscription must reference at least one of (reply or a subscriber)" return fe }(), }, { - name: "missing Result", + name: "missing Reply", c: &SubscriptionSpec{ - From: getValidFromRef(), - Call: getValidEndpointSpec(), + Channel: getValidChannelRef(), + Subscriber: getValidSubscriberSpec(), }, want: nil, }, { - name: "empty Result", + name: "empty Reply", c: &SubscriptionSpec{ - From: getValidFromRef(), - Call: getValidEndpointSpec(), - Result: &ResultStrategy{}, + Channel: getValidChannelRef(), + Subscriber: getValidSubscriberSpec(), + Reply: &ReplyStrategy{}, }, want: nil, }, { - name: "missing Call", + name: "missing Subscriber", c: &SubscriptionSpec{ - From: getValidFromRef(), - Result: getValidResultStrategy(), + Channel: getValidChannelRef(), + Reply: getValidReplyStrategy(), }, want: nil, }, { - name: "empty Call", + name: "empty Subscriber", c: &SubscriptionSpec{ - From: getValidFromRef(), - Call: &EndpointSpec{}, - Result: getValidResultStrategy(), + Channel: getValidChannelRef(), + Subscriber: &SubscriberSpec{}, + Reply: getValidReplyStrategy(), }, want: nil, }, { - name: "missing name in from, and missing call, result", + name: "missing name in channel, and missing subscriber, reply", c: &SubscriptionSpec{ - From: corev1.ObjectReference{ + Channel: corev1.ObjectReference{ Kind: channelKind, APIVersion: channelAPIVersion, }, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("result", "call") - fe.Details = "the Subscription must reference at least one of (result channel or a call)" - return apis.ErrMissingField("from.name").Also(fe) + fe := apis.ErrMissingField("reply", "subscriber") + fe.Details = "the Subscription must reference at least one of (reply or a subscriber)" + return apis.ErrMissingField("channel.name").Also(fe) }(), }, { name: "empty", c: &SubscriptionSpec{}, want: func() *apis.FieldError { - fe := apis.ErrMissingField("from") - fe.Details = "the Subscription must reference a from channel" + fe := apis.ErrMissingField("channel") + fe.Details = "the Subscription must reference a channel" return fe }(), }, { - name: "missing name in Call.TargetRef", + name: "missing name in Subscriber.Ref", c: &SubscriptionSpec{ - From: getValidFromRef(), - Call: &EndpointSpec{ - TargetRef: &corev1.ObjectReference{ + Channel: getValidChannelRef(), + Subscriber: &SubscriberSpec{ + Ref: &corev1.ObjectReference{ Kind: channelKind, APIVersion: channelAPIVersion, }, }, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("call.targetRef.name") + fe := apis.ErrMissingField("subscriber.ref.name") return fe }(), }, { - name: "missing name in Result.TargetRef", + name: "missing name in Reply.Ref", c: &SubscriptionSpec{ - From: getValidFromRef(), - Result: &ResultStrategy{ - Target: &corev1.ObjectReference{ + Channel: getValidChannelRef(), + Reply: &ReplyStrategy{ + Channel: &corev1.ObjectReference{ Kind: channelKind, APIVersion: channelAPIVersion, }, }, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("result.target.name") + fe := apis.ErrMissingField("reply.target.name") return fe }(), }} @@ -233,21 +233,21 @@ func TestSubscriptionSpecValidation(t *testing.T) { t.Run(test.name, func(t *testing.T) { got := test.c.Validate() if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("%s: validateFrom (-want, +got) = %v", test.name, diff) + t.Errorf("%s: validateChannel (-want, +got) = %v", test.name, diff) } }) } } func TestSubscriptionImmutable(t *testing.T) { - newFrom := getValidFromRef() - newFrom.Name = "newFromChannel" + newChannel := getValidChannelRef() + newChannel.Name = "newChannel" - newCall := getValidEndpointSpec() - newCall.TargetRef.Name = "newCall" + newSubscriber := getValidSubscriberSpec() + newSubscriber.Ref.Name = "newSubscriber" - newResult := getValidResultStrategy() - newResult.Target.Name = "newResultChannel" + newReply := getValidReplyStrategy() + newReply.Channel.Name = "newReplyChannel" tests := []struct { name string @@ -258,12 +258,12 @@ func TestSubscriptionImmutable(t *testing.T) { name: "valid", c: &Subscription{ Spec: SubscriptionSpec{ - From: getValidFromRef(), + Channel: getValidChannelRef(), }, }, og: &Subscription{ Spec: SubscriptionSpec{ - From: getValidFromRef(), + Channel: getValidChannelRef(), }, }, want: nil, @@ -271,90 +271,90 @@ func TestSubscriptionImmutable(t *testing.T) { name: "new nil is ok", c: &Subscription{ Spec: SubscriptionSpec{ - From: getValidFromRef(), - Call: getValidEndpointSpec(), + Channel: getValidChannelRef(), + Subscriber: getValidSubscriberSpec(), }, }, og: nil, want: nil, }, { - name: "valid, new Call", + name: "valid, new Subscriber", c: &Subscription{ Spec: SubscriptionSpec{ - From: getValidFromRef(), - Call: getValidEndpointSpec(), + Channel: getValidChannelRef(), + Subscriber: getValidSubscriberSpec(), }, }, og: &Subscription{ Spec: SubscriptionSpec{ - From: getValidFromRef(), - Call: newCall, + Channel: getValidChannelRef(), + Subscriber: newSubscriber, }, }, want: nil, }, { - name: "valid, new Result", + name: "valid, new Reply", c: &Subscription{ Spec: SubscriptionSpec{ - From: getValidFromRef(), - Result: getValidResultStrategy(), + Channel: getValidChannelRef(), + Reply: getValidReplyStrategy(), }, }, og: &Subscription{ Spec: SubscriptionSpec{ - From: getValidFromRef(), - Result: newResult, + Channel: getValidChannelRef(), + Reply: newReply, }, }, want: nil, }, { - name: "valid, have Result, remove and replace with Call", + name: "valid, have Reply, remove and replace with Subscriber", c: &Subscription{ Spec: SubscriptionSpec{ - From: getValidFromRef(), - Result: getValidResultStrategy(), + Channel: getValidChannelRef(), + Reply: getValidReplyStrategy(), }, }, og: &Subscription{ Spec: SubscriptionSpec{ - From: getValidFromRef(), - Call: getValidEndpointSpec(), + Channel: getValidChannelRef(), + Subscriber: getValidSubscriberSpec(), }, }, want: nil, }, { - name: "valid, have Call, remove and replace with Result", + name: "valid, have Subscriber, remove and replace with Reply", c: &Subscription{ Spec: SubscriptionSpec{ - From: getValidFromRef(), - Call: getValidEndpointSpec(), + Channel: getValidChannelRef(), + Subscriber: getValidSubscriberSpec(), }, }, og: &Subscription{ Spec: SubscriptionSpec{ - From: getValidFromRef(), - Result: getValidResultStrategy(), + Channel: getValidChannelRef(), + Reply: getValidReplyStrategy(), }, }, want: nil, }, { - name: "From changed", + name: "Channel changed", c: &Subscription{ Spec: SubscriptionSpec{ - From: getValidFromRef(), + Channel: getValidChannelRef(), }, }, og: &Subscription{ Spec: SubscriptionSpec{ - From: newFrom, + Channel: newChannel, }, }, want: &apis.FieldError{ Message: "Immutable fields changed (-old +new)", Paths: []string{"spec"}, - Details: `{v1alpha1.SubscriptionSpec}.From.Name: - -: "newFromChannel" - +: "fromChannel" + Details: `{v1alpha1.SubscriptionSpec}.Channel.Name: + -: "newChannel" + +: "subscribedChannel" `, }, }} @@ -373,8 +373,8 @@ func TestInvalidImmutableType(t *testing.T) { name := "invalid type" c := &Subscription{ Spec: SubscriptionSpec{ - From: getValidFromRef(), - Call: getValidEndpointSpec(), + Channel: getValidChannelRef(), + Subscriber: getValidSubscriberSpec(), }, } og := &DummyImmutableType{} @@ -389,7 +389,7 @@ func TestInvalidImmutableType(t *testing.T) { }) } -func TestValidFrom(t *testing.T) { +func TestValidChannel(t *testing.T) { tests := []struct { name string c corev1.ObjectReference @@ -397,7 +397,7 @@ func TestValidFrom(t *testing.T) { }{{ name: "valid", c: corev1.ObjectReference{ - Name: fromChannelName, + Name: channelName, APIVersion: channelAPIVersion, Kind: channelKind, }, @@ -415,7 +415,7 @@ func TestValidFrom(t *testing.T) { }, { name: "missing apiVersion", c: corev1.ObjectReference{ - Name: fromChannelName, + Name: channelName, Kind: channelKind, }, want: func() *apis.FieldError { @@ -426,7 +426,7 @@ func TestValidFrom(t *testing.T) { }, { name: "missing kind", c: corev1.ObjectReference{ - Name: fromChannelName, + Name: channelName, APIVersion: channelAPIVersion, }, want: func() *apis.FieldError { @@ -437,7 +437,7 @@ func TestValidFrom(t *testing.T) { }, { name: "invalid kind", c: corev1.ObjectReference{ - Name: fromChannelName, + Name: channelName, APIVersion: channelAPIVersion, Kind: "subscription", }, @@ -449,7 +449,7 @@ func TestValidFrom(t *testing.T) { }, { name: "invalid apiVersion", c: corev1.ObjectReference{ - Name: fromChannelName, + Name: channelName, APIVersion: "wrongapiversion", Kind: channelKind, }, @@ -461,7 +461,7 @@ func TestValidFrom(t *testing.T) { }, { name: "extra field, namespace", c: corev1.ObjectReference{ - Name: fromChannelName, + Name: channelName, APIVersion: channelAPIVersion, Kind: channelKind, Namespace: "secretnamespace", @@ -474,7 +474,7 @@ func TestValidFrom(t *testing.T) { }, { name: "extra field, namespace and resourceVersion", c: corev1.ObjectReference{ - Name: fromChannelName, + Name: channelName, APIVersion: channelAPIVersion, Kind: channelKind, Namespace: "secretnamespace", @@ -489,7 +489,7 @@ func TestValidFrom(t *testing.T) { // Make sure that if an empty field for namespace is given, it's treated as not there. name: "valid extra field, namespace empty", c: corev1.ObjectReference{ - Name: fromChannelName, + Name: channelName, APIVersion: channelAPIVersion, Kind: channelKind, Namespace: "", @@ -499,101 +499,101 @@ func TestValidFrom(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got := isValidFrom(test.c) + got := isValidChannel(test.c) if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("isValidFrom (-want, +got) = %v", diff) + t.Errorf("isValidChannel (-want, +got) = %v", diff) } }) } } -func TestValidgetValidEndpointSpec(t *testing.T) { +func TestValidgetValidSubscriber(t *testing.T) { dnsName := "example.com" tests := []struct { name string - e EndpointSpec + s SubscriberSpec want *apis.FieldError }{{ - name: "valid targetRef", - e: *getValidEndpointSpec(), + name: "valid ref", + s: *getValidSubscriberSpec(), want: nil, }, { name: "valid dnsName", - e: EndpointSpec{ + s: SubscriberSpec{ DNSName: &dnsName, }, want: nil, }, { - name: "both targetRef and dnsName given", - e: EndpointSpec{ - TargetRef: &corev1.ObjectReference{ - Name: fromChannelName, + name: "both ref and dnsName given", + s: SubscriberSpec{ + Ref: &corev1.ObjectReference{ + Name: channelName, APIVersion: channelAPIVersion, Kind: channelKind, }, DNSName: &dnsName, }, want: func() *apis.FieldError { - fe := apis.ErrMultipleOneOf("targetRef", "dnsName") + fe := apis.ErrMultipleOneOf("ref", "dnsName") return fe }(), }, { - name: "missing name in targetRef", - e: EndpointSpec{ - TargetRef: &corev1.ObjectReference{ + name: "missing name in ref", + s: SubscriberSpec{ + Ref: &corev1.ObjectReference{ APIVersion: channelAPIVersion, Kind: channelKind, }, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("targetRef.name") + fe := apis.ErrMissingField("ref.name") return fe }(), }, { - name: "missing apiVersion in targetRef", - e: EndpointSpec{ - TargetRef: &corev1.ObjectReference{ - Name: fromChannelName, + name: "missing apiVersion in ref", + s: SubscriberSpec{ + Ref: &corev1.ObjectReference{ + Name: channelName, Kind: channelKind, }, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("targetRef.apiVersion") + fe := apis.ErrMissingField("ref.apiVersion") return fe }(), }, { - name: "missing kind in targetRef", - e: EndpointSpec{ - TargetRef: &corev1.ObjectReference{ - Name: fromChannelName, + name: "missing kind in ref", + s: SubscriberSpec{ + Ref: &corev1.ObjectReference{ + Name: channelName, APIVersion: channelAPIVersion, }, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("targetRef.kind") + fe := apis.ErrMissingField("ref.kind") return fe }(), }, { name: "extra field, namespace", - e: EndpointSpec{ - TargetRef: &corev1.ObjectReference{ - Name: fromChannelName, + s: SubscriberSpec{ + Ref: &corev1.ObjectReference{ + Name: channelName, APIVersion: channelAPIVersion, Kind: channelKind, Namespace: "secretnamespace", }, }, want: func() *apis.FieldError { - fe := apis.ErrDisallowedFields("targetRef.Namespace") + fe := apis.ErrDisallowedFields("ref.Namespace") fe.Details = "only name, apiVersion and kind are supported fields" return fe }(), }, { // Make sure that if an empty field for namespace is given, it's treated as not there. name: "valid extra field, namespace empty", - e: EndpointSpec{ - TargetRef: &corev1.ObjectReference{ - Name: fromChannelName, + s: SubscriberSpec{ + Ref: &corev1.ObjectReference{ + Name: channelName, APIVersion: channelAPIVersion, Kind: channelKind, Namespace: "", @@ -604,27 +604,27 @@ func TestValidgetValidEndpointSpec(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got := isValidEndpointSpec(test.e) + got := isValidSubscriberSpec(test.s) if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("%s: isValidFrom (-want, +got) = %v", test.name, diff) + t.Errorf("%s: isValidSubscriber (-want, +got) = %v", test.name, diff) } }) } } -func TestValidResultStrategy(t *testing.T) { +func TestValidReply(t *testing.T) { tests := []struct { name string - c ResultStrategy + r ReplyStrategy want *apis.FieldError }{{ name: "valid target", - c: *getValidResultStrategy(), + r: *getValidReplyStrategy(), want: nil, }, { name: "missing name in target", - c: ResultStrategy{ - Target: &corev1.ObjectReference{ + r: ReplyStrategy{ + Channel: &corev1.ObjectReference{ APIVersion: channelAPIVersion, Kind: channelKind, }, @@ -635,9 +635,9 @@ func TestValidResultStrategy(t *testing.T) { }(), }, { name: "missing apiVersion in target", - c: ResultStrategy{ - Target: &corev1.ObjectReference{ - Name: fromChannelName, + r: ReplyStrategy{ + Channel: &corev1.ObjectReference{ + Name: channelName, Kind: channelKind, }, }, @@ -647,9 +647,9 @@ func TestValidResultStrategy(t *testing.T) { }(), }, { name: "missing kind in target", - c: ResultStrategy{ - Target: &corev1.ObjectReference{ - Name: fromChannelName, + r: ReplyStrategy{ + Channel: &corev1.ObjectReference{ + Name: channelName, APIVersion: channelAPIVersion, }, }, @@ -659,9 +659,9 @@ func TestValidResultStrategy(t *testing.T) { }(), }, { name: "invalid kind", - c: ResultStrategy{ - Target: &corev1.ObjectReference{ - Name: fromChannelName, + r: ReplyStrategy{ + Channel: &corev1.ObjectReference{ + Name: channelName, APIVersion: channelAPIVersion, Kind: "subscription", }, @@ -673,9 +673,9 @@ func TestValidResultStrategy(t *testing.T) { }(), }, { name: "invalid apiVersion", - c: ResultStrategy{ - Target: &corev1.ObjectReference{ - Name: fromChannelName, + r: ReplyStrategy{ + Channel: &corev1.ObjectReference{ + Name: channelName, APIVersion: "wrongapiversion", Kind: channelKind, }, @@ -687,9 +687,9 @@ func TestValidResultStrategy(t *testing.T) { }(), }, { name: "extra field, namespace", - c: ResultStrategy{ - Target: &corev1.ObjectReference{ - Name: fromChannelName, + r: ReplyStrategy{ + Channel: &corev1.ObjectReference{ + Name: channelName, APIVersion: channelAPIVersion, Kind: channelKind, Namespace: "secretnamespace", @@ -703,9 +703,9 @@ func TestValidResultStrategy(t *testing.T) { }, { // Make sure that if an empty field for namespace is given, it's treated as not there. name: "valid extra field, namespace empty", - c: ResultStrategy{ - Target: &corev1.ObjectReference{ - Name: fromChannelName, + r: ReplyStrategy{ + Channel: &corev1.ObjectReference{ + Name: channelName, APIVersion: channelAPIVersion, Kind: channelKind, Namespace: "", @@ -716,9 +716,9 @@ func TestValidResultStrategy(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { - got := isValidResultStrategy(test.c) + got := isValidReply(test.r) if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("%s: isValidFrom (-want, +got) = %v", test.name, diff) + t.Errorf("%s: isValidReply (-want, +got) = %v", test.name, diff) } }) } diff --git a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go index 6929da841c0..a50e2e78073 100644 --- a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go @@ -256,10 +256,10 @@ func (in *ClusterChannelProvisionerStatus) DeepCopy() *ClusterChannelProvisioner } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EndpointSpec) DeepCopyInto(out *EndpointSpec) { +func (in *ReplyStrategy) DeepCopyInto(out *ReplyStrategy) { *out = *in - if in.TargetRef != nil { - in, out := &in.TargetRef, &out.TargetRef + if in.Channel != nil { + in, out := &in.Channel, &out.Channel if *in == nil { *out = nil } else { @@ -267,33 +267,24 @@ func (in *EndpointSpec) DeepCopyInto(out *EndpointSpec) { **out = **in } } - if in.DNSName != nil { - in, out := &in.DNSName, &out.DNSName - if *in == nil { - *out = nil - } else { - *out = new(string) - **out = **in - } - } return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EndpointSpec. -func (in *EndpointSpec) DeepCopy() *EndpointSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReplyStrategy. +func (in *ReplyStrategy) DeepCopy() *ReplyStrategy { if in == nil { return nil } - out := new(EndpointSpec) + out := new(ReplyStrategy) in.DeepCopyInto(out) return out } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ResultStrategy) DeepCopyInto(out *ResultStrategy) { +func (in *SubscriberSpec) DeepCopyInto(out *SubscriberSpec) { *out = *in - if in.Target != nil { - in, out := &in.Target, &out.Target + if in.Ref != nil { + in, out := &in.Ref, &out.Ref if *in == nil { *out = nil } else { @@ -301,15 +292,24 @@ func (in *ResultStrategy) DeepCopyInto(out *ResultStrategy) { **out = **in } } + if in.DNSName != nil { + in, out := &in.DNSName, &out.DNSName + if *in == nil { + *out = nil + } else { + *out = new(string) + **out = **in + } + } return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ResultStrategy. -func (in *ResultStrategy) DeepCopy() *ResultStrategy { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscriberSpec. +func (in *SubscriberSpec) DeepCopy() *SubscriberSpec { if in == nil { return nil } - out := new(ResultStrategy) + out := new(SubscriberSpec) in.DeepCopyInto(out) return out } @@ -378,22 +378,22 @@ func (in *SubscriptionList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *SubscriptionSpec) DeepCopyInto(out *SubscriptionSpec) { *out = *in - out.From = in.From - if in.Call != nil { - in, out := &in.Call, &out.Call + out.Channel = in.Channel + if in.Subscriber != nil { + in, out := &in.Subscriber, &out.Subscriber if *in == nil { *out = nil } else { - *out = new(EndpointSpec) + *out = new(SubscriberSpec) (*in).DeepCopyInto(*out) } } - if in.Result != nil { - in, out := &in.Result, &out.Result + if in.Reply != nil { + in, out := &in.Reply, &out.Reply if *in == nil { *out = nil } else { - *out = new(ResultStrategy) + *out = new(ReplyStrategy) (*in).DeepCopyInto(*out) } } diff --git a/pkg/buses/message_dispatcher.go b/pkg/buses/message_dispatcher.go index c04b99508dc..39346b7eb2d 100644 --- a/pkg/buses/message_dispatcher.go +++ b/pkg/buses/message_dispatcher.go @@ -63,25 +63,25 @@ func NewMessageDispatcher(logger *zap.SugaredLogger) *MessageDispatcher { // DispatchMessage dispatches a message to a destination over HTTP. // -// The destination and replyTo are DNS names. For names with a single label, +// The destination and reply are DNS names. For names with a single label, // the default namespace is used to expand it into a fully qualified name // within the cluster. -func (d *MessageDispatcher) DispatchMessage(message *Message, destination, replyTo string, defaults DispatchDefaults) error { +func (d *MessageDispatcher) DispatchMessage(message *Message, destination, reply string, defaults DispatchDefaults) error { var err error // Default to replying with the original message. If there is a destination, then replace it // with the response from the call to the destination instead. - reply := message + response := message if destination != "" { destinationURL := d.resolveURL(destination, defaults.Namespace) - reply, err = d.executeRequest(destinationURL, message) + response, err = d.executeRequest(destinationURL, message) if err != nil { return fmt.Errorf("Unable to complete request %v", err) } } - if replyTo != "" && reply != nil { - replyToURL := d.resolveURL(replyTo, defaults.Namespace) - _, err = d.executeRequest(replyToURL, reply) + if reply != "" && response != nil { + replyURL := d.resolveURL(reply, defaults.Namespace) + _, err = d.executeRequest(replyURL, response) if err != nil { return fmt.Errorf("Failed to forward reply %v", err) } diff --git a/pkg/buses/message_dispatcher_test.go b/pkg/buses/message_dispatcher_test.go index 4ccee0d462b..ab173169080 100644 --- a/pkg/buses/message_dispatcher_test.go +++ b/pkg/buses/message_dispatcher_test.go @@ -18,14 +18,15 @@ package buses import ( "bytes" - "github.com/google/go-cmp/cmp" - "go.uber.org/zap" "io/ioutil" "net/http" "net/http/httptest" "net/url" "strings" "testing" + + "github.com/google/go-cmp/cmp" + "go.uber.org/zap" ) var ( @@ -110,7 +111,7 @@ func TestDispatchMessage(t *testing.T) { "knative-2": "knative-2-value", "ce-abc": "ce-abc-value", }, - Payload: []byte("replyTo"), + Payload: []byte("reply"), }, expectedReplyRequest: &requestValidation{ Headers: map[string][]string{ @@ -119,7 +120,7 @@ func TestDispatchMessage(t *testing.T) { "knative-2": {"knative-2-value"}, "ce-abc": {"ce-abc-value"}, }, - Body: "replyTo", + Body: "reply", }, }, "reply - only -- error": { @@ -133,7 +134,7 @@ func TestDispatchMessage(t *testing.T) { "knative-2": "knative-2-value", "ce-abc": "ce-abc-value", }, - Payload: []byte("replyTo"), + Payload: []byte("reply"), }, expectedReplyRequest: &requestValidation{ Headers: map[string][]string{ @@ -142,7 +143,7 @@ func TestDispatchMessage(t *testing.T) { "knative-2": {"knative-2-value"}, "ce-abc": {"ce-abc-value"}, }, - Body: "replyTo", + Body: "reply", }, fakeResponse: &http.Response{ StatusCode: http.StatusNotFound, diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index 90d2929e884..236a666071a 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -75,14 +75,14 @@ var ( FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "foo", + SubscriberURI: "foo", }, { - SinkableURI: "bar", + ReplyURI: "bar", }, { - CallableURI: "baz", - SinkableURI: "qux", + SubscriberURI: "baz", + ReplyURI: "qux", }, }, }, @@ -93,7 +93,7 @@ var ( FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "steve", + SubscriberURI: "steve", }, }, }, @@ -117,14 +117,14 @@ var ( Channelable: &eventingduck.Channelable{ Subscribers: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "foo", + SubscriberURI: "foo", }, { - SinkableURI: "bar", + ReplyURI: "bar", }, { - CallableURI: "baz", - SinkableURI: "qux", + SubscriberURI: "baz", + ReplyURI: "qux", }, }, }, @@ -145,7 +145,7 @@ var ( Channelable: &eventingduck.Channelable{ Subscribers: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "anything", + SubscriberURI: "anything", }, }, }, @@ -166,7 +166,7 @@ var ( Channelable: &eventingduck.Channelable{ Subscribers: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "steve", + SubscriberURI: "steve", }, }, }, diff --git a/pkg/controller/eventing/subscription/reconcile.go b/pkg/controller/eventing/subscription/reconcile.go index cea0c518c48..27154d8522e 100644 --- a/pkg/controller/eventing/subscription/reconcile.go +++ b/pkg/controller/eventing/subscription/reconcile.go @@ -88,50 +88,50 @@ func (r *reconciler) reconcile(subscription *v1alpha1.Subscription) error { deletionTimestamp := accessor.GetDeletionTimestamp() glog.Infof("DeletionTimestamp: %v", deletionTimestamp) - // Verify that `from` exists. - _, err = r.fetchObjectReference(subscription.Namespace, &subscription.Spec.From) + // Verify that `channel` exists. + _, err = r.fetchObjectReference(subscription.Namespace, &subscription.Spec.Channel) if err != nil { - glog.Warningf("Failed to validate `from` exists: %+v, %v", subscription.Spec.From, err) + glog.Warningf("Failed to validate `channel` exists: %+v, %v", subscription.Spec.Channel, err) return err } - callURI := "" - if subscription.Spec.Call != nil { - callURI, err = r.resolveEndpointSpec(subscription.Namespace, *subscription.Spec.Call) + subscriberURI := "" + if subscription.Spec.Subscriber != nil { + subscriberURI, err = r.resolveSubscriberSpec(subscription.Namespace, *subscription.Spec.Subscriber) if err != nil { - glog.Warningf("Failed to resolve Call %+v : %s", *subscription.Spec.Call, err) + glog.Warningf("Failed to resolve Subscriber %+v : %s", *subscription.Spec.Subscriber, err) return err } - if callURI == "" { - return fmt.Errorf("could not get domain from call (is it not targetable?)") + if subscriberURI == "" { + return fmt.Errorf("could not get domain from subscriber (is it not targetable?)") } - subscription.Status.PhysicalSubscription.CallURI = callURI - glog.Infof("Resolved call to: %q", callURI) + subscription.Status.PhysicalSubscription.SubscriberURI = subscriberURI + glog.Infof("Resolved subscriber to: %q", subscriberURI) } - resultURI := "" - if subscription.Spec.Result != nil { - resultURI, err = r.resolveResult(subscription.Namespace, *subscription.Spec.Result) + replyURI := "" + if subscription.Spec.Reply != nil { + replyURI, err = r.resolveResult(subscription.Namespace, *subscription.Spec.Reply) if err != nil { - glog.Warningf("Failed to resolve Result %v : %v", subscription.Spec.Result, err) + glog.Warningf("Failed to resolve Result %v : %v", subscription.Spec.Reply, err) return err } - if resultURI == "" { - glog.Warningf("Failed to resolve result %v to actual domain", *subscription.Spec.Result) + if replyURI == "" { + glog.Warningf("Failed to resolve reply %v to actual domain", *subscription.Spec.Reply) return err } - subscription.Status.PhysicalSubscription.ResultURI = resultURI - glog.Infof("Resolved result to: %q", resultURI) + subscription.Status.PhysicalSubscription.ReplyURI = replyURI + glog.Infof("Resolved reply to: %q", replyURI) } // Everything that was supposed to be resolved was, so flip the status bit on that. subscription.Status.MarkReferencesResolved() - // Ok, now that we have the From and at least one of the Call/Result, let's reconcile - // the From with this information. - err = r.syncPhysicalFromChannel(subscription) + // Ok, now that we have the Channel and at least one of the Call/Result, let's reconcile + // the Channel with this information. + err = r.syncPhysicalChannel(subscription) if err != nil { - glog.Warningf("Failed to sync physical from Channel : %s", err) + glog.Warningf("Failed to sync physical Channel : %s", err) return err } // Everything went well, set the fact that subscriptions have been modified @@ -158,35 +158,35 @@ func (r *reconciler) updateStatus(subscription *v1alpha1.Subscription) (*v1alpha return newSubscription, nil } -// resolveEndpointSpec resolves the Spec.Call object. If it's an +// resolveSubscriberSpec resolves the Spec.Call object. If it's an // ObjectReference will resolve the object and treat it as a Targetable. If // it's DNSName then it's used as is. // TODO: Once Service Routes, etc. support Targetable, use that. // -func (r *reconciler) resolveEndpointSpec(namespace string, es v1alpha1.EndpointSpec) (string, error) { - if es.DNSName != nil && *es.DNSName != "" { - return *es.DNSName, nil +func (r *reconciler) resolveSubscriberSpec(namespace string, s v1alpha1.SubscriberSpec) (string, error) { + if s.DNSName != nil && *s.DNSName != "" { + return *s.DNSName, nil } // K8s services are special cased. They can be called, even though they do not satisfy the // Targetable interface. - if es.TargetRef != nil && es.TargetRef.APIVersion == "v1" && es.TargetRef.Kind == "Service" { + if s.Ref != nil && s.Ref.APIVersion == "v1" && s.Ref.Kind == "Service" { svc := &corev1.Service{} svcKey := types.NamespacedName{ Namespace: namespace, - Name: es.TargetRef.Name, + Name: s.Ref.Name, } err := r.client.Get(context.TODO(), svcKey, svc) if err != nil { - glog.Warningf("Failed to fetch EndpointSpec target as a K8s Service %+v: %s", es.TargetRef, err) + glog.Warningf("Failed to fetch SubscriberSpec target as a K8s Service %+v: %s", s.Ref, err) return "", err } return domainToURL(controller.ServiceHostName(svc.Name, svc.Namespace)), nil } - obj, err := r.fetchObjectReference(namespace, es.TargetRef) + obj, err := r.fetchObjectReference(namespace, s.Ref) if err != nil { - glog.Warningf("Failed to fetch EndpointSpec target %+v: %s", es.TargetRef, err) + glog.Warningf("Failed to fetch SubscriberSpec target %+v: %s", s.Ref, err) return "", err } t := duckv1alpha1.Target{} @@ -203,10 +203,10 @@ func (r *reconciler) resolveEndpointSpec(namespace string, es v1alpha1.EndpointS } // resolveResult resolves the Spec.Result object. -func (r *reconciler) resolveResult(namespace string, resultStrategy v1alpha1.ResultStrategy) (string, error) { - obj, err := r.fetchObjectReference(namespace, resultStrategy.Target) +func (r *reconciler) resolveResult(namespace string, replyStrategy v1alpha1.ReplyStrategy) (string, error) { + obj, err := r.fetchObjectReference(namespace, replyStrategy.Channel) if err != nil { - glog.Warningf("Failed to fetch ResultStrategy target %+v: %s", resultStrategy, err) + glog.Warningf("Failed to fetch ReplyStrategy channel %+v: %s", replyStrategy, err) return "", err } s := duckv1alpha1.Sink{} @@ -241,21 +241,21 @@ func domainToURL(domain string) string { return u.String() } -func (r *reconciler) syncPhysicalFromChannel(sub *v1alpha1.Subscription) error { +func (r *reconciler) syncPhysicalChannel(sub *v1alpha1.Subscription) error { glog.Infof("Reconciling Physical From Channel: %+v", sub) - subs, err := r.listAllSubscriptionsWithPhysicalFrom(sub) + subs, err := r.listAllSubscriptionsWithPhysicalChannel(sub) if err != nil { - glog.Infof("Unable to list all channels with physical from: %+v", err) + glog.Infof("Unable to list all subscriptions with physical channel: %+v", err) return err } channelable := r.createChannelable(subs) - return r.patchPhysicalFrom(sub.Namespace, sub.Spec.From, channelable) + return r.patchPhysicalFrom(sub.Namespace, sub.Spec.Channel, channelable) } -func (r *reconciler) listAllSubscriptionsWithPhysicalFrom(sub *v1alpha1.Subscription) ([]v1alpha1.Subscription, error) { +func (r *reconciler) listAllSubscriptionsWithPhysicalChannel(sub *v1alpha1.Subscription) ([]v1alpha1.Subscription, error) { subs := make([]v1alpha1.Subscription, 0) // The sub we are currently reconciling has not yet written any updated status, so when listing @@ -285,7 +285,7 @@ func (r *reconciler) listAllSubscriptionsWithPhysicalFrom(sub *v1alpha1.Subscrip // This is the sub that is being reconciled. It has already been added to the list. continue } - if equality.Semantic.DeepEqual(sub.Spec.From, s.Spec.From) { + if equality.Semantic.DeepEqual(sub.Spec.Channel, s.Spec.Channel) { subs = append(subs, s) } } @@ -300,10 +300,10 @@ func (r *reconciler) listAllSubscriptionsWithPhysicalFrom(sub *v1alpha1.Subscrip func (r *reconciler) createChannelable(subs []v1alpha1.Subscription) *eventingduck.Channelable { rv := &eventingduck.Channelable{} for _, sub := range subs { - if sub.Status.PhysicalSubscription.CallURI != "" || sub.Status.PhysicalSubscription.ResultURI != "" { + if sub.Status.PhysicalSubscription.SubscriberURI != "" || sub.Status.PhysicalSubscription.ReplyURI != "" { rv.Subscribers = append(rv.Subscribers, eventingduck.ChannelSubscriberSpec{ - CallableURI: sub.Status.PhysicalSubscription.CallURI, - SinkableURI: sub.Status.PhysicalSubscription.ResultURI, + SubscriberURI: sub.Status.PhysicalSubscription.SubscriberURI, + ReplyURI: sub.Status.PhysicalSubscription.ReplyURI, }) } } diff --git a/pkg/controller/eventing/subscription/reconcile_test.go b/pkg/controller/eventing/subscription/reconcile_test.go index d8dec2b7f7c..4ed1acb3304 100644 --- a/pkg/controller/eventing/subscription/reconcile_test.go +++ b/pkg/controller/eventing/subscription/reconcile_test.go @@ -42,7 +42,7 @@ const ( fromChannelName = "fromchannel" resultChannelName = "resultchannel" sourceName = "source" - routeName = "callroute" + routeName = "subscriberroute" channelKind = "Channel" routeKind = "Route" sourceKind = "Source" @@ -96,7 +96,7 @@ var testCases = []controllertesting.TestCase{ "spec": map[string]interface{}{}, }, }, - // Call (using knative route) + // Subscriber (using knative route) &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "serving.knative.dev/v1alpha1", @@ -112,7 +112,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, - // Result channel + // Reply channel &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), @@ -133,11 +133,11 @@ var testCases = []controllertesting.TestCase{ }, }, }, { - Name: "Valid from, call does not exist", + Name: "Valid channel, subscriber does not exist", InitialState: []runtime.Object{ getNewSubscription(), }, - WantErrMsg: `routes.serving.knative.dev "callroute" not found`, + WantErrMsg: `routes.serving.knative.dev "subscriberroute" not found`, WantPresent: []runtime.Object{ getNewSubscriptionWithUnknownConditions(), }, @@ -159,7 +159,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, { - Name: "Valid from, call is not targetable", + Name: "Valid channel, subscriber is not targetable", InitialState: []runtime.Object{ getNewSubscription(), }, @@ -183,7 +183,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, - // Call (using knative route) + // Subscriber (using knative route) &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "serving.knative.dev/v1alpha1", @@ -199,12 +199,12 @@ var testCases = []controllertesting.TestCase{ }, }, }, { - Name: "Valid from and call, result does not exist", + Name: "Valid channel and subscriber, result does not exist", InitialState: []runtime.Object{ getNewSubscription(), }, WantPresent: []runtime.Object{ - getNewSubscriptionWithUnknownConditionsAndPhysicalCall(), + getNewSubscriptionWithUnknownConditionsAndPhysicalSubscriber(), }, WantErrMsg: `channels.eventing.knative.dev "resultchannel" not found`, Scheme: scheme.Scheme, @@ -223,7 +223,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, - // Call (using knative route) + // Subscriber (using knative route) &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "serving.knative.dev/v1alpha1", @@ -241,7 +241,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, { - Name: "valid from, call, result is not sinkable", + Name: "valid channel, subscriber, result is not sinkable", InitialState: []runtime.Object{ getNewSubscription(), }, @@ -250,7 +250,7 @@ var testCases = []controllertesting.TestCase{ // TODO: Again this works on gke cluster, but I need to set // something else up here. later... // getNewSubscriptionWithReferencesResolvedStatus(), - getNewSubscriptionWithUnknownConditionsAndPhysicalCall(), + getNewSubscriptionWithUnknownConditionsAndPhysicalSubscriber(), }, Scheme: scheme.Scheme, Objects: []runtime.Object{ @@ -268,7 +268,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, - // Call (using knative route) + // Subscriber (using knative route) &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "serving.knative.dev/v1alpha1", @@ -284,7 +284,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, - // Result channel + // Reply channel &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), @@ -309,7 +309,7 @@ var testCases = []controllertesting.TestCase{ // failure for now, until upstream is fixed. WantResult: reconcile.Result{}, WantPresent: []runtime.Object{ - getNewSubscriptionWithReferencesResolvedAndPhysicalCallResult(), + getNewSubscriptionWithReferencesResolvedAndPhysicalSubscriberReply(), }, WantErrMsg: "invalid JSON document", Scheme: scheme.Scheme, @@ -328,7 +328,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, - // Call (using knative route) + // Subscriber (using knative route) &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "serving.knative.dev/v1alpha1", @@ -344,7 +344,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, - // Result channel + // Reply channel &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), @@ -375,7 +375,7 @@ var testCases = []controllertesting.TestCase{ // failure for now, until upstream is fixed. WantResult: reconcile.Result{}, WantPresent: []runtime.Object{ - getNewSubscriptionToK8sServiceWithReferencesResolvedAndPhysicalFromCallResult(), + getNewSubscriptionToK8sServiceWithReferencesResolvedAndPhysicalFromSubscriberReply(), }, WantErrMsg: "invalid JSON document", Scheme: scheme.Scheme, @@ -394,7 +394,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, - // Call (using K8s Service) + // Subscriber (using K8s Service) &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "v1", @@ -405,7 +405,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, - // Result channel + // Reply channel &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), @@ -436,7 +436,7 @@ var testCases = []controllertesting.TestCase{ WantResult: reconcile.Result{}, WantErrMsg: "invalid JSON document", WantPresent: []runtime.Object{ - getNewSubscriptionWithSourceWithReferencesResolvedAndPhysicalFromCallResult(), + getNewSubscriptionWithSourceWithReferencesResolvedAndPhysicalFromSubscriberReply(), }, Scheme: scheme.Scheme, Objects: []runtime.Object{ @@ -468,7 +468,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, - // Call (using knative route) + // Subscriber (using knative route) &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "serving.knative.dev/v1alpha1", @@ -484,7 +484,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, - // Result channel + // Reply channel &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), @@ -511,7 +511,7 @@ var testCases = []controllertesting.TestCase{ // The first two Subscriptions both have the same physical From, so we should see that // Channel updated with both Subscriptions. getNewSubscriptionWithFromChannel(), - rename(getNewSubscriptionWithReferencesResolvedAndPhysicalCallResult()), + rename(getNewSubscriptionWithReferencesResolvedAndPhysicalSubscriberReply()), // This subscription has a different physical From, so we should not see it in the same // Channel as the first two. getSubscriptionWithDifferentChannel(), @@ -528,9 +528,9 @@ var testCases = []controllertesting.TestCase{ // a Strategic Merge Patch, whereas we are doing a JSON Patch). so for now, comment it // out. //getChannelWithMultipleSubscriptions(), - getNewSubscriptionWithSourceWithReferencesResolvedAndPhysicalFromCallResult(), + getNewSubscriptionWithSourceWithReferencesResolvedAndPhysicalFromSubscriberReply(), // Unaltered because this Subscription was not reconciled. - rename(getNewSubscriptionWithReferencesResolvedAndPhysicalCallResult()), + rename(getNewSubscriptionWithReferencesResolvedAndPhysicalSubscriberReply()), getSubscriptionWithDifferentChannel(), }, Scheme: scheme.Scheme, @@ -563,7 +563,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, - // Call (using knative route) + // Subscriber (using knative route) &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": "serving.knative.dev/v1alpha1", @@ -579,7 +579,7 @@ var testCases = []controllertesting.TestCase{ }, }, }, - // Result channel + // Reply channel &unstructured.Unstructured{ Object: map[string]interface{}{ "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), @@ -625,7 +625,7 @@ func getNewFromChannel() *eventingv1alpha1.Channel { return getNewChannel(fromChannelName) } -func getNewResultChannel() *eventingv1alpha1.Channel { +func getNewReplyChannel() *eventingv1alpha1.Channel { return getNewChannel(resultChannelName) } @@ -647,8 +647,8 @@ func getNewChannel(name string) *eventingv1alpha1.Channel { func rename(sub *eventingv1alpha1.Subscription) *eventingv1alpha1.Subscription { sub.Name = "renamed" sub.UID = "renamed-UID" - sub.Status.PhysicalSubscription.CallURI = "" - sub.Status.PhysicalSubscription.ResultURI = otherSinkableDNS + sub.Status.PhysicalSubscription.SubscriberURI = "" + sub.Status.PhysicalSubscription.ReplyURI = otherSinkableDNS return sub } @@ -657,20 +657,20 @@ func getNewSubscription() *eventingv1alpha1.Subscription { TypeMeta: subscriptionType(), ObjectMeta: om(testNS, subscriptionName), Spec: eventingv1alpha1.SubscriptionSpec{ - From: corev1.ObjectReference{ + Channel: corev1.ObjectReference{ Name: fromChannelName, Kind: channelKind, APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), }, - Call: &eventingv1alpha1.EndpointSpec{ - TargetRef: &corev1.ObjectReference{ + Subscriber: &eventingv1alpha1.SubscriberSpec{ + Ref: &corev1.ObjectReference{ Name: routeName, Kind: routeKind, APIVersion: "serving.knative.dev/v1alpha1", }, }, - Result: &eventingv1alpha1.ResultStrategy{ - Target: &corev1.ObjectReference{ + Reply: &eventingv1alpha1.ReplyStrategy{ + Channel: &corev1.ObjectReference{ Name: resultChannelName, Kind: channelKind, APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), @@ -687,7 +687,7 @@ func getNewSubscription() *eventingv1alpha1.Subscription { func getNewSourceSubscription() *eventingv1alpha1.Subscription { sub := getNewSubscription() - sub.Spec.From = corev1.ObjectReference{ + sub.Spec.Channel = corev1.ObjectReference{ APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), Kind: sourceKind, Name: sourceName, @@ -697,8 +697,8 @@ func getNewSourceSubscription() *eventingv1alpha1.Subscription { func getNewSubscriptionToK8sService() *eventingv1alpha1.Subscription { sub := getNewSubscription() - sub.Spec.Call = &eventingv1alpha1.EndpointSpec{ - TargetRef: &corev1.ObjectReference{ + sub.Spec.Subscriber = &eventingv1alpha1.SubscriberSpec{ + Ref: &corev1.ObjectReference{ Name: k8sServiceName, Kind: "Service", APIVersion: "v1", @@ -712,20 +712,20 @@ func getNewSubscriptionWithFromChannel() *eventingv1alpha1.Subscription { TypeMeta: subscriptionType(), ObjectMeta: om(testNS, subscriptionName), Spec: eventingv1alpha1.SubscriptionSpec{ - From: corev1.ObjectReference{ + Channel: corev1.ObjectReference{ Name: fromChannelName, Kind: channelKind, APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), }, - Call: &eventingv1alpha1.EndpointSpec{ - TargetRef: &corev1.ObjectReference{ + Subscriber: &eventingv1alpha1.SubscriberSpec{ + Ref: &corev1.ObjectReference{ Name: routeName, Kind: routeKind, APIVersion: "serving.knative.dev/v1alpha1", }, }, - Result: &eventingv1alpha1.ResultStrategy{ - Target: &corev1.ObjectReference{ + Reply: &eventingv1alpha1.ReplyStrategy{ + Channel: &corev1.ObjectReference{ Name: resultChannelName, Kind: channelKind, APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), @@ -745,38 +745,38 @@ func getNewSubscriptionWithUnknownConditions() *eventingv1alpha1.Subscription { s.Status.InitializeConditions() return s } -func getNewSubscriptionWithUnknownConditionsAndPhysicalCall() *eventingv1alpha1.Subscription { +func getNewSubscriptionWithUnknownConditionsAndPhysicalSubscriber() *eventingv1alpha1.Subscription { s := getNewSubscriptionWithUnknownConditions() - s.Status.PhysicalSubscription.CallURI = domainToURL(targetDNS) + s.Status.PhysicalSubscription.SubscriberURI = domainToURL(targetDNS) return s } -func getNewSubscriptionWithReferencesResolvedAndPhysicalCallResult() *eventingv1alpha1.Subscription { +func getNewSubscriptionWithReferencesResolvedAndPhysicalSubscriberReply() *eventingv1alpha1.Subscription { s := getNewSubscriptionWithUnknownConditions() s.Status.MarkReferencesResolved() - s.Status.PhysicalSubscription.CallURI = domainToURL(targetDNS) - s.Status.PhysicalSubscription.ResultURI = domainToURL(sinkableDNS) + s.Status.PhysicalSubscription.SubscriberURI = domainToURL(targetDNS) + s.Status.PhysicalSubscription.ReplyURI = domainToURL(sinkableDNS) return s } -func getNewSubscriptionToK8sServiceWithReferencesResolvedAndPhysicalFromCallResult() *eventingv1alpha1.Subscription { +func getNewSubscriptionToK8sServiceWithReferencesResolvedAndPhysicalFromSubscriberReply() *eventingv1alpha1.Subscription { s := getNewSubscriptionToK8sService() s.Status.InitializeConditions() s.Status.MarkReferencesResolved() s.Status.PhysicalSubscription = eventingv1alpha1.SubscriptionStatusPhysicalSubscription{ - CallURI: domainToURL(k8sServiceDNS), - ResultURI: domainToURL(sinkableDNS), + SubscriberURI: domainToURL(k8sServiceDNS), + ReplyURI: domainToURL(sinkableDNS), } return s } -func getNewSubscriptionWithSourceWithReferencesResolvedAndPhysicalFromCallResult() *eventingv1alpha1.Subscription { +func getNewSubscriptionWithSourceWithReferencesResolvedAndPhysicalFromSubscriberReply() *eventingv1alpha1.Subscription { s := getNewSubscriptionWithFromChannel() s.Status.InitializeConditions() s.Status.MarkReferencesResolved() s.Status.PhysicalSubscription = eventingv1alpha1.SubscriptionStatusPhysicalSubscription{ - CallURI: domainToURL(targetDNS), - ResultURI: domainToURL(sinkableDNS), + SubscriberURI: domainToURL(targetDNS), + ReplyURI: domainToURL(sinkableDNS), } return s } @@ -788,10 +788,10 @@ func getNewSubscriptionWithReferencesResolvedStatus() *eventingv1alpha1.Subscrip } func getSubscriptionWithDifferentChannel() *eventingv1alpha1.Subscription { - s := getNewSubscriptionWithSourceWithReferencesResolvedAndPhysicalFromCallResult() + s := getNewSubscriptionWithSourceWithReferencesResolvedAndPhysicalFromSubscriberReply() s.Name = "different-channel" s.UID = "different-channel-UID" - s.Status.PhysicalSubscription.CallURI = "some-other-domain" + s.Status.PhysicalSubscription.SubscriberURI = "some-other-domain" return s } @@ -833,11 +833,11 @@ func getChannelWithMultipleSubscriptions() *eventingv1alpha1.Channel { Channelable: &eventingduck.Channelable{ Subscribers: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: targetDNS, - SinkableURI: sinkableDNS, + SubscriberURI: targetDNS, + ReplyURI: sinkableDNS, }, { - SinkableURI: otherSinkableDNS, + ReplyURI: otherSinkableDNS, }, }, }, diff --git a/pkg/sidecar/configmap/filesystem/filesystem_watcher_test.go b/pkg/sidecar/configmap/filesystem/filesystem_watcher_test.go index 2e54aad92c8..8152289573c 100644 --- a/pkg/sidecar/configmap/filesystem/filesystem_watcher_test.go +++ b/pkg/sidecar/configmap/filesystem/filesystem_watcher_test.go @@ -83,20 +83,20 @@ func TestReadConfigMap(t *testing.T) { name: c1 fanoutConfig: subscriptions: - - callableURI: event-changer.default.svc.cluster.local - sinkableURI: message-dumper-bar.default.svc.cluster.local - - callableURI: message-dumper-foo.default.svc.cluster.local - - sinkableURI: message-dumper-bar.default.svc.cluster.local + - subscriberURI: event-changer.default.svc.cluster.local + replyURI: message-dumper-bar.default.svc.cluster.local + - subscriberURI: message-dumper-foo.default.svc.cluster.local + - replyURI: message-dumper-bar.default.svc.cluster.local - namespace: default name: c2 fanoutConfig: subscriptions: - - sinkableURI: message-dumper-foo.default.svc.cluster.local + - replyURI: message-dumper-foo.default.svc.cluster.local - namespace: other name: c3 fanoutConfig: subscriptions: - - sinkableURI: message-dumper-foo.default.svc.cluster.local + - replyURI: message-dumper-foo.default.svc.cluster.local `, expected: &multichannelfanout.Config{ ChannelConfigs: []multichannelfanout.ChannelConfig{ @@ -106,14 +106,14 @@ func TestReadConfigMap(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "event-changer.default.svc.cluster.local", - SinkableURI: "message-dumper-bar.default.svc.cluster.local", + SubscriberURI: "event-changer.default.svc.cluster.local", + ReplyURI: "message-dumper-bar.default.svc.cluster.local", }, { - CallableURI: "message-dumper-foo.default.svc.cluster.local", + SubscriberURI: "message-dumper-foo.default.svc.cluster.local", }, { - SinkableURI: "message-dumper-bar.default.svc.cluster.local", + ReplyURI: "message-dumper-bar.default.svc.cluster.local", }, }, }, @@ -124,7 +124,7 @@ func TestReadConfigMap(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: "message-dumper-foo.default.svc.cluster.local", + ReplyURI: "message-dumper-foo.default.svc.cluster.local", }, }, }, @@ -135,7 +135,7 @@ func TestReadConfigMap(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: "message-dumper-foo.default.svc.cluster.local", + ReplyURI: "message-dumper-foo.default.svc.cluster.local", }, }, }, @@ -188,7 +188,7 @@ func TestWatch(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: "foo.bar", + ReplyURI: "foo.bar", }, }, }, @@ -205,7 +205,7 @@ func TestWatch(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: "foo.bar", + ReplyURI: "foo.bar", }, }, }, @@ -223,7 +223,7 @@ func TestWatch(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: "foo.bar", + ReplyURI: "foo.bar", }, }, }, @@ -238,7 +238,7 @@ func TestWatch(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "baz.qux", + SubscriberURI: "baz.qux", }, }, }, diff --git a/pkg/sidecar/configmap/parse_test.go b/pkg/sidecar/configmap/parse_test.go index 135f7620424..4a47a7acf9d 100644 --- a/pkg/sidecar/configmap/parse_test.go +++ b/pkg/sidecar/configmap/parse_test.go @@ -65,20 +65,20 @@ func TestNewFanoutConfig(t *testing.T) { name: c1 fanoutConfig: subscriptions: - - callableURI: event-changer.default.svc.cluster.local - sinkableURI: message-dumper-bar.default.svc.cluster.local - - callableURI: message-dumper-foo.default.svc.cluster.local - - sinkableURI: message-dumper-bar.default.svc.cluster.local + - subscriberURI: event-changer.default.svc.cluster.local + replyURI: message-dumper-bar.default.svc.cluster.local + - subscriberURI: message-dumper-foo.default.svc.cluster.local + - replyURI: message-dumper-bar.default.svc.cluster.local - namespace: default name: c2 fanoutConfig: subscriptions: - - sinkableURI: message-dumper-foo.default.svc.cluster.local + - replyURI: message-dumper-foo.default.svc.cluster.local - namespace: other name: c3 fanoutConfig: subscriptions: - - sinkableURI: message-dumper-foo.default.svc.cluster.local + - replyURI: message-dumper-foo.default.svc.cluster.local `, expected: &multichannelfanout.Config{ ChannelConfigs: []multichannelfanout.ChannelConfig{ @@ -88,14 +88,14 @@ func TestNewFanoutConfig(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "event-changer.default.svc.cluster.local", - SinkableURI: "message-dumper-bar.default.svc.cluster.local", + SubscriberURI: "event-changer.default.svc.cluster.local", + ReplyURI: "message-dumper-bar.default.svc.cluster.local", }, { - CallableURI: "message-dumper-foo.default.svc.cluster.local", + SubscriberURI: "message-dumper-foo.default.svc.cluster.local", }, { - SinkableURI: "message-dumper-bar.default.svc.cluster.local", + ReplyURI: "message-dumper-bar.default.svc.cluster.local", }, }, }, @@ -106,7 +106,7 @@ func TestNewFanoutConfig(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: "message-dumper-foo.default.svc.cluster.local", + ReplyURI: "message-dumper-foo.default.svc.cluster.local", }, }, }, @@ -117,7 +117,7 @@ func TestNewFanoutConfig(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: "message-dumper-foo.default.svc.cluster.local", + ReplyURI: "message-dumper-foo.default.svc.cluster.local", }, }, }, @@ -159,14 +159,14 @@ func TestSerializeConfig(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "foo.example.com", - SinkableURI: "bar.example.com", + SubscriberURI: "foo.example.com", + ReplyURI: "bar.example.com", }, { - SinkableURI: "qux.example.com", + ReplyURI: "qux.example.com", }, { - CallableURI: "baz.example.com", + SubscriberURI: "baz.example.com", }, {}, }, diff --git a/pkg/sidecar/configmap/watcher/watcher_test.go b/pkg/sidecar/configmap/watcher/watcher_test.go index fd92d6e7ed1..6164c38cd63 100644 --- a/pkg/sidecar/configmap/watcher/watcher_test.go +++ b/pkg/sidecar/configmap/watcher/watcher_test.go @@ -67,8 +67,8 @@ func TestReconcile(t *testing.T) { namespace: bar fanoutConfig: subscriptions: - - callableURI: callable - sinkableURI: sinkable`, + - subscriberURI: subscriber + replyURI: reply`, }, expectedConfig: &multichannelfanout.Config{ ChannelConfigs: []multichannelfanout.ChannelConfig{ @@ -78,8 +78,8 @@ func TestReconcile(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "callable", - SinkableURI: "sinkable", + SubscriberURI: "subscriber", + ReplyURI: "reply", }, }, }, diff --git a/pkg/sidecar/fanout/fanout_handler.go b/pkg/sidecar/fanout/fanout_handler.go index 48ff82a5988..f5873c527a8 100644 --- a/pkg/sidecar/fanout/fanout_handler.go +++ b/pkg/sidecar/fanout/fanout_handler.go @@ -120,5 +120,5 @@ func (f *Handler) dispatch(msg *buses.Message) error { // makeFanoutRequest sends the request to exactly one subscription. It handles both the `call` and // the `sink` portions of the subscription. func (f *Handler) makeFanoutRequest(m buses.Message, sub eventingduck.ChannelSubscriberSpec) error { - return f.dispatcher.DispatchMessage(&m, sub.CallableURI, sub.SinkableURI, buses.DispatchDefaults{}) + return f.dispatcher.DispatchMessage(&m, sub.SubscriberURI, sub.ReplyURI, buses.DispatchDefaults{}) } diff --git a/pkg/sidecar/fanout/fanout_handler_test.go b/pkg/sidecar/fanout/fanout_handler_test.go index b1d2376a408..464afcff9b2 100644 --- a/pkg/sidecar/fanout/fanout_handler_test.go +++ b/pkg/sidecar/fanout/fanout_handler_test.go @@ -35,8 +35,8 @@ import ( // Domains used in subscriptions, which will be replaced by the real domains of the started HTTP // servers. const ( - replaceCallable = "replaceCallable" - replaceSinkable = "replaceSinkable" + replaceSubscriber = "replaceSubscriber" + replaceSinkable = "replaceSinkable" ) var ( @@ -61,7 +61,7 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { receiverFunc func(buses.ChannelReference, *buses.Message) error timeout time.Duration subs []eventingduck.ChannelSubscriberSpec - callable func(http.ResponseWriter, *http.Request) + subscriber func(http.ResponseWriter, *http.Request) sinkable func(http.ResponseWriter, *http.Request) expectedStatus int }{ @@ -75,10 +75,10 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { timeout: time.Millisecond, subs: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: replaceCallable, + SubscriberURI: replaceSubscriber, }, }, - callable: func(writer http.ResponseWriter, _ *http.Request) { + subscriber: func(writer http.ResponseWriter, _ *http.Request) { time.Sleep(10 * time.Millisecond) writer.WriteHeader(http.StatusAccepted) }, @@ -94,10 +94,10 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { }, expectedStatus: http.StatusAccepted, }, - "sinkable fails": { + "reply fails": { subs: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: replaceSinkable, + ReplyURI: replaceSinkable, }, }, sinkable: func(writer http.ResponseWriter, _ *http.Request) { @@ -105,25 +105,25 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { }, expectedStatus: http.StatusInternalServerError, }, - "callable fails": { + "subscriber fails": { subs: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: replaceCallable, + SubscriberURI: replaceSubscriber, }, }, - callable: func(writer http.ResponseWriter, _ *http.Request) { + subscriber: func(writer http.ResponseWriter, _ *http.Request) { writer.WriteHeader(http.StatusNotFound) }, expectedStatus: http.StatusInternalServerError, }, - "callable succeeds, sinkable fails": { + "subscriber succeeds, sinkable fails": { subs: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: replaceCallable, - SinkableURI: replaceSinkable, + SubscriberURI: replaceSubscriber, + ReplyURI: replaceSinkable, }, }, - callable: callableSucceed, + subscriber: callableSucceed, sinkable: func(writer http.ResponseWriter, _ *http.Request) { writer.WriteHeader(http.StatusForbidden) }, @@ -132,11 +132,11 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { "one sub succeeds": { subs: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: replaceCallable, - SinkableURI: replaceSinkable, + SubscriberURI: replaceSubscriber, + ReplyURI: replaceSinkable, }, }, - callable: callableSucceed, + subscriber: callableSucceed, sinkable: func(writer http.ResponseWriter, _ *http.Request) { writer.WriteHeader(http.StatusAccepted) }, @@ -145,34 +145,34 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { "one sub succeeds, one sub fails": { subs: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: replaceCallable, - SinkableURI: replaceSinkable, + SubscriberURI: replaceSubscriber, + ReplyURI: replaceSinkable, }, { - CallableURI: replaceCallable, - SinkableURI: replaceSinkable, + SubscriberURI: replaceSubscriber, + ReplyURI: replaceSinkable, }, }, - callable: callableSucceed, + subscriber: callableSucceed, sinkable: (&succeedOnce{}).handler, expectedStatus: http.StatusInternalServerError, }, "all subs succeed": { subs: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: replaceCallable, - SinkableURI: replaceSinkable, + SubscriberURI: replaceSubscriber, + ReplyURI: replaceSinkable, }, { - CallableURI: replaceCallable, - SinkableURI: replaceSinkable, + SubscriberURI: replaceSubscriber, + ReplyURI: replaceSinkable, }, { - CallableURI: replaceCallable, - SinkableURI: replaceSinkable, + SubscriberURI: replaceSubscriber, + ReplyURI: replaceSinkable, }, }, - callable: callableSucceed, + subscriber: callableSucceed, sinkable: func(writer http.ResponseWriter, _ *http.Request) { writer.WriteHeader(http.StatusAccepted) }, @@ -185,7 +185,7 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { } t.Run(n, func(t *testing.T) { callableServer := httptest.NewServer(&fakeHandler{ - handler: tc.callable, + handler: tc.subscriber, }) defer callableServer.Close() sinkableServer := httptest.NewServer(&fakeHandler{ @@ -196,11 +196,11 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { // Rewrite the subs to use the servers we just started. subs := make([]eventingduck.ChannelSubscriberSpec, 0) for _, sub := range tc.subs { - if sub.CallableURI == replaceCallable { - sub.CallableURI = callableServer.URL[7:] // strip the leading 'http://' + if sub.SubscriberURI == replaceSubscriber { + sub.SubscriberURI = callableServer.URL[7:] // strip the leading 'http://' } - if sub.SinkableURI == replaceSinkable { - sub.SinkableURI = sinkableServer.URL[7:] // strip the leading 'http://' + if sub.ReplyURI == replaceSinkable { + sub.ReplyURI = sinkableServer.URL[7:] // strip the leading 'http://' } subs = append(subs, sub) } diff --git a/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler_test.go b/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler_test.go index 82693b0c3a6..02cad30d9e1 100644 --- a/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler_test.go +++ b/pkg/sidecar/multichannelfanout/multi_channel_fanout_handler_test.go @@ -111,7 +111,7 @@ func TestCopyWithNewConfig(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "callabledomain", + SubscriberURI: "subscriberdomain", }, }, }, @@ -126,7 +126,7 @@ func TestCopyWithNewConfig(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: "sinkabledomain", + ReplyURI: "replydomain", }, }, }, @@ -164,7 +164,7 @@ func TestConfigDiff(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "callabledomain", + SubscriberURI: "subscriberdomain", }, }, }, @@ -194,7 +194,7 @@ func TestConfigDiff(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "different", + SubscriberURI: "different", }, }, }, @@ -241,7 +241,7 @@ func TestServeHTTP(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: replaceDomain, + ReplyURI: replaceDomain, }, }, }, @@ -261,7 +261,7 @@ func TestServeHTTP(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: "first-to-domain", + ReplyURI: "first-to-domain", }, }, }, @@ -272,7 +272,7 @@ func TestServeHTTP(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: replaceDomain, + SubscriberURI: replaceDomain, }, }, }, @@ -318,11 +318,11 @@ func TestServeHTTP(t *testing.T) { func replaceDomains(config Config, replacement string) { for i, cc := range config.ChannelConfigs { for j, sub := range cc.FanoutConfig.Subscriptions { - if sub.CallableURI == replaceDomain { - sub.CallableURI = replacement + if sub.SubscriberURI == replaceDomain { + sub.SubscriberURI = replacement } - if sub.SinkableURI == replaceDomain { - sub.SinkableURI = replacement + if sub.ReplyURI == replaceDomain { + sub.ReplyURI = replacement } cc.FanoutConfig.Subscriptions[j] = sub } diff --git a/pkg/sidecar/multichannelfanout/parse_test.go b/pkg/sidecar/multichannelfanout/parse_test.go index c510db6b890..8ef86bc7542 100644 --- a/pkg/sidecar/multichannelfanout/parse_test.go +++ b/pkg/sidecar/multichannelfanout/parse_test.go @@ -64,20 +64,20 @@ func TestConfigMapData(t *testing.T) { name: c1 fanoutConfig: subscriptions: - - callableURI: event-changer.default.svc.cluster.local - sinkableURI: message-dumper-bar.default.svc.cluster.local - - callableURI: message-dumper-foo.default.svc.cluster.local - - sinkableURI: message-dumper-bar.default.svc.cluster.local + - subscriberURI: event-changer.default.svc.cluster.local + replyURI: message-dumper-bar.default.svc.cluster.local + - subscriberURI: message-dumper-foo.default.svc.cluster.local + - replyURI: message-dumper-bar.default.svc.cluster.local - namespace: default name: c2 fanoutConfig: subscriptions: - - sinkableURI: message-dumper-foo.default.svc.cluster.local + - replyURI: message-dumper-foo.default.svc.cluster.local - namespace: other name: c3 fanoutConfig: subscriptions: - - sinkableURI: message-dumper-foo.default.svc.cluster.local + - replyURI: message-dumper-foo.default.svc.cluster.local `, expected: &Config{ ChannelConfigs: []ChannelConfig{ @@ -87,14 +87,14 @@ func TestConfigMapData(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: "event-changer.default.svc.cluster.local", - SinkableURI: "message-dumper-bar.default.svc.cluster.local", + SubscriberURI: "event-changer.default.svc.cluster.local", + ReplyURI: "message-dumper-bar.default.svc.cluster.local", }, { - CallableURI: "message-dumper-foo.default.svc.cluster.local", + SubscriberURI: "message-dumper-foo.default.svc.cluster.local", }, { - SinkableURI: "message-dumper-bar.default.svc.cluster.local", + ReplyURI: "message-dumper-bar.default.svc.cluster.local", }, }, }, @@ -105,7 +105,7 @@ func TestConfigMapData(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: "message-dumper-foo.default.svc.cluster.local", + ReplyURI: "message-dumper-foo.default.svc.cluster.local", }, }, }, @@ -116,7 +116,7 @@ func TestConfigMapData(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: "message-dumper-foo.default.svc.cluster.local", + ReplyURI: "message-dumper-foo.default.svc.cluster.local", }, }, }, diff --git a/pkg/sidecar/swappable/swappable_test.go b/pkg/sidecar/swappable/swappable_test.go index 13a8b7936f3..3993ed17fd9 100644 --- a/pkg/sidecar/swappable/swappable_test.go +++ b/pkg/sidecar/swappable/swappable_test.go @@ -49,7 +49,7 @@ func TestHandler(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: replaceDomain, + SubscriberURI: replaceDomain, }, }, }, @@ -64,7 +64,7 @@ func TestHandler(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - SinkableURI: replaceDomain, + ReplyURI: replaceDomain, }, }, }, @@ -101,7 +101,7 @@ func TestHandler_InvalidConfigChange(t *testing.T) { FanoutConfig: fanout.Config{ Subscriptions: []eventingduck.ChannelSubscriberSpec{ { - CallableURI: replaceDomain, + SubscriberURI: replaceDomain, }, }, }, @@ -204,11 +204,11 @@ func makeRequest(namespace, name string) *http.Request { func replaceDomains(c multichannelfanout.Config, replacement string) multichannelfanout.Config { for i, cc := range c.ChannelConfigs { for j, sub := range cc.FanoutConfig.Subscriptions { - if sub.SinkableURI == replaceDomain { - sub.SinkableURI = replacement + if sub.ReplyURI == replaceDomain { + sub.ReplyURI = replacement } - if sub.CallableURI == replaceDomain { - sub.CallableURI = replacement + if sub.SubscriberURI == replaceDomain { + sub.SubscriberURI = replacement } cc.FanoutConfig.Subscriptions[j] = sub } From 3a976f68e734098e465fdd4a42ad298971f6477a Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Thu, 1 Nov 2018 17:34:34 -0700 Subject: [PATCH 15/54] target -> channel (#574) * target -> channel * Change error message to channel. --- pkg/apis/eventing/v1alpha1/subscription_types.go | 2 +- pkg/apis/eventing/v1alpha1/subscription_validation.go | 5 ++--- .../eventing/v1alpha1/subscription_validation_test.go | 10 +++++----- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/pkg/apis/eventing/v1alpha1/subscription_types.go b/pkg/apis/eventing/v1alpha1/subscription_types.go index e101e1ec6dc..72dcb5fd7a6 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_types.go +++ b/pkg/apis/eventing/v1alpha1/subscription_types.go @@ -155,7 +155,7 @@ type ReplyStrategy struct { // Kind must be "Channel" and APIVersion must be // "eventing.knative.dev/v1alpha1" // +optional - Channel *corev1.ObjectReference `json:"target,omitempty"` + Channel *corev1.ObjectReference `json:"channel,omitempty"` } // subCondSet is a condition set with Ready as the happy condition and diff --git a/pkg/apis/eventing/v1alpha1/subscription_validation.go b/pkg/apis/eventing/v1alpha1/subscription_validation.go index 7b9d088baa1..c74f1a4ac44 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_validation.go +++ b/pkg/apis/eventing/v1alpha1/subscription_validation.go @@ -90,9 +90,8 @@ func isReplyStrategyNilOrEmpty(r *ReplyStrategy) bool { } func isValidReply(r ReplyStrategy) *apis.FieldError { - fe := isValidObjectReference(*r.Channel) - if fe != nil { - return fe.ViaField("target") + if fe := isValidObjectReference(*r.Channel); fe != nil { + return fe.ViaField("channel") } if r.Channel.Kind != "Channel" { fe := apis.ErrInvalidValue(r.Channel.Kind, "kind") diff --git a/pkg/apis/eventing/v1alpha1/subscription_validation_test.go b/pkg/apis/eventing/v1alpha1/subscription_validation_test.go index a386297da2c..5acdfdcb230 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/subscription_validation_test.go @@ -224,7 +224,7 @@ func TestSubscriptionSpecValidation(t *testing.T) { }, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("reply.target.name") + fe := apis.ErrMissingField("reply.channel.name") return fe }(), }} @@ -630,7 +630,7 @@ func TestValidReply(t *testing.T) { }, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("target.name") + fe := apis.ErrMissingField("channel.name") return fe }(), }, { @@ -642,7 +642,7 @@ func TestValidReply(t *testing.T) { }, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("target.apiVersion") + fe := apis.ErrMissingField("channel.apiVersion") return fe }(), }, { @@ -654,7 +654,7 @@ func TestValidReply(t *testing.T) { }, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("target.kind") + fe := apis.ErrMissingField("channel.kind") return fe }(), }, { @@ -696,7 +696,7 @@ func TestValidReply(t *testing.T) { }, }, want: func() *apis.FieldError { - fe := apis.ErrDisallowedFields("target.Namespace") + fe := apis.ErrDisallowedFields("channel.Namespace") fe.Details = "only name, apiVersion and kind are supported fields" return fe }(), From 2f65bb8e149c04801a66ed6e7dba47132e6ddd24 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Fri, 2 Nov 2018 08:35:34 -0700 Subject: [PATCH 16/54] Rename fromReady to channelReady, to reflect the change in field name in #563. (#577) --- .../eventing/v1alpha1/subscription_types.go | 16 ++--- .../v1alpha1/subscription_types_test.go | 62 +++++++++---------- .../eventing/subscription/reconcile.go | 2 +- 3 files changed, 40 insertions(+), 40 deletions(-) diff --git a/pkg/apis/eventing/v1alpha1/subscription_types.go b/pkg/apis/eventing/v1alpha1/subscription_types.go index 72dcb5fd7a6..97a60e60670 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_types.go +++ b/pkg/apis/eventing/v1alpha1/subscription_types.go @@ -159,8 +159,8 @@ type ReplyStrategy struct { } // subCondSet is a condition set with Ready as the happy condition and -// ReferencesResolved and FromReady as the dependent conditions. -var subCondSet = duckv1alpha1.NewLivingConditionSet(SubscriptionConditionReferencesResolved, SubscriptionConditionFromReady) +// ReferencesResolved and ChannelReady as the dependent conditions. +var subCondSet = duckv1alpha1.NewLivingConditionSet(SubscriptionConditionReferencesResolved, SubscriptionConditionChannelReady) // SubscriptionStatus (computed) for a subscription type SubscriptionStatus struct { @@ -191,9 +191,9 @@ const ( // resolved. SubscriptionConditionReferencesResolved duckv1alpha1.ConditionType = "Resolved" - // SubscriptionConditionFromReady has status True when controller has successfully added a subscription to From - // resource. - SubscriptionConditionFromReady duckv1alpha1.ConditionType = "FromReady" + // SubscriptionConditionChannelReady has status True when controller has successfully added a + // subscription to the spec.channel resource. + SubscriptionConditionChannelReady duckv1alpha1.ConditionType = "ChannelReady" ) // GetCondition returns the condition currently associated with the given type, or nil. @@ -216,9 +216,9 @@ func (ss *SubscriptionStatus) MarkReferencesResolved() { subCondSet.Manage(ss).MarkTrue(SubscriptionConditionReferencesResolved) } -// MarkFromReady sets the FromReady condition to True state. -func (ss *SubscriptionStatus) MarkFromReady() { - subCondSet.Manage(ss).MarkTrue(SubscriptionConditionFromReady) +// MarkChannelReady sets the ChannelReady condition to True state. +func (ss *SubscriptionStatus) MarkChannelReady() { + subCondSet.Manage(ss).MarkTrue(SubscriptionConditionChannelReady) } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object diff --git a/pkg/apis/eventing/v1alpha1/subscription_types_test.go b/pkg/apis/eventing/v1alpha1/subscription_types_test.go index b55c6d1527e..e159457d21f 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_types_test.go +++ b/pkg/apis/eventing/v1alpha1/subscription_types_test.go @@ -35,8 +35,8 @@ var subscriptionConditionReferencesResolved = duckv1alpha1.Condition{ Status: corev1.ConditionFalse, } -var subscriptionConditionFromReady = duckv1alpha1.Condition{ - Type: SubscriptionConditionFromReady, +var subscriptionConditionChannelReady = duckv1alpha1.Condition{ + Type: SubscriptionConditionChannelReady, Status: corev1.ConditionTrue, } @@ -70,11 +70,11 @@ func TestSubscriptionGetCondition(t *testing.T) { ss: &SubscriptionStatus{ Conditions: []duckv1alpha1.Condition{ subscriptionConditionReady, - subscriptionConditionFromReady, + subscriptionConditionChannelReady, }, }, - condQuery: SubscriptionConditionFromReady, - want: &subscriptionConditionFromReady, + condQuery: SubscriptionConditionChannelReady, + want: &subscriptionConditionChannelReady, }, { name: "unknown condition", ss: &SubscriptionStatus{ @@ -107,7 +107,7 @@ func TestSubscriptionInitializeConditions(t *testing.T) { ss: &SubscriptionStatus{}, want: &SubscriptionStatus{ Conditions: []duckv1alpha1.Condition{{ - Type: SubscriptionConditionFromReady, + Type: SubscriptionConditionChannelReady, Status: corev1.ConditionUnknown, }, { Type: SubscriptionConditionReady, @@ -121,13 +121,13 @@ func TestSubscriptionInitializeConditions(t *testing.T) { name: "one false", ss: &SubscriptionStatus{ Conditions: []duckv1alpha1.Condition{{ - Type: SubscriptionConditionFromReady, + Type: SubscriptionConditionChannelReady, Status: corev1.ConditionFalse, }}, }, want: &SubscriptionStatus{ Conditions: []duckv1alpha1.Condition{{ - Type: SubscriptionConditionFromReady, + Type: SubscriptionConditionChannelReady, Status: corev1.ConditionFalse, }, { Type: SubscriptionConditionReady, @@ -147,7 +147,7 @@ func TestSubscriptionInitializeConditions(t *testing.T) { }, want: &SubscriptionStatus{ Conditions: []duckv1alpha1.Condition{{ - Type: SubscriptionConditionFromReady, + Type: SubscriptionConditionChannelReady, Status: corev1.ConditionUnknown, }, { Type: SubscriptionConditionReady, @@ -172,30 +172,30 @@ func TestSubscriptionInitializeConditions(t *testing.T) { func TestSubscriptionIsReady(t *testing.T) { tests := []struct { - name string - markResolved bool - markFromReady bool - wantReady bool + name string + markResolved bool + markChannelReady bool + wantReady bool }{{ - name: "all happy", - markResolved: true, - markFromReady: true, - wantReady: true, + name: "all happy", + markResolved: true, + markChannelReady: true, + wantReady: true, }, { - name: "one sad", - markResolved: false, - markFromReady: true, - wantReady: false, + name: "one sad", + markResolved: false, + markChannelReady: true, + wantReady: false, }, { - name: "other sad", - markResolved: true, - markFromReady: false, - wantReady: false, + name: "other sad", + markResolved: true, + markChannelReady: false, + wantReady: false, }, { - name: "both sad", - markResolved: false, - markFromReady: false, - wantReady: false, + name: "both sad", + markResolved: false, + markChannelReady: false, + wantReady: false, }} for _, test := range tests { t.Run(test.name, func(t *testing.T) { @@ -203,8 +203,8 @@ func TestSubscriptionIsReady(t *testing.T) { if test.markResolved { ss.MarkReferencesResolved() } - if test.markFromReady { - ss.MarkFromReady() + if test.markChannelReady { + ss.MarkChannelReady() } got := ss.IsReady() if test.wantReady != got { diff --git a/pkg/controller/eventing/subscription/reconcile.go b/pkg/controller/eventing/subscription/reconcile.go index 27154d8522e..b941e9c8763 100644 --- a/pkg/controller/eventing/subscription/reconcile.go +++ b/pkg/controller/eventing/subscription/reconcile.go @@ -135,7 +135,7 @@ func (r *reconciler) reconcile(subscription *v1alpha1.Subscription) error { return err } // Everything went well, set the fact that subscriptions have been modified - subscription.Status.MarkFromReady() + subscription.Status.MarkChannelReady() return nil } From 9a45a796065c8961230f4f1ffa9725a729947caa Mon Sep 17 00:00:00 2001 From: Matthias Wessendorf Date: Fri, 2 Nov 2018 17:02:34 +0100 Subject: [PATCH 17/54] Adding as shortname for the CRD (#571) --- config/300-clusterchannelprovisioner.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/config/300-clusterchannelprovisioner.yaml b/config/300-clusterchannelprovisioner.yaml index cb2361f52b8..d02f068f27b 100644 --- a/config/300-clusterchannelprovisioner.yaml +++ b/config/300-clusterchannelprovisioner.yaml @@ -26,4 +26,6 @@ spec: - all - knative - eventing + shortNames: + - ccp scope: Cluster From 89692b95d801b583665fda25ffe17be5faf1efc7 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Fri, 2 Nov 2018 09:28:34 -0700 Subject: [PATCH 18/54] Make the subscription controller resilient to empty subscriber and reply fields. (#575) --- .../eventing/subscription/reconcile.go | 12 +- .../eventing/subscription/reconcile_test.go | 123 ++++++++++++++++++ 2 files changed, 133 insertions(+), 2 deletions(-) diff --git a/pkg/controller/eventing/subscription/reconcile.go b/pkg/controller/eventing/subscription/reconcile.go index b941e9c8763..10b0eb0a175 100644 --- a/pkg/controller/eventing/subscription/reconcile.go +++ b/pkg/controller/eventing/subscription/reconcile.go @@ -96,7 +96,7 @@ func (r *reconciler) reconcile(subscription *v1alpha1.Subscription) error { } subscriberURI := "" - if subscription.Spec.Subscriber != nil { + if !isNilOrEmptySubscriber(subscription.Spec.Subscriber) { subscriberURI, err = r.resolveSubscriberSpec(subscription.Namespace, *subscription.Spec.Subscriber) if err != nil { glog.Warningf("Failed to resolve Subscriber %+v : %s", *subscription.Spec.Subscriber, err) @@ -110,7 +110,7 @@ func (r *reconciler) reconcile(subscription *v1alpha1.Subscription) error { } replyURI := "" - if subscription.Spec.Reply != nil { + if !isNilOrEmptyReply(subscription.Spec.Reply) { replyURI, err = r.resolveResult(subscription.Namespace, *subscription.Spec.Reply) if err != nil { glog.Warningf("Failed to resolve Result %v : %v", subscription.Spec.Reply, err) @@ -139,6 +139,14 @@ func (r *reconciler) reconcile(subscription *v1alpha1.Subscription) error { return nil } +func isNilOrEmptySubscriber(sub *v1alpha1.SubscriberSpec) bool { + return sub == nil || equality.Semantic.DeepEqual(sub, &v1alpha1.SubscriberSpec{}) +} + +func isNilOrEmptyReply(reply *v1alpha1.ReplyStrategy) bool { + return reply == nil || equality.Semantic.DeepEqual(reply, &v1alpha1.ReplyStrategy{}) +} + func (r *reconciler) updateStatus(subscription *v1alpha1.Subscription) (*v1alpha1.Subscription, error) { newSubscription := &v1alpha1.Subscription{} err := r.client.Get(context.TODO(), client.ObjectKey{Namespace: subscription.Namespace, Name: subscription.Name}, newSubscription) diff --git a/pkg/controller/eventing/subscription/reconcile_test.go b/pkg/controller/eventing/subscription/reconcile_test.go index 4ed1acb3304..aa2b2a3e04c 100644 --- a/pkg/controller/eventing/subscription/reconcile_test.go +++ b/pkg/controller/eventing/subscription/reconcile_test.go @@ -364,6 +364,101 @@ var testCases = []controllertesting.TestCase{ }, }, }, + }, { + Name: "new subscription: adds status, all targets resolved, subscribers modified -- empty but non-nil reply", + InitialState: []runtime.Object{ + getNewSubscriptionWithEmptyNonNilReply(), + }, + // TODO: JSON patch is not working on the fake, see + // https://github.com/kubernetes/client-go/issues/478. Marking this as expecting a specific + // failure for now, until upstream is fixed. + WantResult: reconcile.Result{}, + WantPresent: []runtime.Object{ + getNewSubscriptionWithReferencesResolvedAndPhysicalSubscriberAndNoReply(), + }, + WantErrMsg: "invalid JSON document", + Scheme: scheme.Scheme, + Objects: []runtime.Object{ + // Source channel + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), + "kind": channelKind, + "metadata": map[string]interface{}{ + "namespace": testNS, + "name": fromChannelName, + }, + "spec": map[string]interface{}{ + "channelable": map[string]interface{}{}, + }, + }, + }, + // Subscriber (using knative route) + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": "serving.knative.dev/v1alpha1", + "kind": routeKind, + "metadata": map[string]interface{}{ + "namespace": testNS, + "name": routeName, + }, + "status": map[string]interface{}{ + "targetable": map[string]interface{}{ + "domainInternal": targetDNS, + }, + }, + }, + }, + }, + }, { + Name: "new subscription: adds status, all targets resolved, subscribers modified -- empty but non-nil subscriber", + InitialState: []runtime.Object{ + getNewSubscriptionWithEmptyNonNilSubscriber(), + }, + // TODO: JSON patch is not working on the fake, see + // https://github.com/kubernetes/client-go/issues/478. Marking this as expecting a specific + // failure for now, until upstream is fixed. + WantResult: reconcile.Result{}, + WantPresent: []runtime.Object{ + getNewSubscriptionWithReferencesResolvedAndPhysicalReplyAndNoSubscriber(), + }, + WantErrMsg: "invalid JSON document", + Scheme: scheme.Scheme, + Objects: []runtime.Object{ + // Source channel + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), + "kind": channelKind, + "metadata": map[string]interface{}{ + "namespace": testNS, + "name": fromChannelName, + }, + "spec": map[string]interface{}{ + "channelable": map[string]interface{}{}, + }, + }, + }, + // Reply channel + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), + "kind": channelKind, + "metadata": map[string]interface{}{ + "namespace": testNS, + "name": resultChannelName, + }, + "spec": map[string]interface{}{ + "channelable": map[string]interface{}{}, + }, + "status": map[string]interface{}{ + "sinkable": map[string]interface{}{ + "domainInternal": sinkableDNS, + }, + }, + }, + }, + }, }, { Name: "new subscription to K8s Service: adds status, all targets resolved, subscribers modified", InitialState: []runtime.Object{ @@ -685,6 +780,18 @@ func getNewSubscription() *eventingv1alpha1.Subscription { return subscription } +func getNewSubscriptionWithEmptyNonNilReply() *eventingv1alpha1.Subscription { + sub := getNewSubscription() + sub.Spec.Reply = &eventingv1alpha1.ReplyStrategy{} + return sub +} + +func getNewSubscriptionWithEmptyNonNilSubscriber() *eventingv1alpha1.Subscription { + sub := getNewSubscription() + sub.Spec.Subscriber = &eventingv1alpha1.SubscriberSpec{} + return sub +} + func getNewSourceSubscription() *eventingv1alpha1.Subscription { sub := getNewSubscription() sub.Spec.Channel = corev1.ObjectReference{ @@ -751,6 +858,22 @@ func getNewSubscriptionWithUnknownConditionsAndPhysicalSubscriber() *eventingv1a return s } +func getNewSubscriptionWithReferencesResolvedAndPhysicalSubscriberAndNoReply() *eventingv1alpha1.Subscription { + s := getNewSubscriptionWithEmptyNonNilReply() + s.Status.InitializeConditions() + s.Status.MarkReferencesResolved() + s.Status.PhysicalSubscription.SubscriberURI = domainToURL(targetDNS) + return s +} + +func getNewSubscriptionWithReferencesResolvedAndPhysicalReplyAndNoSubscriber() *eventingv1alpha1.Subscription { + s := getNewSubscriptionWithEmptyNonNilSubscriber() + s.Status.InitializeConditions() + s.Status.MarkReferencesResolved() + s.Status.PhysicalSubscription.ReplyURI = domainToURL(sinkableDNS) + return s +} + func getNewSubscriptionWithReferencesResolvedAndPhysicalSubscriberReply() *eventingv1alpha1.Subscription { s := getNewSubscriptionWithUnknownConditions() s.Status.MarkReferencesResolved() From 0782c525610c1cb1d8d11bc683da640b95f624a5 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Fri, 2 Nov 2018 13:18:35 -0400 Subject: [PATCH 19/54] Align Subscribable source with spec (#565) * Align Subscribable source with spec - renamed Channelable -> Subscribable - add ObjectReference to ChannelSubscriberSpec to track originating resource - removed Subscribable as a public interface in the spec - preserved as an internal interface * polish --- docs/spec/interfaces.md | 25 --------- docs/spec/spec.md | 27 +++++----- ...nelable_types.go => subscribable_types.go} | 52 ++++++++++++++----- .../duck/v1alpha1/zz_generated.deepcopy.go | 28 +++++++--- pkg/apis/eventing/v1alpha1/channel_types.go | 4 +- .../eventing/v1alpha1/channel_validation.go | 8 +-- .../v1alpha1/channel_validation_test.go | 12 ++--- pkg/apis/eventing/v1alpha1/implements_test.go | 2 +- .../subscribable_channelable_validation.go | 4 +- .../v1alpha1/zz_generated.deepcopy.go | 6 +-- .../eventing/inmemory/channel/reconcile.go | 4 +- .../inmemory/channel/reconcile_test.go | 6 +-- .../eventing/subscription/reconcile.go | 19 ++++--- .../eventing/subscription/reconcile_test.go | 51 +++++++++++------- 14 files changed, 141 insertions(+), 107 deletions(-) rename pkg/apis/duck/v1alpha1/{channelable_types.go => subscribable_types.go} (55%) diff --git a/docs/spec/interfaces.md b/docs/spec/interfaces.md index 3142554fb24..b339dde1b86 100644 --- a/docs/spec/interfaces.md +++ b/docs/spec/interfaces.md @@ -1,30 +1,5 @@ # Interface Contracts -## Subscribable - -A **Subscribable** resource contains a list of subscribers and is responsible -for delivering events to each of them. Subscriptions only allow Channels to be -Subscribable (via `spec.from` on the Subscription) at the moment, but this may -be revisited with future experience. _Channel_ as the target of a -_Subscription_'s `from` field. - - -### Control Plane - -The **Subscribable** resource stores a list of resolved _Subscriptions_ in the -resource's `spec.subscribers` field. The Subscription Controller is responsible -for resolving any ObjectReferences (such as _call_ and _result_) in the -_Subscription_ to network addresses. - -### Data Plane - -**Subscribable** resources will attempt delivery to each of the _subscribers_ -at least once, and retry if the subscriber returns errors. - - - ---- - ## Targetable A **Targetable** resource represents an endpoint that receives events and diff --git a/docs/spec/spec.md b/docs/spec/spec.md index c4f0abe9d03..a84c4f73bbd 100644 --- a/docs/spec/spec.md +++ b/docs/spec/spec.md @@ -24,11 +24,11 @@ its subscribers._ #### Spec -| Field | Type | Description | Constraints | -| ------------- | ---------------------------------- | -------------------------------------------------------------------------- | -------------------------------------- | -| provisioner\* | ObjectReference | The name of the provisioner to create the resources that back the Channel. | Immutable. | -| arguments | runtime.RawExtension (JSON object) | Arguments to be passed to the provisioner. | | -| subscribers | ChannelSubscriberSpec[] | Information about subscriptions used to implement message forwarding. | Filled out by Subscription Controller. | +| Field | Type | Description | Constraints | +| ------------------------ | ---------------------------------- | -------------------------------------------------------------------------- | -------------------------------------- | +| provisioner\* | ObjectReference | The name of the provisioner to create the resources that back the Channel. | Immutable. | +| arguments | runtime.RawExtension (JSON object) | Arguments to be passed to the provisioner. | | +| subscribable.subscribers | ChannelSubscriberSpec[] | Information about subscriptions used to implement message forwarding. | Filled out by Subscription Controller. | \*: Required @@ -106,11 +106,11 @@ _Describes a linkage between a Channel and a Targetable and/or Sinkable._ ### Life Cycle -| Action | Reactions | Constraints | -| ------ | ----------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | +| Action | Reactions | Constraints | +| ------ | --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- | | Create | The subscription controller adds the resolved URIs of `subscriber` and `reply` to the `subscribers` field in the `channel` _Subscribable_ resource. | | -| Update | | | -| Delete | | | +| Update | | | +| Delete | | | --- @@ -161,10 +161,11 @@ or a Channel system that receives and delivers events._ ### ChannelSubscriberSpec -| Field | Type | Description | Constraints | -| ------------- | ------ | ------------------------------------------------ | -------------- | -| subscriberURI | String | The URI name of the endpoint for the subscriber. | Must be a URL. | -| replyURI | String | The URI name of the endpoint for the reply. | Must be a URL. | +| Field | Type | Description | Constraints | +| ------------- | --------------- | -------------------------------------------------------------- | -------------- | +| ref | ObjectReference | The Subscription this ChannelSubscriberSpec was resolved from. | | +| subscriberURI | String | The URI name of the endpoint for the subscriber. | Must be a URL. | +| replyURI | String | The URI name of the endpoint for the reply. | Must be a URL. | ### ReplyStrategy diff --git a/pkg/apis/duck/v1alpha1/channelable_types.go b/pkg/apis/duck/v1alpha1/subscribable_types.go similarity index 55% rename from pkg/apis/duck/v1alpha1/channelable_types.go rename to pkg/apis/duck/v1alpha1/subscribable_types.go index f208377aa81..b0ca54d2fcd 100644 --- a/pkg/apis/duck/v1alpha1/channelable_types.go +++ b/pkg/apis/duck/v1alpha1/subscribable_types.go @@ -18,61 +18,85 @@ package v1alpha1 import ( "github.com/knative/pkg/apis/duck" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" ) -// Channelable is the schema for the channelable portion of the spec +// Subscribable is the schema for the subscribable portion of the spec // section of the resource. -type Channelable struct { +type Subscribable struct { // TODO: What is actually required here for Channel spec. // This is the list of subscriptions for this channel. Subscribers []ChannelSubscriberSpec `json:"subscribers,omitempty"` } // ChannelSubscriberSpec defines a single subscriber to a Channel. +// Ref is a reference to the Subscription this ChannelSubscriberSpec was created for // SubscriberURI is the endpoint for the subscriber // ReplyURI is the endpoint for the reply -// At least one of them must be present +// At least one of SubscriberURI and ReplyURI must be present type ChannelSubscriberSpec struct { + // +optional + Ref *corev1.ObjectReference `json:"ref,omitempty"` // +optional SubscriberURI string `json:"subscriberURI,omitempty"` // +optional ReplyURI string `json:"replyURI,omitempty"` } -// DuckChannel is a skeleton type wrapping Channelable in the manner we expect resource writers +// Channel is a skeleton type wrapping Subscribable in the manner we expect resource writers // defining compatible resources to embed it. We will typically use this type to deserialize -// Channelable ObjectReferences and access the Channelable data. This is not a real resource. +// Channel ObjectReferences and access the Subscription data. This is not a real resource. type Channel struct { metav1.TypeMeta `json:",inline"` metav1.ObjectMeta `json:"metadata,omitempty"` - // ChannelSpec is the part where Channelable object is - // configured as to be compatible with Channelable contract. + // ChannelSpec is the part where Subscribable object is + // configured as to be compatible with Subscribable contract. Spec ChannelSpec `json:"spec"` } -// DuckChannelSpec shows how we expect folks to embed Channelable in their Spec field. +// ChannelSpec shows how we expect folks to embed Subscribable in their Spec field. type ChannelSpec struct { - Channelable *Channelable `json:"channelable,omitempty"` + Subscribable *Subscribable `json:"subscribable,omitempty"` } // GetFullType implements duck.Implementable -func (_ *Channelable) GetFullType() duck.Populatable { +func (s *Subscribable) GetFullType() duck.Populatable { return &Channel{} } // Populate implements duck.Populatable -func (t *Channel) Populate() { - t.Spec.Channelable = &Channelable{ +func (c *Channel) Populate() { + c.Spec.Subscribable = &Subscribable{ // Populate ALL fields - Subscribers: []ChannelSubscriberSpec{{"call1", "sink2"}, {"call2", "sink2"}}, + Subscribers: []ChannelSubscriberSpec{{ + Ref: &corev1.ObjectReference{ + APIVersion: "eventing.knative.dev/v1alpha1", + Kind: "Subscription", + Name: "subscription1", + Namespace: "default", + UID: "2f9b5e8e-deb6-11e8-9f32-f2801f1b9fd1", + }, + SubscriberURI: "call1", + ReplyURI: "sink2", + }, { + Ref: &corev1.ObjectReference{ + APIVersion: "eventing.knative.dev/v1alpha1", + Kind: "Subscription", + Name: "subscription2", + Namespace: "default", + UID: "34c5aec8-deb6-11e8-9f32-f2801f1b9fd1", + }, + SubscriberURI: "call2", + ReplyURI: "sink2", + }}, } } // GetListType implements apis.Listable -func (r *Channel) GetListType() runtime.Object { +func (c *Channel) GetListType() runtime.Object { return &ChannelList{} } diff --git a/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go index c3707fddd3d..064638afd33 100644 --- a/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go @@ -21,6 +21,7 @@ limitations under the License. package v1alpha1 import ( + v1 "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -79,12 +80,12 @@ func (in *ChannelList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ChannelSpec) DeepCopyInto(out *ChannelSpec) { *out = *in - if in.Channelable != nil { - in, out := &in.Channelable, &out.Channelable + if in.Subscribable != nil { + in, out := &in.Subscribable, &out.Subscribable if *in == nil { *out = nil } else { - *out = new(Channelable) + *out = new(Subscribable) (*in).DeepCopyInto(*out) } } @@ -104,6 +105,15 @@ func (in *ChannelSpec) DeepCopy() *ChannelSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ChannelSubscriberSpec) DeepCopyInto(out *ChannelSubscriberSpec) { *out = *in + if in.Ref != nil { + in, out := &in.Ref, &out.Ref + if *in == nil { + *out = nil + } else { + *out = new(v1.ObjectReference) + **out = **in + } + } return } @@ -118,22 +128,24 @@ func (in *ChannelSubscriberSpec) DeepCopy() *ChannelSubscriberSpec { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Channelable) DeepCopyInto(out *Channelable) { +func (in *Subscribable) DeepCopyInto(out *Subscribable) { *out = *in if in.Subscribers != nil { in, out := &in.Subscribers, &out.Subscribers *out = make([]ChannelSubscriberSpec, len(*in)) - copy(*out, *in) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Channelable. -func (in *Channelable) DeepCopy() *Channelable { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subscribable. +func (in *Subscribable) DeepCopy() *Subscribable { if in == nil { return nil } - out := new(Channelable) + out := new(Subscribable) in.DeepCopyInto(out) return out } diff --git a/pkg/apis/eventing/v1alpha1/channel_types.go b/pkg/apis/eventing/v1alpha1/channel_types.go index 801e7a4b077..aea8620132e 100644 --- a/pkg/apis/eventing/v1alpha1/channel_types.go +++ b/pkg/apis/eventing/v1alpha1/channel_types.go @@ -72,8 +72,8 @@ type ChannelSpec struct { // +optional Arguments *runtime.RawExtension `json:"arguments,omitempty"` - // Channel conforms to Duck type Channelable. - Channelable *eventingduck.Channelable `json:"channelable,omitempty"` + // Channel conforms to Duck type Subscribable. + Subscribable *eventingduck.Subscribable `json:"subscribable,omitempty"` } var chanCondSet = duckv1alpha1.NewLivingConditionSet(ChannelConditionProvisioned, ChannelConditionSinkable) diff --git a/pkg/apis/eventing/v1alpha1/channel_validation.go b/pkg/apis/eventing/v1alpha1/channel_validation.go index d686a8c7bfd..054526b9fe8 100644 --- a/pkg/apis/eventing/v1alpha1/channel_validation.go +++ b/pkg/apis/eventing/v1alpha1/channel_validation.go @@ -34,12 +34,12 @@ func (cs *ChannelSpec) Validate() *apis.FieldError { errs = errs.Also(apis.ErrMissingField("provisioner")) } - if cs.Channelable != nil { - for i, subscriber := range cs.Channelable.Subscribers { + if cs.Subscribable != nil { + for i, subscriber := range cs.Subscribable.Subscribers { if subscriber.ReplyURI == "" && subscriber.SubscriberURI == "" { fe := apis.ErrMissingField("replyURI", "subscriberURI") fe.Details = "expected at least one of, got none" - errs = errs.Also(fe.ViaField(fmt.Sprintf("subscriber[%d]", i)).ViaField("channelable")) + errs = errs.Also(fe.ViaField(fmt.Sprintf("subscriber[%d]", i)).ViaField("subscribable")) } } } @@ -55,7 +55,7 @@ func (current *Channel) CheckImmutableFields(og apis.Immutable) *apis.FieldError if !ok { return &apis.FieldError{Message: "The provided resource was not a Channel"} } - ignoreArguments := cmpopts.IgnoreFields(ChannelSpec{}, "Arguments", "Channelable") + ignoreArguments := cmpopts.IgnoreFields(ChannelSpec{}, "Arguments", "Subscribable") if diff := cmp.Diff(original.Spec, current.Spec, ignoreArguments); diff != "" { return &apis.FieldError{ Message: "Immutable fields changed", diff --git a/pkg/apis/eventing/v1alpha1/channel_validation_test.go b/pkg/apis/eventing/v1alpha1/channel_validation_test.go index 0e4545bb943..d21159209cc 100644 --- a/pkg/apis/eventing/v1alpha1/channel_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/channel_validation_test.go @@ -52,7 +52,7 @@ func TestChannelValidation(t *testing.T) { Provisioner: &corev1.ObjectReference{ Name: "foo", }, - Channelable: &eventingduck.Channelable{ + Subscribable: &eventingduck.Subscribable{ Subscribers: []eventingduck.ChannelSubscriberSpec{{ SubscriberURI: "subscriberendpoint", ReplyURI: "resultendpoint", @@ -67,7 +67,7 @@ func TestChannelValidation(t *testing.T) { Provisioner: &corev1.ObjectReference{ Name: "foo", }, - Channelable: &eventingduck.Channelable{ + Subscribable: &eventingduck.Subscribable{ Subscribers: []eventingduck.ChannelSubscriberSpec{{ SubscriberURI: "subscriberendpoint", ReplyURI: "replyendpoint", @@ -75,7 +75,7 @@ func TestChannelValidation(t *testing.T) { }}, }, want: func() *apis.FieldError { - fe := apis.ErrMissingField("spec.channelable.subscriber[1].replyURI", "spec.channelable.subscriber[1].subscriberURI") + fe := apis.ErrMissingField("spec.subscribable.subscriber[1].replyURI", "spec.subscribable.subscriber[1].subscriberURI") fe.Details = "expected at least one of, got none" return fe }(), @@ -86,17 +86,17 @@ func TestChannelValidation(t *testing.T) { Provisioner: &corev1.ObjectReference{ Name: "foo", }, - Channelable: &eventingduck.Channelable{ + Subscribable: &eventingduck.Subscribable{ Subscribers: []eventingduck.ChannelSubscriberSpec{{}, {}}, }, }, }, want: func() *apis.FieldError { var errs *apis.FieldError - fe := apis.ErrMissingField("spec.channelable.subscriber[0].replyURI", "spec.channelable.subscriber[0].subscriberURI") + fe := apis.ErrMissingField("spec.subscribable.subscriber[0].replyURI", "spec.subscribable.subscriber[0].subscriberURI") fe.Details = "expected at least one of, got none" errs = errs.Also(fe) - fe = apis.ErrMissingField("spec.channelable.subscriber[1].replyURI", "spec.channelable.subscriber[1].subscriberURI") + fe = apis.ErrMissingField("spec.subscribable.subscriber[1].replyURI", "spec.subscribable.subscriber[1].subscriberURI") fe.Details = "expected at least one of, got none" errs = errs.Also(fe) return errs diff --git a/pkg/apis/eventing/v1alpha1/implements_test.go b/pkg/apis/eventing/v1alpha1/implements_test.go index 83953a3f959..9e701cf8659 100644 --- a/pkg/apis/eventing/v1alpha1/implements_test.go +++ b/pkg/apis/eventing/v1alpha1/implements_test.go @@ -28,7 +28,7 @@ func TestTypesImplements(t *testing.T) { }{ // Channel {instance: &Channel{}, iface: &duckv1alpha1.Conditions{}}, - {instance: &Channel{}, iface: &eventingduck.Channelable{}}, + {instance: &Channel{}, iface: &eventingduck.Subscribable{}}, {instance: &Channel{}, iface: &duckv1alpha1.Sinkable{}}, // ClusterChannelProvisioner {instance: &ClusterChannelProvisioner{}, iface: &duckv1alpha1.Conditions{}}, diff --git a/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go b/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go index 452ab0dcbad..804f8bdc885 100644 --- a/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go +++ b/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go @@ -50,7 +50,7 @@ func isValidChannel(f corev1.ObjectReference) *apis.FieldError { return errs } -func isChannelableEmpty(f corev1.ObjectReference) bool { +func isSubscribableEmpty(f corev1.ObjectReference) bool { return equality.Semantic.DeepEqual(f, corev1.ObjectReference{}) } @@ -58,7 +58,7 @@ func isChannelableEmpty(f corev1.ObjectReference) bool { // - Kind == 'Channel' // - APIVersion == 'eventing.knative.dev/v1alpha1' // - Name == not empty -func isValidChannelable(f corev1.ObjectReference) *apis.FieldError { +func isValidSubscribable(f corev1.ObjectReference) *apis.FieldError { errs := isValidObjectReference(f) if f.Kind != "Channel" { diff --git a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go index a50e2e78073..8b14c429ef1 100644 --- a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go @@ -109,12 +109,12 @@ func (in *ChannelSpec) DeepCopyInto(out *ChannelSpec) { (*in).DeepCopyInto(*out) } } - if in.Channelable != nil { - in, out := &in.Channelable, &out.Channelable + if in.Subscribable != nil { + in, out := &in.Subscribable, &out.Subscribable if *in == nil { *out = nil } else { - *out = new(duck_v1alpha1.Channelable) + *out = new(duck_v1alpha1.Subscribable) (*in).DeepCopyInto(*out) } } diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 1ea43ceffb5..7d63e34925b 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -389,9 +389,9 @@ func multiChannelFanoutConfig(channels []eventingv1alpha1.Channel) *multichannel Namespace: c.Namespace, Name: c.Name, } - if c.Spec.Channelable != nil { + if c.Spec.Subscribable != nil { channelConfig.FanoutConfig = fanout.Config{ - Subscriptions: c.Spec.Channelable.Subscribers, + Subscriptions: c.Spec.Subscribable.Subscribers, } } cc = append(cc, channelConfig) diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index 236a666071a..7f679d9360e 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -114,7 +114,7 @@ var ( Provisioner: &corev1.ObjectReference{ Name: ccpName, }, - Channelable: &eventingduck.Channelable{ + Subscribable: &eventingduck.Subscribable{ Subscribers: []eventingduck.ChannelSubscriberSpec{ { SubscriberURI: "foo", @@ -142,7 +142,7 @@ var ( Provisioner: &corev1.ObjectReference{ Name: "some-other-provisioner", }, - Channelable: &eventingduck.Channelable{ + Subscribable: &eventingduck.Subscribable{ Subscribers: []eventingduck.ChannelSubscriberSpec{ { SubscriberURI: "anything", @@ -163,7 +163,7 @@ var ( Provisioner: &corev1.ObjectReference{ Name: ccpName, }, - Channelable: &eventingduck.Channelable{ + Subscribable: &eventingduck.Subscribable{ Subscribers: []eventingduck.ChannelSubscriberSpec{ { SubscriberURI: "steve", diff --git a/pkg/controller/eventing/subscription/reconcile.go b/pkg/controller/eventing/subscription/reconcile.go index 10b0eb0a175..3d36c23aaaf 100644 --- a/pkg/controller/eventing/subscription/reconcile.go +++ b/pkg/controller/eventing/subscription/reconcile.go @@ -258,9 +258,9 @@ func (r *reconciler) syncPhysicalChannel(sub *v1alpha1.Subscription) error { return err } - channelable := r.createChannelable(subs) + subscribable := r.createSubscribable(subs) - return r.patchPhysicalFrom(sub.Namespace, sub.Spec.Channel, channelable) + return r.patchPhysicalFrom(sub.Namespace, sub.Spec.Channel, subscribable) } func (r *reconciler) listAllSubscriptionsWithPhysicalChannel(sub *v1alpha1.Subscription) ([]v1alpha1.Subscription, error) { @@ -305,11 +305,18 @@ func (r *reconciler) listAllSubscriptionsWithPhysicalChannel(sub *v1alpha1.Subsc } } -func (r *reconciler) createChannelable(subs []v1alpha1.Subscription) *eventingduck.Channelable { - rv := &eventingduck.Channelable{} +func (r *reconciler) createSubscribable(subs []v1alpha1.Subscription) *eventingduck.Subscribable { + rv := &eventingduck.Subscribable{} for _, sub := range subs { if sub.Status.PhysicalSubscription.SubscriberURI != "" || sub.Status.PhysicalSubscription.ReplyURI != "" { rv.Subscribers = append(rv.Subscribers, eventingduck.ChannelSubscriberSpec{ + Ref: &corev1.ObjectReference{ + APIVersion: sub.APIVersion, + Kind: sub.Kind, + Namespace: sub.Namespace, + Name: sub.Name, + UID: sub.UID, + }, SubscriberURI: sub.Status.PhysicalSubscription.SubscriberURI, ReplyURI: sub.Status.PhysicalSubscription.ReplyURI, }) @@ -318,7 +325,7 @@ func (r *reconciler) createChannelable(subs []v1alpha1.Subscription) *eventingdu return rv } -func (r *reconciler) patchPhysicalFrom(namespace string, physicalFrom corev1.ObjectReference, subs *eventingduck.Channelable) error { +func (r *reconciler) patchPhysicalFrom(namespace string, physicalFrom corev1.ObjectReference, subs *eventingduck.Subscribable) error { // First get the original object and convert it to only the bits we care about s, err := r.fetchObjectReference(namespace, &physicalFrom) if err != nil { @@ -331,7 +338,7 @@ func (r *reconciler) patchPhysicalFrom(namespace string, physicalFrom corev1.Obj } after := original.DeepCopy() - after.Spec.Channelable = subs + after.Spec.Subscribable = subs patch, err := duck.CreatePatch(original, after) if err != nil { diff --git a/pkg/controller/eventing/subscription/reconcile_test.go b/pkg/controller/eventing/subscription/reconcile_test.go index aa2b2a3e04c..349cf106404 100644 --- a/pkg/controller/eventing/subscription/reconcile_test.go +++ b/pkg/controller/eventing/subscription/reconcile_test.go @@ -46,6 +46,7 @@ const ( channelKind = "Channel" routeKind = "Route" sourceKind = "Source" + subscriptionKind = "Subscription" targetDNS = "myfunction.mynamespace.svc.cluster.local" sinkableDNS = "myresultchannel.mynamespace.svc.cluster.local" eventType = "myeventtype" @@ -73,7 +74,7 @@ var testCases = []controllertesting.TestCase{ }, WantErrMsg: `channels.eventing.knative.dev "fromchannel" not found`, }, { - Name: "subscription, but From is not channelable", + Name: "subscription, but From is not subscribable", InitialState: []runtime.Object{ getNewSourceSubscription(), }, @@ -122,7 +123,7 @@ var testCases = []controllertesting.TestCase{ "name": resultChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, "status": map[string]interface{}{ "sinkable": map[string]interface{}{ @@ -153,7 +154,7 @@ var testCases = []controllertesting.TestCase{ "name": fromChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, }, }, @@ -179,7 +180,7 @@ var testCases = []controllertesting.TestCase{ "name": fromChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, }, }, @@ -219,7 +220,7 @@ var testCases = []controllertesting.TestCase{ "name": fromChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, }, }, @@ -264,7 +265,7 @@ var testCases = []controllertesting.TestCase{ "name": fromChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, }, }, @@ -294,7 +295,7 @@ var testCases = []controllertesting.TestCase{ "name": resultChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, }, }, @@ -324,7 +325,7 @@ var testCases = []controllertesting.TestCase{ "name": fromChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, }, }, @@ -354,7 +355,7 @@ var testCases = []controllertesting.TestCase{ "name": resultChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, "status": map[string]interface{}{ "sinkable": map[string]interface{}{ @@ -485,7 +486,7 @@ var testCases = []controllertesting.TestCase{ "name": fromChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, }, }, @@ -510,7 +511,7 @@ var testCases = []controllertesting.TestCase{ "name": resultChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, "status": map[string]interface{}{ "sinkable": map[string]interface{}{ @@ -545,7 +546,7 @@ var testCases = []controllertesting.TestCase{ "name": sourceName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, }, }, @@ -559,7 +560,7 @@ var testCases = []controllertesting.TestCase{ "name": fromChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, }, }, @@ -589,7 +590,7 @@ var testCases = []controllertesting.TestCase{ "name": resultChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, "status": map[string]interface{}{ "sinkable": map[string]interface{}{ @@ -640,7 +641,7 @@ var testCases = []controllertesting.TestCase{ "name": sourceName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, }, }, @@ -654,7 +655,7 @@ var testCases = []controllertesting.TestCase{ "name": fromChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, }, }, @@ -684,7 +685,7 @@ var testCases = []controllertesting.TestCase{ "name": resultChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, "status": map[string]interface{}{ "sinkable": map[string]interface{}{ @@ -953,13 +954,27 @@ func getChannelWithMultipleSubscriptions() *eventingv1alpha1.Channel { }, ObjectMeta: om(testNS, fromChannelName), Spec: eventingv1alpha1.ChannelSpec{ - Channelable: &eventingduck.Channelable{ + Subscribable: &eventingduck.Subscribable{ Subscribers: []eventingduck.ChannelSubscriberSpec{ { + Ref: &corev1.ObjectReference{ + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: subscriptionKind, + Namespace: testNS, + Name: subscriptionName, + UID: "", + }, SubscriberURI: targetDNS, ReplyURI: sinkableDNS, }, { + Ref: &corev1.ObjectReference{ + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: subscriptionKind, + Namespace: testNS, + Name: "renamed", + UID: "renamed-UID", + }, ReplyURI: otherSinkableDNS, }, }, From 370145ff70e409b2ddf0b8e25b24790309dc1e49 Mon Sep 17 00:00:00 2001 From: Scott Nichols <32305648+n3wscott@users.noreply.github.com> Date: Fri, 2 Nov 2018 13:18:35 -0700 Subject: [PATCH 20/54] Move cloudevents lib out of eventing. (#578) --- Gopkg.lock | 8 +- Gopkg.toml | 4 +- cmd/sendevent/main.go | 8 +- pkg/event/event_test.go | 264 ---------- pkg/event/handler_test.go | 461 ------------------ .../receive_adapter/receive_adapter.go | 8 +- .../github/receive_adapter/receive_adapter.go | 8 +- .../receive_adapter/receive_adapter.go | 10 +- test/e2e/k8sevents/function.go | 7 +- .../knative/pkg/apis/duck/verify.go | 2 +- .../knative/pkg/cloudevents}/doc.go | 5 +- .../pkg/cloudevents}/encoding_binary.go | 2 +- .../pkg/cloudevents}/encoding_structured.go | 2 +- .../knative/pkg/cloudevents}/event.go | 2 +- .../knative/pkg/cloudevents}/handler.go | 2 +- .../knative/pkg/test/spoof/spoof.go | 98 +++- .../knative/pkg/test/zipkin/util.go | 110 +++++ 17 files changed, 221 insertions(+), 780 deletions(-) delete mode 100644 pkg/event/event_test.go delete mode 100644 pkg/event/handler_test.go rename {pkg/event => vendor/github.com/knative/pkg/cloudevents}/doc.go (89%) rename {pkg/event => vendor/github.com/knative/pkg/cloudevents}/encoding_binary.go (99%) rename {pkg/event => vendor/github.com/knative/pkg/cloudevents}/encoding_structured.go (99%) rename {pkg/event => vendor/github.com/knative/pkg/cloudevents}/event.go (99%) rename {pkg/event => vendor/github.com/knative/pkg/cloudevents}/handler.go (99%) create mode 100644 vendor/github.com/knative/pkg/test/zipkin/util.go diff --git a/Gopkg.lock b/Gopkg.lock index c63559599f7..00a2677f75a 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -300,7 +300,7 @@ revision = "5c1d8c8469d1ed34b2aecf4c2305b3a57fff2ee3" [[projects]] - digest = "1:840124cac74eca13ec295942bf92693076fba45809f2f46dcb4e83cc92555fae" + digest = "1:9dd034878e35ffe80dd0a4d7991521acce80f33cc7d3d5e21c7944bb2874e037" name = "github.com/knative/pkg" packages = [ "apis", @@ -323,6 +323,7 @@ "client/informers/externalversions/istio/v1alpha3", "client/listers/authentication/v1alpha1", "client/listers/istio/v1alpha3", + "cloudevents", "configmap", "logging", "logging/logkey", @@ -330,10 +331,11 @@ "test", "test/logging", "test/spoof", + "test/zipkin", "webhook", ] pruneopts = "NUT" - revision = "d247efe41d5855f43133f9894f95ce8f9caa195f" + revision = "04b40fd38ac1cf17e0426b79860c792fd593ce71" [[projects]] digest = "1:63f3974f3afe3dc5b6a115c0d53b0897cd01be6880c4bf5d014fc69a95db6ed1" @@ -1091,7 +1093,6 @@ "cloud.google.com/go/pubsub", "github.com/Shopify/sarama", "github.com/bsm/sarama-cluster", - "github.com/davecgh/go-spew/spew", "github.com/fsnotify/fsnotify", "github.com/golang/glog", "github.com/google/go-cmp/cmp", @@ -1105,6 +1106,7 @@ "github.com/knative/pkg/client/clientset/versioned", "github.com/knative/pkg/client/informers/externalversions", "github.com/knative/pkg/client/listers/istio/v1alpha3", + "github.com/knative/pkg/cloudevents", "github.com/knative/pkg/configmap", "github.com/knative/pkg/logging", "github.com/knative/pkg/logging/logkey", diff --git a/Gopkg.toml b/Gopkg.toml index a03659e1eb1..7b6ce137575 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -76,8 +76,8 @@ required = [ [[override]] name = "github.com/knative/pkg" - # HEAD as of 2018-10-30 - revision = "d247efe41d5855f43133f9894f95ce8f9caa195f" + # HEAD as of 2018-11-02 + revision = "04b40fd38ac1cf17e0426b79860c792fd593ce71" [[constraint]] name = "github.com/knative/serving" diff --git a/cmd/sendevent/main.go b/cmd/sendevent/main.go index 491434fa398..b3f7cd80ad2 100644 --- a/cmd/sendevent/main.go +++ b/cmd/sendevent/main.go @@ -26,13 +26,13 @@ import ( "os" "time" - "github.com/knative/eventing/pkg/event" + "github.com/knative/pkg/cloudevents" "github.com/google/uuid" ) var ( - context event.EventContext + context cloudevents.EventContext webhook string data string ) @@ -61,7 +61,7 @@ func main() { } fillEventContext(&context) - req, err := event.NewRequest(webhook, untyped, context) + req, err := cloudevents.NewRequest(webhook, untyped, context) if err != nil { fmt.Printf("Failed to create request: %s", err) os.Exit(1) @@ -79,7 +79,7 @@ func main() { } } -func fillEventContext(ctx *event.EventContext) { +func fillEventContext(ctx *cloudevents.EventContext) { ctx.CloudEventsVersion = "0.1" ctx.EventTime = time.Now().UTC() diff --git a/pkg/event/event_test.go b/pkg/event/event_test.go deleted file mode 100644 index 710fa1a7719..00000000000 --- a/pkg/event/event_test.go +++ /dev/null @@ -1,264 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package event_test - -import ( - "encoding/json" - "encoding/xml" - "errors" - "fmt" - "io/ioutil" - "net/http" - "reflect" - "strings" - "testing" - "time" - - "github.com/knative/eventing/pkg/event" -) - -type FirestoreDocument struct { - Name string `json:"name"` - Fields map[string]interface{} `json:"fields"` - CreateTime time.Time `json:"createTime"` - UpdateTime time.Time `json:"updateTime"` -} - -var ( - webhook = "http://localhost/:sendEvent" -) - -// Wrap the global functions in an HTTPMarshaller interface for table-driven testing: -type defaultMarshaller int - -var Default defaultMarshaller = 0 - -func (defaultMarshaller) FromRequest(data interface{}, r *http.Request) (*event.EventContext, error) { - return event.FromRequest(data, r) -} -func (defaultMarshaller) NewRequest(urlString string, data interface{}, context event.EventContext) (*http.Request, error) { - return event.NewRequest(urlString, data, context) -} - -func TestValidRoundTrips(t *testing.T) { - doc := FirestoreDocument{ - Name: "projects/demo/databases/default/documents/users/inlined", - Fields: map[string]interface{}{ - "project": "eventing", - "handle": "@inlined", - }, - CreateTime: time.Date(1985, 6, 5, 12, 0, 0, 0, time.UTC), - UpdateTime: time.Now().UTC(), - } - - service := "firestore.googleapis.com" - - context := &event.EventContext{ - CloudEventsVersion: "0.1", - EventID: "eventid-123", - EventTime: doc.UpdateTime, - EventType: "google.firestore.document.create", - EventTypeVersion: "v1beta2", - SchemaURL: "http://type.googleapis.com/google.firestore.v1beta1.Document", - ContentType: "application/json", - Source: fmt.Sprintf("//%s/%s", service, doc.Name), - Extensions: map[string]interface{}{ - "purpose": "tbd", - }, - } - for _, test := range []struct { - name string - encoder event.HTTPMarshaller - decoder event.HTTPMarshaller - }{ - { - name: "binary -> binary", - encoder: event.Binary, - decoder: event.Binary, - }, - { - name: "binary -> default", - encoder: event.Binary, - decoder: Default, - }, - { - name: "structured -> structured", - encoder: event.Structured, - decoder: event.Structured, - }, - { - name: "structured -> default", - encoder: event.Structured, - decoder: Default, - }, - } { - t.Run(test.name, func(t *testing.T) { - req, err := test.encoder.NewRequest(webhook, doc, *context) - if err != nil { - t.Fatalf("Failed to encode event %s", err) - } - - var foundData FirestoreDocument - foundContext, err := test.decoder.FromRequest(&foundData, req) - if err != nil { - t.Fatalf("Failed to decode event %s", err) - } - - if !reflect.DeepEqual(context, foundContext) { - t.Fatalf("Context was transcoded lossily: expected=%+v got=%+v", context, foundContext) - } - if !reflect.DeepEqual(doc, foundData) { - t.Fatalf("Data was transcoded lossily: expected=%+v got=%+v", doc, foundData) - } - }) - } -} - -type Address struct { - City, State string -} -type Person struct { - XMLName xml.Name `xml:"person"` - Id int `xml:"id,attr"` - FirstName string `xml:"name>first"` - LastName string `xml:"name>last"` - Age int `xml:"age"` - Height float32 `xml:"height,omitempty"` - Married bool - Address - Comment string `xml:",comment"` -} - -func (Person) MarshalJSON() ([]byte, error) { - return nil, errors.New("Person cannot be JSON encoded") -} - -func (*Person) UnmarshalJSON([]byte) error { - return errors.New("Person cannot be JSON decoded") -} - -func TestXmlStructuredDecoding(t *testing.T) { - person := &Person{ - XMLName: xml.Name{ - Local: "person", - }, - Id: 13, - FirstName: "John", - LastName: "Doe", - Age: 42, - Comment: " Need more details. ", - Address: Address{"Hanga Roa", "Easter Island"}, - } - - xmlPerson := ` - - - John - Doe - - 42 - false - Hanga Roa - Easter Island - - ` - xmlJsonSafe, err := json.Marshal(xmlPerson) - if err != nil { - t.Fatalf("Failed to create JSON encoded XML string: %s", err) - } - - eventText := ` - { - "eventID": "1234", - "eventType": "dev.eventing.test", - "source": "tests://TextXmlStructuredEncoding", - "contentType": "application/xml", - "data": ` + string(xmlJsonSafe) + ` - }` - - h := http.Header{} - h.Set(event.HeaderContentType, event.ContentTypeStructuredJSON) - req := &http.Request{ - Header: h, - Body: ioutil.NopCloser(strings.NewReader(eventText)), - } - - var foundPerson Person - _, err = event.FromRequest(&foundPerson, req) - if err != nil { - t.Fatalf("Failed to parse cross-encoded request: %s", err) - } - - if !reflect.DeepEqual(person, &foundPerson) { - t.Fatalf("Failed to parse xml-encoded data; wanted=%+v; got=%+v", person, foundPerson) - } -} - -func TestExtensionsAreNeverNil(t *testing.T) { - r := &http.Request{ - Header: http.Header{ - event.HeaderContentType: []string{event.ContentTypeStructuredJSON}, - }, - Body: ioutil.NopCloser(strings.NewReader(` - { - "eventID": "1234", - "eventType": "dev.eventing.test", - "source": "tests://TextXmlStructuredEncoding", - "data": "hello, world" - }`)), - } - - var data interface{} - ctx, err := event.FromRequest(&data, r) - if err != nil { - t.Fatal("Failed to parse request", err) - } - if ctx.Extensions == nil { - t.Fatal("Extensions should never be nil") - } -} - -func TestExtensionExtraction(t *testing.T) { - h := http.Header{} - h.Set(event.HeaderContentType, event.ContentTypeBinaryJSON) - h.Set(event.HeaderEventID, "1234") - h.Set(event.HeaderEventType, "dev.eventing.test") - h.Set(event.HeaderSource, "tests://TestExtensionExtraction") - h.Set("CE-X-Prop1", "value1") - h.Set("CE-X-Prop2", `{"nestedProp":"nestedValue"}`) - b := strings.NewReader(`{"hello": "world"}`) - - r := &http.Request{ - Header: h, - Body: ioutil.NopCloser(b), - } - var data interface{} - ctx, err := event.FromRequest(&data, r) - if err != nil { - t.Fatal("Failed to parse request", err) - } - - expectedExtensions := map[string]interface{}{ - "Prop1": "value1", - "Prop2": map[string]interface{}{ - "nestedProp": "nestedValue", - }, - } - if !reflect.DeepEqual(expectedExtensions, ctx.Extensions) { - t.Fatalf("Did not parse expected extensions. Wanted=%v; got=%v", expectedExtensions, ctx.Extensions) - } -} diff --git a/pkg/event/handler_test.go b/pkg/event/handler_test.go deleted file mode 100644 index 994c3e83cbc..00000000000 --- a/pkg/event/handler_test.go +++ /dev/null @@ -1,461 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package event_test - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/http/httptest" - "reflect" - "strings" - "testing" - "time" - - "github.com/davecgh/go-spew/spew" - "github.com/knative/eventing/pkg/event" -) - -func TestHandlerTypeErrors(t *testing.T) { - for _, test := range []struct { - name string - param interface{} - err string - }{ - { - name: "non-func", - param: 5, - err: "Must pass a function to handle events", - }, - { - name: "wrong param count", - param: func(context.Context, interface{}, interface{}) {}, - err: "Expected a function taking either no parameters, a context.Context, or (context.Context, any); function has too many parameters (3)", - }, - { - name: "wrong first parameter type", - param: func(int) {}, - err: "Expected a function taking either no parameters, a context.Context, or (context.Context, any); cannot convert parameter 0 from int to context.Context", - }, - { - name: "wrong return count", - param: func() (interface{}, error, interface{}) { return nil, nil, nil }, - err: "Expected a function returning either nothing, an error, or (any, error); function has too many return types (3)", - }, - { - name: "invalid return type", - param: func() interface{} { return nil }, - err: "Expected a function returning either nothing, an error, or (any, error); cannot convert return type 0 from interface {} to error", - }, - } { - t.Run(test.name, func(t *testing.T) { - h := event.Handler(test.param) - err, ok := h.(error) - if !ok { - t.Fatalf("Expected Handler() to fail with %v, passed", test.err) - } - if !strings.Contains(err.Error(), test.err) { - t.Errorf("Expected Handler() to fail. want %q, got %v", test.err, err) - } - - // Attempt to call the returned Handler to verify it fails. - srv := httptest.NewServer(h) - defer srv.Close() - type E struct{} - req, err := event.NewRequest(srv.URL, E{}, event.EventContext{ - EventID: "1", EventType: "a", Source: "one"}) - if err != nil { - t.Errorf("Couldn't construct request: %v", err) - } - if resp, err := srv.Client().Do(req); err != nil { - t.Errorf("Failed to Post event: %v", resp) - } else if resp.StatusCode != http.StatusNotImplemented { - t.Errorf("Expected error status. got %d, got %d", resp.StatusCode, http.StatusNotImplemented) - } - }) - } -} - -func TestHandlerValidTypes(t *testing.T) { - for _, test := range []struct { - name string - f interface{} - }{ - { - name: "no in, no out", - f: func() {}, - }, { - name: "one in, no out", - f: func(context.Context) {}, - }, { - name: "interface in, no out", - f: func(context.Context, io.Reader) {}, - }, { - name: "value-type in, no out", - f: func(context.Context, int) {}, - }, { - name: "pointer-type in, no out", - f: func(context.Context, *int) {}, - }, { - name: "no in, one out", - f: func() error { return nil }, - }, { - name: "one in, one out", - f: func(context.Context) error { return nil }, - }, { - name: "two in, one out", - f: func(context.Context, string) error { return nil }, - }, { - name: "no in, two out", - f: func() (string, error) { return "", nil }, - }, { - name: "one in, two out", - f: func(context.Context) (map[string]interface{}, error) { return nil, nil }, - }, { - name: "two in, two out", - f: func(context.Context, io.Reader) (interface{}, error) { return nil, nil }, - }, - } { - t.Run(test.name, func(t *testing.T) { - if err, ok := event.Handler(test.f).(error); ok { - t.Errorf("%q failed: %v", test.name, err) - } - }) - } -} - -func TestParameterMarsahlling(t *testing.T) { - type Data struct { - Message string - } - expectedData := Data{Message: "Hello, world!"} - expectedContext := &event.EventContext{ - CloudEventsVersion: event.CloudEventsVersion, - EventID: "1234", - Source: "tests:TestUndtypedHandling", - EventType: "dev.eventing.test", - EventTime: time.Now().UTC(), - ContentType: "application/json", - Extensions: map[string]interface{}{}, - } - var wasCalled = false - for _, marshaller := range []struct { - name string - val event.HTTPMarshaller - }{ - { - name: "structured", - val: event.Structured, - }, { - name: "binary", - val: event.Binary, - }, - } { - for _, test := range []struct { - name string - generator func(t *testing.T) http.Handler - }{ - { - name: "no parameters", - generator: func(t *testing.T) http.Handler { - return event.Handler(func() { - wasCalled = true - }) - }, - }, - { - name: "one parameter", - generator: func(t *testing.T) http.Handler { - return event.Handler(func(ctx context.Context) { - eventContext := event.FromContext(ctx) - if !reflect.DeepEqual(expectedContext, eventContext) { - t.Fatalf("Did not get expected context; wanted=%s; got=%s", spew.Sdump(expectedContext), spew.Sdump(eventContext)) - } - wasCalled = true - }) - }, - }, { - name: "two parameters (struct type)", - generator: func(t *testing.T) http.Handler { - return event.Handler(func(ctx context.Context, data Data) { - eventContext := event.FromContext(ctx) - if !reflect.DeepEqual(expectedContext, eventContext) { - t.Fatalf("Did not get expected context; wanted=%s; got=%s", spew.Sdump(expectedContext), spew.Sdump(eventContext)) - } - if !reflect.DeepEqual(expectedData, data) { - t.Fatalf("Did not get expected data; wanted=%s; got=%s", spew.Sdump(expectedData), spew.Sdump(data)) - } - wasCalled = true - }) - }, - }, { - name: "two parameters (pointer type)", - generator: func(t *testing.T) http.Handler { - return event.Handler(func(ctx context.Context, data *Data) { - eventContext := event.FromContext(ctx) - if !reflect.DeepEqual(expectedContext, eventContext) { - t.Fatalf("Did not get expected context; wanted=%s; got=%s", spew.Sdump(expectedContext), spew.Sdump(eventContext)) - } - if !reflect.DeepEqual(expectedData, *data) { - t.Fatalf("Did not get expected data; wanted=%s; got=%s", spew.Sdump(&expectedData), spew.Sdump(data)) - } - wasCalled = true - }) - }, - }, { - name: "two parameters (untyped)", - generator: func(t *testing.T) http.Handler { - return event.Handler(func(ctx context.Context, data map[string]interface{}) { - eventContext := event.FromContext(ctx) - if !reflect.DeepEqual(expectedContext, eventContext) { - t.Fatalf("Did not get expected context; wanted=%s; got=%s", spew.Sdump(expectedContext), spew.Sdump(eventContext)) - } - b, err := json.Marshal(expectedData) - if err != nil { - t.Fatal("Failed to serialize expected data", err) - } - var expectedUntyped map[string]interface{} - err = json.Unmarshal(b, &expectedUntyped) - if err != nil { - t.Fatal("Failed to deserialize expected data", err) - } - if !reflect.DeepEqual(expectedUntyped, data) { - t.Fatalf("Did not get expected data; wanted=%s; got=%s", spew.Sdump(expectedUntyped), spew.Sdump(data)) - } - wasCalled = true - }) - }, - }, - } { - t.Run(fmt.Sprintf("%s: %s", marshaller.name, test.name), func(t *testing.T) { - wasCalled = false - handler := test.generator(t) - if err, ok := handler.(error); ok { - t.Errorf("Handler() failed: %v", err) - return - } - srv := httptest.NewServer(handler) - defer srv.Close() - req, err := marshaller.val.NewRequest(srv.URL, expectedData, *expectedContext) - if err != nil { - t.Fatal("Failed to marshal request ", err) - } - res, err := srv.Client().Do(req) - if err != nil { - t.Fatal("Failed to send request") - } - if res.StatusCode/100 != 2 { - t.Fatal("Got non-successful response: ", res.StatusCode) - } - if !wasCalled { - t.Fatal("Handler was never called") - } - }) - } - } -} - -func TestReturnTypeRendering(t *testing.T) { - eventData := map[string]interface{}{ - "unused": "data", - } - type RetVal struct { - ID interface{} - } - eventContext := event.EventContext{ - CloudEventsVersion: event.CloudEventsVersion, - EventID: "1234", - Source: "tests:TestUndtypedHandling", - EventType: "dev.eventing.test", - Extensions: map[string]interface{}{}, - } - for _, test := range []struct { - name string - expectedStatus int - expectedResponse string - handler http.Handler - }{ - { - name: "no return", - expectedStatus: http.StatusNoContent, - handler: event.Handler(func() {}), - }, { - name: "nil error return", - expectedStatus: http.StatusNoContent, - handler: event.Handler(func() error { - return nil - }), - }, { - name: "non-nil error return (one return type)", - expectedStatus: http.StatusInternalServerError, - handler: event.Handler(func() error { - return errors.New("Some error") - }), - expectedResponse: "Internal server error", - }, { - name: "successful return", - expectedStatus: http.StatusOK, - handler: event.Handler(func() (map[string]interface{}, error) { - return map[string]interface{}{"hello": "world"}, nil - }), - expectedResponse: `{"hello":"world"}`, - }, { - name: "non-nil error return (two return types)", - expectedStatus: http.StatusInternalServerError, - handler: event.Handler(func() (map[string]interface{}, error) { - return map[string]interface{}{"hello": "world"}, errors.New("Errors take precedence") - }), - expectedResponse: "Internal server error", - }, - { - name: "non-nil content return", - expectedStatus: http.StatusOK, - handler: event.Handler(func() (map[string]interface{}, error) { - return map[string]interface{}{"hello": "world"}, nil - }), - expectedResponse: `{"hello":"world"}`, - }, - { - name: "bad JSON content", - expectedStatus: http.StatusInternalServerError, - handler: event.Handler(func() (RetVal, error) { - return RetVal{ID: func() {}}, nil - }), - }, - } { - t.Run(test.name, func(t *testing.T) { - if err, ok := test.handler.(error); ok { - t.Errorf("Handler() failed: %v", err) - return - } - srv := httptest.NewServer(test.handler) - defer srv.Close() - req, err := event.NewRequest(srv.URL, eventData, eventContext) - if err != nil { - t.Fatal("Failed to marshal request ", err) - } - res, err := srv.Client().Do(req) - if err != nil { - t.Fatal("Failed to send request") - } - defer res.Body.Close() - if test.expectedStatus != res.StatusCode { - t.Fatalf("Wrong status code from event handler; wanted=%d; got=%d", test.expectedStatus, res.StatusCode) - } - if test.expectedResponse != "" { - b, err := ioutil.ReadAll(res.Body) - if err != nil { - t.Fatal("Failed to read response body:", err) - } - resBody := string(b) - if test.expectedResponse != resBody { - t.Fatalf("Got unexpected respnose string; wanted=%q; got=%q", test.expectedResponse, resBody) - } - } - }) - } -} - -func TestMux(t *testing.T) { - type TypeA struct { - Greeting string - } - type TypeB struct { - Farewell string - } - - eventA := TypeA{ - Greeting: "Hello, world!", - } - eventB := TypeB{ - Farewell: "Hasta la vista", - } - - contextA := &event.EventContext{ - EventID: "1234", - EventType: "org.A.test", - Source: "test:TestMux", - ContentType: "application/json", - Extensions: map[string]interface{}{}, - } - contextB := &event.EventContext{ - EventID: "5678", - EventType: "org.B.test", - Source: "test:TestMux", - ContentType: "application/json", - Extensions: map[string]interface{}{}, - } - sawA, sawB := false, false - - mux := event.NewMux() - err := mux.Handle("org.A.test", func(ctx context.Context, data TypeA) error { - sawA = true - context := event.FromContext(ctx) - if !reflect.DeepEqual(eventA, data) { - t.Fatalf("Got wrong data for event A; wanted=%s; got=%s", eventA, data) - } - if !reflect.DeepEqual(contextA, context) { - t.Fatalf("Got wrong context for event A; wanted=%s; got=%s", contextA, context) - } - return nil - }) - if err != nil { - t.Fatalf("mux.Handle('org.A.test') failed: %v", err) - } - err = mux.Handle("org.B.test", func(ctx context.Context, data TypeB) error { - sawB = true - context := event.FromContext(ctx) - if !reflect.DeepEqual(eventB, data) { - t.Fatalf("Got wrong data for event A; wanted=%s; got=%s", eventB, data) - } - if !reflect.DeepEqual(contextB, context) { - t.Fatalf("Got wrong context for event A; wanted=%s; got=%s", contextB, context) - } - return nil - }) - if err != nil { - t.Fatalf("mux.Handle('org.B.test') failed: %v", err) - } - - srv := httptest.NewServer(mux) - defer srv.Close() - req, err := event.NewRequest(srv.URL, eventA, *contextA) - if err != nil { - t.Fatal("Failed to marshal request for eventA", err) - } - if _, err := srv.Client().Do(req); err != nil { - t.Fatal("Failed to send eventA", err) - } - req, err = event.NewRequest(srv.URL, eventB, *contextB) - if err != nil { - t.Fatal("Failed to marshal request for eventB", err) - } - if _, err := srv.Client().Do(req); err != nil { - t.Fatal("Failed to send eventB", err) - } - - if !sawA { - t.Fatal("Handler for eventA never called") - } - if !sawB { - t.Fatal("Handler for eventB never called") - } -} diff --git a/pkg/sources/gcppubsub/receive_adapter/receive_adapter.go b/pkg/sources/gcppubsub/receive_adapter/receive_adapter.go index 1dce5d3e82e..46933b12aee 100644 --- a/pkg/sources/gcppubsub/receive_adapter/receive_adapter.go +++ b/pkg/sources/gcppubsub/receive_adapter/receive_adapter.go @@ -10,7 +10,7 @@ import ( // Imports the Google Cloud Pub/Sub client package. "cloud.google.com/go/pubsub" - "github.com/knative/eventing/pkg/event" + "github.com/knative/pkg/cloudevents" "golang.org/x/net/context" ) @@ -70,14 +70,14 @@ func main() { func postMessage(target string, source string, m *pubsub.Message) error { URL := fmt.Sprintf("http://%s/", target) - ctx := event.EventContext{ - CloudEventsVersion: event.CloudEventsVersion, + ctx := cloudevents.EventContext{ + CloudEventsVersion: cloudevents.CloudEventsVersion, EventType: "google.pubsub.topic.publish", EventID: m.ID, EventTime: m.PublishTime, Source: source, } - req, err := event.Binary.NewRequest(URL, m, ctx) + req, err := cloudevents.Binary.NewRequest(URL, m, ctx) if err != nil { log.Printf("Failed to marshal the message: %+v : %s", m, err) return err diff --git a/pkg/sources/github/receive_adapter/receive_adapter.go b/pkg/sources/github/receive_adapter/receive_adapter.go index f4f75ebb75c..fcfbe62f1ef 100644 --- a/pkg/sources/github/receive_adapter/receive_adapter.go +++ b/pkg/sources/github/receive_adapter/receive_adapter.go @@ -30,8 +30,8 @@ import ( ghclient "github.com/google/go-github/github" "github.com/google/uuid" - "github.com/knative/eventing/pkg/event" "github.com/knative/eventing/pkg/sources/github" + "github.com/knative/pkg/cloudevents" "log" "time" ) @@ -120,14 +120,14 @@ func main() { func postMessage(target string, payload interface{}, source, eventType, eventID string) error { URL := fmt.Sprintf("http://%s/", target) - ctx := event.EventContext{ - CloudEventsVersion: event.CloudEventsVersion, + ctx := cloudevents.EventContext{ + CloudEventsVersion: cloudevents.CloudEventsVersion, EventType: eventType, EventID: eventID, EventTime: time.Now(), Source: source, } - req, err := event.Binary.NewRequest(URL, payload, ctx) + req, err := cloudevents.Binary.NewRequest(URL, payload, ctx) if err != nil { log.Printf("Failed to marshal the message: %+v : %s", payload, err) return err diff --git a/pkg/sources/k8sevents/receive_adapter/receive_adapter.go b/pkg/sources/k8sevents/receive_adapter/receive_adapter.go index 76cbc97a280..e40ea37ebb8 100644 --- a/pkg/sources/k8sevents/receive_adapter/receive_adapter.go +++ b/pkg/sources/k8sevents/receive_adapter/receive_adapter.go @@ -26,7 +26,7 @@ import ( "strings" "github.com/golang/glog" - "github.com/knative/eventing/pkg/event" + "github.com/knative/pkg/cloudevents" "github.com/knative/pkg/signals" corev1 "k8s.io/api/core/v1" coreinformers "k8s.io/client-go/informers/core/v1" @@ -156,10 +156,10 @@ func createSelfLink(o corev1.ObjectReference) string { // Type:Normal, // EventTime:0001-01-01 00:00:00 +0000 UTC, // } -func cloudEventsContext(m *corev1.Event) *event.EventContext { - return &event.EventContext{ +func cloudEventsContext(m *corev1.Event) *cloudevents.EventContext { + return &cloudevents.EventContext{ // Events are themselves object and have a unique UUID. Could also have used the UID - CloudEventsVersion: event.CloudEventsVersion, + CloudEventsVersion: cloudevents.CloudEventsVersion, EventType: "dev.knative.k8s.event", EventID: string(m.ObjectMeta.UID), Source: createSelfLink(m.InvolvedObject), @@ -174,7 +174,7 @@ func postMessage(target string, m *corev1.Event) error { log.Printf("Posting to %q", URL) // Explicitly using Binary encoding so that Istio, et. al. can better inspect // event metadata. - req, err := event.Binary.NewRequest(URL, m, *ctx) + req, err := cloudevents.Binary.NewRequest(URL, m, *ctx) if err != nil { log.Printf("Failed to create http request: %s", err) return err diff --git a/test/e2e/k8sevents/function.go b/test/e2e/k8sevents/function.go index 3743aab5fe2..108d9320cc5 100644 --- a/test/e2e/k8sevents/function.go +++ b/test/e2e/k8sevents/function.go @@ -15,21 +15,20 @@ package main import ( "context" + "github.com/knative/pkg/cloudevents" "log" "net/http" "time" corev1 "k8s.io/api/core/v1" - - "github.com/knative/eventing/pkg/event" ) func handler(ctx context.Context, e *corev1.Event) { - metadata := event.FromContext(ctx) + metadata := cloudevents.FromContext(ctx) log.Printf("[%s] %s : %q", metadata.EventTime.Format(time.RFC3339), metadata.Source, e.Message) } func main() { log.Print("Ready and listening on port 8080") - log.Fatal(http.ListenAndServe(":8080", event.Handler(handler))) + log.Fatal(http.ListenAndServe(":8080", cloudevents.Handler(handler))) } diff --git a/vendor/github.com/knative/pkg/apis/duck/verify.go b/vendor/github.com/knative/pkg/apis/duck/verify.go index b55efa09317..d53421b0464 100644 --- a/vendor/github.com/knative/pkg/apis/duck/verify.go +++ b/vendor/github.com/knative/pkg/apis/duck/verify.go @@ -23,7 +23,7 @@ import ( "github.com/google/go-cmp/cmp" ) -// Implementable in implemented by the Fooable duck type that consumers +// Implementable is implemented by the Fooable duck type that consumers // are expected to embed as a `.status.fooable` field. type Implementable interface { // GetFullType returns an instance of a full resource wrapping diff --git a/pkg/event/doc.go b/vendor/github.com/knative/pkg/cloudevents/doc.go similarity index 89% rename from pkg/event/doc.go rename to vendor/github.com/knative/pkg/cloudevents/doc.go index 99474855687..d33753866f8 100644 --- a/pkg/event/doc.go +++ b/vendor/github.com/knative/pkg/cloudevents/doc.go @@ -14,9 +14,10 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Package event implements utilities for handling CloudEvents. +// Package cloudevents implements utilities for handling CloudEvents. // For information on the spec, see // https://github.com/cloudevents/spec/blob/v0.1/http-transport-binding.md // and // https://github.com/cloudevents/spec/blob/v0.1/spec.md -package event + +package cloudevents diff --git a/pkg/event/encoding_binary.go b/vendor/github.com/knative/pkg/cloudevents/encoding_binary.go similarity index 99% rename from pkg/event/encoding_binary.go rename to vendor/github.com/knative/pkg/cloudevents/encoding_binary.go index c387658de2f..ed88e01c47a 100644 --- a/pkg/event/encoding_binary.go +++ b/vendor/github.com/knative/pkg/cloudevents/encoding_binary.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package event +package cloudevents // TODO(inlined): must add header encoding/decoding diff --git a/pkg/event/encoding_structured.go b/vendor/github.com/knative/pkg/cloudevents/encoding_structured.go similarity index 99% rename from pkg/event/encoding_structured.go rename to vendor/github.com/knative/pkg/cloudevents/encoding_structured.go index 55118e06d41..08c2acab97f 100644 --- a/pkg/event/encoding_structured.go +++ b/vendor/github.com/knative/pkg/cloudevents/encoding_structured.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package event +package cloudevents import ( "bytes" diff --git a/pkg/event/event.go b/vendor/github.com/knative/pkg/cloudevents/event.go similarity index 99% rename from pkg/event/event.go rename to vendor/github.com/knative/pkg/cloudevents/event.go index 3ace7433a21..2074139f1a7 100644 --- a/pkg/event/event.go +++ b/vendor/github.com/knative/pkg/cloudevents/event.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package event +package cloudevents import ( "context" diff --git a/pkg/event/handler.go b/vendor/github.com/knative/pkg/cloudevents/handler.go similarity index 99% rename from pkg/event/handler.go rename to vendor/github.com/knative/pkg/cloudevents/handler.go index f26444e46c5..7291064347a 100644 --- a/pkg/event/handler.go +++ b/vendor/github.com/knative/pkg/cloudevents/handler.go @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -package event +package cloudevents import ( "context" diff --git a/vendor/github.com/knative/pkg/test/spoof/spoof.go b/vendor/github.com/knative/pkg/test/spoof/spoof.go index a8bb1f4ac48..9c94b3c5e70 100644 --- a/vendor/github.com/knative/pkg/test/spoof/spoof.go +++ b/vendor/github.com/knative/pkg/test/spoof/spoof.go @@ -19,6 +19,9 @@ limitations under the License. package spoof import ( + "bytes" + "encoding/json" + "errors" "fmt" "io/ioutil" "net" @@ -26,14 +29,22 @@ import ( "time" "github.com/knative/pkg/test/logging" + zipkin "github.com/knative/pkg/test/zipkin" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/util/wait" "k8s.io/client-go/kubernetes" + + "go.opencensus.io/plugin/ochttp" + "go.opencensus.io/plugin/ochttp/propagation/b3" + "go.opencensus.io/trace" ) const ( requestInterval = 1 * time.Second requestTimeout = 5 * time.Minute + // TODO(tcnghia): These probably shouldn't be hard-coded here? + ingressName = "knative-ingressgateway" + ingressNamespace = "istio-system" ) // Response is a stripped down subset of http.Response. The is primarily useful @@ -82,7 +93,7 @@ type SpoofingClient struct { // If that's a problem, see test/request.go#WaitForEndpointState for oneshot spoofing. func New(kubeClientset *kubernetes.Clientset, logger *logging.BaseLogger, domain string, resolvable bool) (*SpoofingClient, error) { sc := SpoofingClient{ - Client: http.DefaultClient, + Client: &http.Client{Transport: &ochttp.Transport{Propagation: &b3.HTTPFormat{}}}, // Using ochttp Transport required for zipkin-tracing RequestInterval: requestInterval, RequestTimeout: requestTimeout, logger: logger, @@ -92,30 +103,12 @@ func New(kubeClientset *kubernetes.Clientset, logger *logging.BaseLogger, domain // If the domain that the Route controller is configured to assign to Route.Status.Domain // (the domainSuffix) is not resolvable, we need to retrieve the IP of the endpoint and // spoof the Host in our requests. - - // TODO(tcnghia): These probably shouldn't be hard-coded here? - ingressName := "knative-ingressgateway" - ingressNamespace := "istio-system" - - ingress, err := kubeClientset.CoreV1().Services(ingressNamespace).Get(ingressName, metav1.GetOptions{}) + e, err := GetServiceEndpoint(kubeClientset) if err != nil { return nil, err } - ingresses := ingress.Status.LoadBalancer.Ingress - - if len(ingresses) != 1 { - return nil, fmt.Errorf("Expected exactly one ingress load balancer, instead had %d: %s", len(ingresses), ingresses) - } - ingressToUse := ingresses[0] - if ingressToUse.IP == "" { - if ingressToUse.Hostname == "" { - return nil, fmt.Errorf("Expected ingress loadbalancer IP or hostname for %s to be set, instead was empty", ingressName) - } - sc.endpoint = ingressToUse.Hostname - } else { - sc.endpoint = ingressToUse.IP - } + sc.endpoint = *e sc.domain = domain } else { // If the domain is resolvable, we can use it directly when we make requests. @@ -125,8 +118,32 @@ func New(kubeClientset *kubernetes.Clientset, logger *logging.BaseLogger, domain return &sc, nil } +// GetServiceEndpoint gets the endpoint IP or hostname to use for the service +func GetServiceEndpoint(kubeClientset *kubernetes.Clientset) (*string, error) { + var endpoint string + ingress, err := kubeClientset.CoreV1().Services(ingressNamespace).Get(ingressName, metav1.GetOptions{}) + if err != nil { + return nil, err + } + ingresses := ingress.Status.LoadBalancer.Ingress + if len(ingresses) != 1 { + return nil, fmt.Errorf("Expected exactly one ingress load balancer, instead had %d: %s", len(ingresses), ingresses) + } + ingressToUse := ingresses[0] + if ingressToUse.IP == "" { + if ingressToUse.Hostname == "" { + return nil, fmt.Errorf("Expected ingress loadbalancer IP or hostname for %s to be set, instead was empty", ingressName) + } + endpoint = ingressToUse.Hostname + } else { + endpoint = ingressToUse.IP + } + return &endpoint, nil +} + // Do dispatches to the underlying http.Client.Do, spoofing domains as needed // and transforming the http.Response into a spoof.Response. +// Each response is augmented with "ZipkinTraceID" header that identifies the zipkin trace corresponding to the request. func (sc *SpoofingClient) Do(req *http.Request) (*Response, error) { // Controls the Host header, for spoofing. if sc.domain != "" { @@ -138,12 +155,18 @@ func (sc *SpoofingClient) Do(req *http.Request) (*Response, error) { req.URL.Host = sc.endpoint } - resp, err := sc.Client.Do(req) + // Starting span to capture zipkin trace. + traceContext, span := trace.StartSpan(req.Context(), "SpoofingClient-Trace") + defer span.End() + + resp, err := sc.Client.Do(req.WithContext(traceContext)) if err != nil { return nil, err } defer resp.Body.Close() + + resp.Header.Add(zipkin.ZipkinTraceIDHeader, span.SpanContext().TraceID.String()) body, err := ioutil.ReadAll(resp.Body) if err != nil { return nil, err @@ -179,3 +202,34 @@ func (sc *SpoofingClient) Poll(req *http.Request, inState ResponseChecker) (*Res return resp, err } + +// LogZipkinTrace provides support to log Zipkin Trace for param: traceID +func (sc *SpoofingClient) LogZipkinTrace(traceID string) error { + if err := zipkin.CheckZipkinPortAvailability(); err == nil { + return errors.New("port-forwarding for Zipkin is not-setup. Failing Zipkin Trace retrieval") + } + + sc.logger.Infof("Logging Zipkin Trace: %s", traceID) + + zipkinTraceEndpoint := zipkin.ZipkinTraceEndpoint + traceID + // Sleep to ensure all traces are correctly pushed on the backend. + time.Sleep(5 * time.Second) + resp, err := http.Get(zipkinTraceEndpoint) + if err != nil { + return fmt.Errorf("Error retrieving Zipkin trace: %v", err) + } + + trace, err := ioutil.ReadAll(resp.Body) + if err != nil { + return fmt.Errorf("Error reading Zipkin trace response: %v", err) + } + + var prettyJSON bytes.Buffer + error := json.Indent(&prettyJSON, trace, "", "\t") + if error != nil { + return fmt.Errorf("JSON Parser Error while trying for Pretty-Format: %v, Original Response: %s", error, string(trace)) + } + sc.logger.Infof(prettyJSON.String()) + + return nil +} \ No newline at end of file diff --git a/vendor/github.com/knative/pkg/test/zipkin/util.go b/vendor/github.com/knative/pkg/test/zipkin/util.go new file mode 100644 index 00000000000..5d41e5dbc73 --- /dev/null +++ b/vendor/github.com/knative/pkg/test/zipkin/util.go @@ -0,0 +1,110 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +//util has constants and helper methods useful for zipkin tracing support. + +package zipkin + +import ( + "errors" + "fmt" + "net" + "os" + "os/exec" + + "github.com/knative/pkg/test/logging" + "go.opencensus.io/trace" + "k8s.io/client-go/kubernetes" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + + //ZipkinTraceIDHeader HTTP response header key to be used to store Zipkin Trace ID. + ZipkinTraceIDHeader = "ZIPKIN_TRACE_ID" + + // ZipkinPort port exposed by the Zipkin Pod + // https://github.com/knative/serving/blob/master/config/monitoring/200-common/100-zipkin.yaml#L25 configures the Zipkin Port on the cluster. + ZipkinPort = 9411 + + // ZipkinTraceEndpoint port-forwarded zipkin endpoint + ZipkinTraceEndpoint = "http://localhost:9411/api/v2/trace/" + + // ZipkinNamespace namespace where zipkin pod runs. + // https://github.com/knative/serving/blob/master/config/monitoring/200-common/100-zipkin.yaml#L19 configures the namespace for zipkin. + ZipkinNamespace = "istio-system" +) + +var zipkinPortForwardPID int + +// SetupZipkinTracing sets up zipkin tracing which involves a) Setting up port-forwarding from localhost to zipkin pod on the cluster (pid of the process doing Port-Forward is stored in a global variable). +// b) enable AlwaysSample config for tracing. +func SetupZipkinTracing(kubeClientset *kubernetes.Clientset) error { + logger := logging.GetContextLogger("SpoofUtil") + + if err := CheckZipkinPortAvailability(); err != nil { + return fmt.Errorf("Zipkin port not available on the machine: %v", err) + } + + zipkinPods, err := kubeClientset.CoreV1().Pods(ZipkinNamespace).List(metav1.ListOptions{LabelSelector: "app=zipkin"}) + if err != nil { + return fmt.Errorf("Error retrieving Zipkin pod details : %v", err) + } + + if len(zipkinPods.Items) == 0 { + return errors.New("No Zipkin Pod found on the cluster. Ensure monitoring is switched on for your Knative Setup") + } + + portForwardCmd := exec.Command("kubectl", "port-forward", "--namespace=" + ZipkinNamespace, zipkinPods.Items[0].Name, fmt.Sprintf("%d:%d", ZipkinPort, ZipkinPort)) + if err = portForwardCmd.Start(); err != nil { + return fmt.Errorf("Error starting kubectl port-forward command : %v", err) + + } + zipkinPortForwardPID = portForwardCmd.Process.Pid + logger.Infof("Zipkin port-forward process started with PID: %d", zipkinPortForwardPID) + + // Applying AlwaysSample config to ensure we propagate zipkin header for every request made by this client. + trace.ApplyConfig(trace.Config{DefaultSampler: trace.AlwaysSample()}) + + logger.Infof("Successfully setup SpoofingClient for Zipkin Tracing") + + return nil +} + +// CleanupZipkinTracingSetup cleans up the Zipkin tracing setup on the machine. This involves killing the process performing port-forward. +func CleanupZipkinTracingSetup() error { + logger := logging.GetContextLogger("SpoofUtil") + + ps := os.Process{Pid: zipkinPortForwardPID} + if err := ps.Kill(); err != nil { + return fmt.Errorf("Encoutered error killing port-forward process in CleanupZipkingTracingSetup() : %v", err) + } + + logger.Infof("Successfully killed Zipkin port-forward process") + return nil +} + +// CheckZipkinPortAvailability checks to see if Zipkin Port is available on the machine. +// returns error if the port is not available. +func CheckZipkinPortAvailability() error { + server, err := net.Listen("tcp", fmt.Sprintf(":%d", ZipkinPort)) + if err != nil { + // Port is likely taken + return err + } + server.Close() + return nil +} \ No newline at end of file From a99376f025df6af99fd5d99826330d2bd5b47e10 Mon Sep 17 00:00:00 2001 From: Adam Harwayne Date: Fri, 2 Nov 2018 14:20:35 -0700 Subject: [PATCH 21/54] Update spec to match the new names from #563. (#576) * Update spec to match the new names from #563. * PR comments. --- docs/spec/interfaces.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/spec/interfaces.md b/docs/spec/interfaces.md index b339dde1b86..ff2bba3d28e 100644 --- a/docs/spec/interfaces.md +++ b/docs/spec/interfaces.md @@ -8,10 +8,10 @@ is a function. ### Control Plane -A **Targetable** resource MUST expose a _status.targetable.domainInternal_ +A **Targetable** resource MUST expose a `status.targetable.domainInternal` field. The _domainInternal_ value is an internal domain name that is capable of receiving event deliveries. _Targetable_ resources may be referenced in the -_call_ section of a _Subscription_. +`subscriber` section of a _Subscription_. ### Data Plane @@ -31,23 +31,23 @@ if it previously indicated success. A **Sinkable** resource receives events and takes responsibility for further delivery. Unlike _Targetable_, a _Sinkable_ cannot return events in its response. One example of a _Sinkable_ is a _Channel_ as the target of a -_Subscription_'s _result_ field. +_Subscription_'s `reply` field. ### Control Plane -A **Sinkable** resource MUST expose a _status.sinkable.domainInternal_ field. +A **Sinkable** resource MUST expose a `status.sinkable.domainInternal` field. The _domainInternal_ value is an internal domain name that is capable of receiving event deliveries. _Sinkable_ resources may be referenced in the -_result_ section of a _Subscription_, and also by other custom resources acting as an event Source. +`reply` section of a _Subscription_, and also by other custom resources acting as an event Source. ### Data Plane From cbdcf268f3207a76abec0f1815fd3f61b52da5bf Mon Sep 17 00:00:00 2001 From: Evan Anderson Date: Tue, 6 Nov 2018 11:13:36 -0600 Subject: [PATCH 22/54] Rename Sinkable to Addressable. Temporarily use Addressable for Callable as well. (#583) --- Gopkg.lock | 4 +- Gopkg.toml | 2 +- docs/spec/interfaces.md | 79 ++--- docs/spec/overview.md | 24 +- docs/spec/spec.md | 18 +- pkg/apis/eventing/v1alpha1/channel_types.go | 44 +-- .../eventing/v1alpha1/channel_types_test.go | 46 +-- pkg/apis/eventing/v1alpha1/implements_test.go | 3 +- .../eventing/v1alpha1/subscription_types.go | 2 +- .../v1alpha1/zz_generated.deepcopy.go | 2 +- .../eventing/inmemory/channel/reconcile.go | 2 +- .../inmemory/channel/reconcile_test.go | 12 +- .../eventing/subscription/reconcile.go | 24 +- .../eventing/subscription/reconcile_test.go | 72 ++--- pkg/sidecar/fanout/fanout_handler_test.go | 42 +-- .../apis/duck/v1alpha1/addressable_types.go | 96 ++++++ .../apis/duck/v1alpha1/channelable_types.go | 104 ------ .../duck/v1alpha1/legacy_targetable_types.go | 10 +- .../pkg/apis/duck/v1alpha1/register.go | 8 +- ...e_types.go => retired_targetable_types.go} | 14 +- .../pkg/apis/duck/v1alpha1/sinkable_types.go | 94 ------ .../apis/duck/v1alpha1/subscribable_types.go | 105 ------ .../duck/v1alpha1/zz_generated.deepcopy.go | 306 +++--------------- 23 files changed, 349 insertions(+), 764 deletions(-) create mode 100644 vendor/github.com/knative/pkg/apis/duck/v1alpha1/addressable_types.go delete mode 100644 vendor/github.com/knative/pkg/apis/duck/v1alpha1/channelable_types.go rename vendor/github.com/knative/pkg/apis/duck/v1alpha1/{targetable_types.go => retired_targetable_types.go} (84%) delete mode 100644 vendor/github.com/knative/pkg/apis/duck/v1alpha1/sinkable_types.go delete mode 100644 vendor/github.com/knative/pkg/apis/duck/v1alpha1/subscribable_types.go diff --git a/Gopkg.lock b/Gopkg.lock index 00a2677f75a..a5d964dd8d0 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -300,7 +300,7 @@ revision = "5c1d8c8469d1ed34b2aecf4c2305b3a57fff2ee3" [[projects]] - digest = "1:9dd034878e35ffe80dd0a4d7991521acce80f33cc7d3d5e21c7944bb2874e037" + digest = "1:7777b78ab28a8f10918bdcdb043f4150d27da3181fafd69a1b15935197bf9d4c" name = "github.com/knative/pkg" packages = [ "apis", @@ -335,7 +335,7 @@ "webhook", ] pruneopts = "NUT" - revision = "04b40fd38ac1cf17e0426b79860c792fd593ce71" + revision = "a8160c7d728d26da67e596c5b1975877082a26e6" [[projects]] digest = "1:63f3974f3afe3dc5b6a115c0d53b0897cd01be6880c4bf5d014fc69a95db6ed1" diff --git a/Gopkg.toml b/Gopkg.toml index 7b6ce137575..159890fe31a 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -77,7 +77,7 @@ required = [ [[override]] name = "github.com/knative/pkg" # HEAD as of 2018-11-02 - revision = "04b40fd38ac1cf17e0426b79860c792fd593ce71" + revision = "a8160c7d728d26da67e596c5b1975877082a26e6" [[constraint]] name = "github.com/knative/serving" diff --git a/docs/spec/interfaces.md b/docs/spec/interfaces.md index ff2bba3d28e..37fc779c3fd 100644 --- a/docs/spec/interfaces.md +++ b/docs/spec/interfaces.md @@ -1,60 +1,65 @@ # Interface Contracts -## Targetable +## Addressable -A **Targetable** resource represents an endpoint that receives events and -optionally returns events to forward downstream. One example of a _Targetable_ -is a function. +An **Addressable** resource receives events over a network transport +(currently only HTTP is supported). The _Addressable_ returns success when it +has successfully handled the event (for example, by committing it to stable +storage). When used as an _Addressable_, only the acknowledgement or return +code is used to determine whether the event was handled successfully. One +example of an _Addressable_ is a _Channel_. ### Control Plane -A **Targetable** resource MUST expose a `status.targetable.domainInternal` -field. The _domainInternal_ value is an internal domain name that is capable of -receiving event deliveries. _Targetable_ resources may be referenced in the -`subscriber` section of a _Subscription_. +An **Addressable** resource MUST expose a `status.address.hostname` field. +The _hostname_ value is a cluster-resolvable DNS name which is capable of +receiving event deliveries. _Addressable_ resources may be referenced in the +`reply` section of a _Subscription_, and also by other custom resources acting +as an event Source. ### Data Plane -The **Targetable** resource receives one event and returns zero or more events -in response. The returned events are not required to be related to the received -event. The _Targetable_ should return a successful response if the event was -processed successfully. - -The _Targetable_ is not responsible for ensuring successful delivery of any -received or returned event. It may receive the same event multiple times even -if it previously indicated success. +An **Addressable** resource will only respond to requests with success or +failure. Any payload (including a valid CloudEvent) returned to the sender +will be ignored. An _Addressable_ may receive the same event multiple times +even if it previously indicated success. --- -## Sinkable +## Callable -A **Sinkable** resource receives events and takes responsibility for further -delivery. Unlike _Targetable_, a _Sinkable_ cannot return events in its -response. One example of a _Sinkable_ is a _Channel_ as the target of a -_Subscription_'s `reply` field. +A **Callable** resource represents an _Addressable_ endpoint which receives +events and optionally returns events to forward downstream. One example of a +_Callable_ is a function. Note that all _Callable_ resources are _Addressable_ +(they accept an event and return a status code when completed), but not all +_Addressable_ resources are _Callable_. - +A **Callable** resource MUST expose a `status.address.hostname` field (like +_Addressable_). The _hostname_ value is a cluster-resolvable DNS name which is +capable of receiving event deliveries and returning a resulting event in the +reply.. _Callable_ resources may be referenced in the `subscriber` section of +a _Subscription_. -### Control Plane + ### Data Plane -A **Sinkable** resource will only respond to requests with success of failure. -Any payload (including a valid CloudEvent) returned to the sender will be -ignored. It may receive the same event multiple times even if it previously -indicated success. +The **Callable** resource receives one event and returns zero or more events +in response. The returned events are not required to be related to the received +event. The _Callable_ should return a successful response if the event was +processed successfully. + +The _Callable_ is not responsible for ensuring successful delivery of any +received or returned event. It may receive the same event multiple times even +if it previously indicated success. --- diff --git a/docs/spec/overview.md b/docs/spec/overview.md index d0fd814364f..dfb136224fe 100644 --- a/docs/spec/overview.md +++ b/docs/spec/overview.md @@ -3,17 +3,17 @@ The API defines and provides a complete implementation for [Subscription](spec.md#kind-subscription) and abstract resource definitions for [Channels](spec.md#kind-channel) and -[ClusterChannelProvisioners](spec.md#kind-clusterchannelprovisioner) which may -be fulfilled by multiple backing implementations (much like the Kubernetes -Ingress resource). +[ClusterChannelProvisioners](spec.md#kind-clusterchannelprovisioner) which +may be fulfilled by multiple backing implementations (much like the +Kubernetes Ingress resource). With extensibility and composability as a goal of Knative Eventing, the eventing API defines several resources that can be reduced down to well understood contracts. These eventing resource interfaces may be fulfilled by other Kubernetes objects and then composed in the same way as the concrete -objects. The interfaces are ([Sinkable](interfaces.md#sinkable), +objects. The interfaces are ([Addressable](interfaces.md#addressable), [Subscribable](interfaces.md#Subscribable), -[Targetable](interfaces.md#targetable)). For more details, see +[Callable](interfaces.md#callable)). For more details, see [Interface Contracts](interfaces.md). - A **Subscription** describes the transformation of an event and optional @@ -43,14 +43,14 @@ Sources](https://github.com/knative/eventing-sources). **Subscriptions** describe a flow of events from one _Channel_ to the next Channel\* through transformations (such as a Knative Service which processes CloudEvents over HTTP). A _Subscription_ controller resolves the addresses of -transformations (`call`) and destination storage (`result`) through the -_Targetable_ and _Sinkable_ interface contracts, and writes the resolved -addresses to the _Channel_ in the `from` reference. _Subscriptions_ do not need -to specify both a transformation and a storage destination, but at least one -must be provided. +transformations (`subscriber`) and destination storage (`result`) through the +_Callable_ and _Addressable_ interface contracts, and writes the resolved +addresses to the _Channel_ in the `channel` reference. _Subscriptions_ do not +need to specify both a transformation and a storage destination, but at least +one must be provided. All event delivery linkage from a **Subscription** is 1:1 – only a single -`from`, `call`, and `result` may be provided. +`channel`, `subscriber`, and `result` may be provided. For more details, see [Kind: Subscription](spec.md#kind-subscription). @@ -58,7 +58,7 @@ For more details, see [Kind: Subscription](spec.md#kind-subscription). **Channel** provides an event delivery mechanism which can fan out received events to multiple destinations via _Subscriptions_. A _Channel_ has a single -inbound _Sinkable_ interface which may accept events delivered directly or +inbound _Addressable_ interface which may accept events delivered directly or forwarded from multiple _Subscriptions_. Different _Channels_ may implement different degrees of persistence. Event delivery order is dependent on the backing implementation of the _Channel_ provided by the diff --git a/docs/spec/spec.md b/docs/spec/spec.md index a84c4f73bbd..5ff623fc712 100644 --- a/docs/spec/spec.md +++ b/docs/spec/spec.md @@ -41,10 +41,10 @@ its subscribers._ #### Status -| Field | Type | Description | Constraints | -| ---------- | ---------- | --------------------------------------------------------------------------------------------------------------------------- | ----------- | -| sinkable | Sinkable | Address to the endpoint as top-level domain that will distribute traffic over the provided targets from inside the cluster. | | -| conditions | Conditions | Channel conditions. | | +| Field | Type | Description | Constraints | +| ---------- | ----------- | -------------------------------------------------------------------------------------------- | ----------- | +| address | Addressable | Address of the endpoint which meets the [_Addressable_ contract](interfaces.md#addressable). | | +| conditions | Conditions | Channel conditions. | | ##### Conditions @@ -70,7 +70,7 @@ its subscribers._ ### group: eventing.knative.dev/v1alpha1 -_Describes a linkage between a Channel and a Targetable and/or Sinkable._ +_Describes a linkage between a Channel and a Callable and/or Addressable channel._ ### Object Schema @@ -152,10 +152,10 @@ or a Channel system that receives and delivers events._ ### SubscriberSpec -| Field | Type | Description | Constraints | -| ------------------- | --------------- | ----------- | -------------------------- | -| ref1 | ObjectReference | | Must adhere to Targetable. | -| dnsName1 | String | | | +| Field | Type | Description | Constraints | +| ------------------- | --------------- | ----------- | ------------------------ | +| ref1 | ObjectReference | | Must adhere to Callable. | +| dnsName1 | String | | | 1: One of (ref, dnsName), Required. diff --git a/pkg/apis/eventing/v1alpha1/channel_types.go b/pkg/apis/eventing/v1alpha1/channel_types.go index aea8620132e..afcf9805211 100644 --- a/pkg/apis/eventing/v1alpha1/channel_types.go +++ b/pkg/apis/eventing/v1alpha1/channel_types.go @@ -30,7 +30,7 @@ import ( // +genclient:noStatus // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// Channel is an abstract resource that implements the Sinkable contract. +// Channel is an abstract resource that implements the Addressable contract. // The Provisioner provisions infrastructure to accepts events and // deliver to Subscriptions. type Channel struct { @@ -67,8 +67,8 @@ type ChannelSpec struct { // Provisioner defines the name of the Provisioner backing this channel. Provisioner *corev1.ObjectReference `json:"provisioner,omitempty"` - // Arguments defines the arguments to pass to the Provisioner which provisions - // this Channel. + // Arguments defines the arguments to pass to the Provisioner which + // provisions this Channel. // +optional Arguments *runtime.RawExtension `json:"arguments,omitempty"` @@ -76,7 +76,7 @@ type ChannelSpec struct { Subscribable *eventingduck.Subscribable `json:"subscribable,omitempty"` } -var chanCondSet = duckv1alpha1.NewLivingConditionSet(ChannelConditionProvisioned, ChannelConditionSinkable) +var chanCondSet = duckv1alpha1.NewLivingConditionSet(ChannelConditionProvisioned, ChannelConditionAddressable) // ChannelStatus represents the current state of a Channel. type ChannelStatus struct { @@ -88,10 +88,12 @@ type ChannelStatus struct { // +optional ObservedGeneration int64 `json:"observedGeneration,omitempty"` - // Channel is Sinkable. It currently exposes the endpoint as top-level domain - // that will distribute traffic over the provided targets from inside the cluster. + // Channel is Addressable. It currently exposes the endpoint as a + // fully-qualified DNS name which will distribute traffic over the + // provided targets from inside the cluster. + // // It generally has the form {channel}.{namespace}.svc.cluster.local - Sinkable duckv1alpha1.Sinkable `json:"sinkable,omitempty"` + Address duckv1alpha1.Addressable `json:"address,omitempty"` // Represents the latest available observations of a channel's current state. // +optional @@ -101,17 +103,17 @@ type ChannelStatus struct { } const ( - // ChannelConditionReady has status True when the Channel is ready to accept - // traffic. + // ChannelConditionReady has status True when the Channel is ready to + // accept traffic. ChannelConditionReady = duckv1alpha1.ConditionReady - // ChannelConditionProvisioned has status True when the Channel's backing - // resources have been provisioned. + // ChannelConditionProvisioned has status True when the Channel's + // backing resources have been provisioned. ChannelConditionProvisioned duckv1alpha1.ConditionType = "Provisioned" - // ChannelConditionSinkable has status true when this Channel meets the Sinkable contract and - // has a non-empty domainInternal. - ChannelConditionSinkable duckv1alpha1.ConditionType = "Sinkable" + // ChannelConditionAddressable has status true when this Channel meets + // the Addressable contract and has a non-empty hostname. + ChannelConditionAddressable duckv1alpha1.ConditionType = "Addressable" ) // GetCondition returns the condition currently associated with the given type, or nil. @@ -134,14 +136,14 @@ func (cs *ChannelStatus) MarkProvisioned() { chanCondSet.Manage(cs).MarkTrue(ChannelConditionProvisioned) } -// SetSinkable makes this Channel sinkable by setting the domainInternal. It also sets the -// ChannelConditionSinkable to true. -func (cs *ChannelStatus) SetSinkable(domainInternal string) { - cs.Sinkable.DomainInternal = domainInternal - if domainInternal != "" { - chanCondSet.Manage(cs).MarkTrue(ChannelConditionSinkable) +// SetAddress makes this Channel addressable by setting the hostname. It also +// sets the ChannelConditionAddressable to true. +func (cs *ChannelStatus) SetAddress(hostname string) { + cs.Address.Hostname = hostname + if hostname != "" { + chanCondSet.Manage(cs).MarkTrue(ChannelConditionAddressable) } else { - chanCondSet.Manage(cs).MarkFalse(ChannelConditionSinkable, "emptyDomainInternal", "domainInternal is the empty string") + chanCondSet.Manage(cs).MarkFalse(ChannelConditionAddressable, "emptyHostname", "hostname is the empty string") } } diff --git a/pkg/apis/eventing/v1alpha1/channel_types_test.go b/pkg/apis/eventing/v1alpha1/channel_types_test.go index 3cdee5c281d..ed9d3737713 100644 --- a/pkg/apis/eventing/v1alpha1/channel_types_test.go +++ b/pkg/apis/eventing/v1alpha1/channel_types_test.go @@ -94,13 +94,13 @@ func TestChannelInitializeConditions(t *testing.T) { cs: &ChannelStatus{}, want: &ChannelStatus{ Conditions: []duckv1alpha1.Condition{{ - Type: ChannelConditionProvisioned, + Type: ChannelConditionAddressable, Status: corev1.ConditionUnknown, }, { - Type: ChannelConditionReady, + Type: ChannelConditionProvisioned, Status: corev1.ConditionUnknown, }, { - Type: ChannelConditionSinkable, + Type: ChannelConditionReady, Status: corev1.ConditionUnknown, }}, }, @@ -114,14 +114,14 @@ func TestChannelInitializeConditions(t *testing.T) { }, want: &ChannelStatus{ Conditions: []duckv1alpha1.Condition{{ + Type: ChannelConditionAddressable, + Status: corev1.ConditionUnknown, + }, { Type: ChannelConditionProvisioned, Status: corev1.ConditionFalse, }, { Type: ChannelConditionReady, Status: corev1.ConditionUnknown, - }, { - Type: ChannelConditionSinkable, - Status: corev1.ConditionUnknown, }}, }, }, { @@ -134,14 +134,14 @@ func TestChannelInitializeConditions(t *testing.T) { }, want: &ChannelStatus{ Conditions: []duckv1alpha1.Condition{{ + Type: ChannelConditionAddressable, + Status: corev1.ConditionUnknown, + }, { Type: ChannelConditionProvisioned, Status: corev1.ConditionTrue, }, { Type: ChannelConditionReady, Status: corev1.ConditionUnknown, - }, { - Type: ChannelConditionSinkable, - Status: corev1.ConditionUnknown, }}}, }, } @@ -161,17 +161,17 @@ func TestChannelIsReady(t *testing.T) { tests := []struct { name string markProvisioned bool - setSinkable bool + setAddress bool wantReady bool }{{ name: "all happy", markProvisioned: true, - setSinkable: true, + setAddress: true, wantReady: true, }, { name: "one sad", markProvisioned: false, - setSinkable: true, + setAddress: true, wantReady: false, }} for _, test := range tests { @@ -180,8 +180,8 @@ func TestChannelIsReady(t *testing.T) { if test.markProvisioned { cs.MarkProvisioned() } - if test.setSinkable { - cs.SetSinkable("foo.bar") + if test.setAddress { + cs.SetAddress("foo.bar") } got := cs.IsReady() if test.wantReady != got { @@ -191,7 +191,7 @@ func TestChannelIsReady(t *testing.T) { } } -func TestChannelStatus_SetSinkable(t *testing.T) { +func TestChannelStatus_SetAddressable(t *testing.T) { testCases := map[string]struct { domainInternal string want *ChannelStatus @@ -199,14 +199,14 @@ func TestChannelStatus_SetSinkable(t *testing.T) { "empty string": { want: &ChannelStatus{ Conditions: []duckv1alpha1.Condition{ - // Note that Ready is here because when the condition is marked False, duck - // automatically sets Ready to false. { - Type: ChannelConditionReady, + Type: ChannelConditionAddressable, Status: corev1.ConditionFalse, }, + // Note that Ready is here because when the condition is marked False, duck + // automatically sets Ready to false. { - Type: ChannelConditionSinkable, + Type: ChannelConditionReady, Status: corev1.ConditionFalse, }, }, @@ -215,12 +215,12 @@ func TestChannelStatus_SetSinkable(t *testing.T) { "has domain": { domainInternal: "test-domain", want: &ChannelStatus{ - Sinkable: duckv1alpha1.Sinkable{ - DomainInternal: "test-domain", + Address: duckv1alpha1.Addressable{ + Hostname: "test-domain", }, Conditions: []duckv1alpha1.Condition{ { - Type: ChannelConditionSinkable, + Type: ChannelConditionAddressable, Status: corev1.ConditionTrue, }, }, @@ -230,7 +230,7 @@ func TestChannelStatus_SetSinkable(t *testing.T) { for n, tc := range testCases { t.Run(n, func(t *testing.T) { cs := &ChannelStatus{} - cs.SetSinkable(tc.domainInternal) + cs.SetAddress(tc.domainInternal) if diff := cmp.Diff(tc.want, cs, ignoreTransitionTimeMessageAndReason); diff != "" { t.Errorf("unexpected conditions (-want, +got) = %v", diff) } diff --git a/pkg/apis/eventing/v1alpha1/implements_test.go b/pkg/apis/eventing/v1alpha1/implements_test.go index 9e701cf8659..84768e12091 100644 --- a/pkg/apis/eventing/v1alpha1/implements_test.go +++ b/pkg/apis/eventing/v1alpha1/implements_test.go @@ -28,8 +28,9 @@ func TestTypesImplements(t *testing.T) { }{ // Channel {instance: &Channel{}, iface: &duckv1alpha1.Conditions{}}, + {instance: &Channel{}, iface: &emptyGen}, {instance: &Channel{}, iface: &eventingduck.Subscribable{}}, - {instance: &Channel{}, iface: &duckv1alpha1.Sinkable{}}, + {instance: &Channel{}, iface: &duckv1alpha1.Addressable{}}, // ClusterChannelProvisioner {instance: &ClusterChannelProvisioner{}, iface: &duckv1alpha1.Conditions{}}, // Subscription diff --git a/pkg/apis/eventing/v1alpha1/subscription_types.go b/pkg/apis/eventing/v1alpha1/subscription_types.go index 97a60e60670..1d7b2950eeb 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_types.go +++ b/pkg/apis/eventing/v1alpha1/subscription_types.go @@ -124,7 +124,7 @@ type SubscriberSpec struct { // Only one of these can be specified // Reference to an object that will be used to find the target - // endpoint. + // endpoint, which should implement the Addressable duck type. // For example, this could be a reference to a Route resource // or a Knative Service resource. // TODO: Specify the required fields the target object must diff --git a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go index 8b14c429ef1..56b501996f4 100644 --- a/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go +++ b/pkg/apis/eventing/v1alpha1/zz_generated.deepcopy.go @@ -134,7 +134,7 @@ func (in *ChannelSpec) DeepCopy() *ChannelSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ChannelStatus) DeepCopyInto(out *ChannelStatus) { *out = *in - out.Sinkable = in.Sinkable + out.Address = in.Address if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions *out = make(apis_duck_v1alpha1.Conditions, len(*in)) diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 7d63e34925b..405312dbcc9 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -146,7 +146,7 @@ func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) logger.Info("Error creating the Channel's K8s Service", zap.Error(err)) return err } else { - c.Status.SetSinkable(controller.ServiceHostName(svc.Name, svc.Namespace)) + c.Status.SetAddress(controller.ServiceHostName(svc.Name, svc.Namespace)) } if err := r.createVirtualService(ctx, c); err != nil { diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index 7f679d9360e..16199c19bc5 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -352,7 +352,7 @@ func TestReconcile(t *testing.T) { WantPresent: []runtime.Object{ // TODO: This should have a useful error message saying that the VirtualService // failed. - makeChannelWithFinalizerAndSinkable(), + makeChannelWithFinalizerAndAddress(), }, WantErrMsg: testErrorMessage, }, @@ -369,7 +369,7 @@ func TestReconcile(t *testing.T) { WantPresent: []runtime.Object{ // TODO: This should have a useful error message saying that the VirtualService // failed. - makeChannelWithFinalizerAndSinkable(), + makeChannelWithFinalizerAndAddress(), }, WantErrMsg: testErrorMessage, }, @@ -515,15 +515,15 @@ func makeChannel() *eventingv1alpha1.Channel { return c } -func makeChannelWithFinalizerAndSinkable() *eventingv1alpha1.Channel { +func makeChannelWithFinalizerAndAddress() *eventingv1alpha1.Channel { c := makeChannelWithFinalizer() - c.Status.SetSinkable(fmt.Sprintf("%s-channel.%s.svc.cluster.local", c.Name, c.Namespace)) + c.Status.SetAddress(fmt.Sprintf("%s-channel.%s.svc.cluster.local", c.Name, c.Namespace)) return c } func makeReadyChannel() *eventingv1alpha1.Channel { - // Ready channels have the finalizer and are Sinkable. - c := makeChannelWithFinalizerAndSinkable() + // Ready channels have the finalizer and are Addressable. + c := makeChannelWithFinalizerAndAddress() c.Status.MarkProvisioned() return c } diff --git a/pkg/controller/eventing/subscription/reconcile.go b/pkg/controller/eventing/subscription/reconcile.go index 3d36c23aaaf..7ba8bcba534 100644 --- a/pkg/controller/eventing/subscription/reconcile.go +++ b/pkg/controller/eventing/subscription/reconcile.go @@ -167,9 +167,9 @@ func (r *reconciler) updateStatus(subscription *v1alpha1.Subscription) (*v1alpha } // resolveSubscriberSpec resolves the Spec.Call object. If it's an -// ObjectReference will resolve the object and treat it as a Targetable. If +// ObjectReference will resolve the object and treat it as a Callable. If // it's DNSName then it's used as is. -// TODO: Once Service Routes, etc. support Targetable, use that. +// TODO: Once Service Routes, etc. support Callable, use that. // func (r *reconciler) resolveSubscriberSpec(namespace string, s v1alpha1.SubscriberSpec) (string, error) { if s.DNSName != nil && *s.DNSName != "" { @@ -177,7 +177,7 @@ func (r *reconciler) resolveSubscriberSpec(namespace string, s v1alpha1.Subscrib } // K8s services are special cased. They can be called, even though they do not satisfy the - // Targetable interface. + // Callable interface. if s.Ref != nil && s.Ref.APIVersion == "v1" && s.Ref.Kind == "Service" { svc := &corev1.Service{} svcKey := types.NamespacedName{ @@ -197,17 +197,17 @@ func (r *reconciler) resolveSubscriberSpec(namespace string, s v1alpha1.Subscrib glog.Warningf("Failed to fetch SubscriberSpec target %+v: %s", s.Ref, err) return "", err } - t := duckv1alpha1.Target{} + t := duckv1alpha1.AddressableType{} err = duck.FromUnstructured(obj, &t) if err != nil { glog.Warningf("Failed to deserialize legacy target: %s", err) return "", err } - if t.Status.Targetable != nil { - return domainToURL(t.Status.Targetable.DomainInternal), nil + if t.Status.Address != nil { + return domainToURL(t.Status.Address.Hostname), nil } - return "", fmt.Errorf("status does not contain targetable") + return "", fmt.Errorf("status does not contain address") } // resolveResult resolves the Spec.Result object. @@ -217,16 +217,16 @@ func (r *reconciler) resolveResult(namespace string, replyStrategy v1alpha1.Repl glog.Warningf("Failed to fetch ReplyStrategy channel %+v: %s", replyStrategy, err) return "", err } - s := duckv1alpha1.Sink{} + s := duckv1alpha1.AddressableType{} err = duck.FromUnstructured(obj, &s) if err != nil { - glog.Warningf("Failed to deserialize Sinkable target: %s", err) + glog.Warningf("Failed to deserialize Addressable target: %s", err) return "", err } - if s.Status.Sinkable != nil { - return domainToURL(s.Status.Sinkable.DomainInternal), nil + if s.Status.Address != nil { + return domainToURL(s.Status.Address.Hostname), nil } - return "", fmt.Errorf("status does not contain sinkable") + return "", fmt.Errorf("status does not contain address") } // fetchObjectReference fetches an object based on ObjectReference. diff --git a/pkg/controller/eventing/subscription/reconcile_test.go b/pkg/controller/eventing/subscription/reconcile_test.go index 349cf106404..7259a40476c 100644 --- a/pkg/controller/eventing/subscription/reconcile_test.go +++ b/pkg/controller/eventing/subscription/reconcile_test.go @@ -54,7 +54,7 @@ const ( testNS = "testnamespace" k8sServiceName = "testk8sservice" k8sServiceDNS = "testk8sservice.testnamespace.svc.cluster.local" - otherSinkableDNS = "other-sinkable-channel.mynamespace.svc.cluster.local" + otherAddressableDNS = "other-sinkable-channel.mynamespace.svc.cluster.local" ) func init() { @@ -107,8 +107,8 @@ var testCases = []controllertesting.TestCase{ "name": routeName, }, "status": map[string]interface{}{ - "targetable": map[string]interface{}{ - "domainInternal": targetDNS, + "address": map[string]interface{}{ + "hostname": targetDNS, }, }, }, @@ -126,8 +126,8 @@ var testCases = []controllertesting.TestCase{ "subscribable": map[string]interface{}{}, }, "status": map[string]interface{}{ - "sinkable": map[string]interface{}{ - "domainInternal": sinkableDNS, + "address": map[string]interface{}{ + "hostname": sinkableDNS, }, }, }, @@ -160,14 +160,14 @@ var testCases = []controllertesting.TestCase{ }, }, }, { - Name: "Valid channel, subscriber is not targetable", + Name: "Valid channel, subscriber is not callable", InitialState: []runtime.Object{ getNewSubscription(), }, WantPresent: []runtime.Object{ getNewSubscriptionWithUnknownConditions(), }, - WantErrMsg: "status does not contain targetable", + WantErrMsg: "status does not contain address", Scheme: scheme.Scheme, Objects: []runtime.Object{ // Source channel @@ -234,19 +234,19 @@ var testCases = []controllertesting.TestCase{ "name": routeName, }, "status": map[string]interface{}{ - "targetable": map[string]interface{}{ - "domainInternal": targetDNS, + "address": map[string]interface{}{ + "hostname": targetDNS, }, }, }, }, }, }, { - Name: "valid channel, subscriber, result is not sinkable", + Name: "valid channel, subscriber, result is not addressable", InitialState: []runtime.Object{ getNewSubscription(), }, - WantErrMsg: "status does not contain sinkable", + WantErrMsg: "status does not contain address", WantPresent: []runtime.Object{ // TODO: Again this works on gke cluster, but I need to set // something else up here. later... @@ -279,8 +279,8 @@ var testCases = []controllertesting.TestCase{ "name": routeName, }, "status": map[string]interface{}{ - "targetable": map[string]interface{}{ - "domainInternal": targetDNS, + "address": map[string]interface{}{ + "hostname": targetDNS, }, }, }, @@ -339,8 +339,8 @@ var testCases = []controllertesting.TestCase{ "name": routeName, }, "status": map[string]interface{}{ - "targetable": map[string]interface{}{ - "domainInternal": targetDNS, + "address": map[string]interface{}{ + "hostname": targetDNS, }, }, }, @@ -358,8 +358,8 @@ var testCases = []controllertesting.TestCase{ "subscribable": map[string]interface{}{}, }, "status": map[string]interface{}{ - "sinkable": map[string]interface{}{ - "domainInternal": sinkableDNS, + "address": map[string]interface{}{ + "hostname": sinkableDNS, }, }, }, @@ -390,7 +390,7 @@ var testCases = []controllertesting.TestCase{ "name": fromChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, }, }, @@ -404,8 +404,8 @@ var testCases = []controllertesting.TestCase{ "name": routeName, }, "status": map[string]interface{}{ - "targetable": map[string]interface{}{ - "domainInternal": targetDNS, + "address": map[string]interface{}{ + "hostname": targetDNS, }, }, }, @@ -436,7 +436,7 @@ var testCases = []controllertesting.TestCase{ "name": fromChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, }, }, @@ -450,11 +450,11 @@ var testCases = []controllertesting.TestCase{ "name": resultChannelName, }, "spec": map[string]interface{}{ - "channelable": map[string]interface{}{}, + "subscribable": map[string]interface{}{}, }, "status": map[string]interface{}{ - "sinkable": map[string]interface{}{ - "domainInternal": sinkableDNS, + "address": map[string]interface{}{ + "hostname": sinkableDNS, }, }, }, @@ -514,8 +514,8 @@ var testCases = []controllertesting.TestCase{ "subscribable": map[string]interface{}{}, }, "status": map[string]interface{}{ - "sinkable": map[string]interface{}{ - "domainInternal": sinkableDNS, + "address": map[string]interface{}{ + "hostname": sinkableDNS, }, }, }, @@ -574,8 +574,8 @@ var testCases = []controllertesting.TestCase{ "name": routeName, }, "status": map[string]interface{}{ - "targetable": map[string]interface{}{ - "domainInternal": targetDNS, + "address": map[string]interface{}{ + "hostname": targetDNS, }, }, }, @@ -593,8 +593,8 @@ var testCases = []controllertesting.TestCase{ "subscribable": map[string]interface{}{}, }, "status": map[string]interface{}{ - "sinkable": map[string]interface{}{ - "domainInternal": sinkableDNS, + "address": map[string]interface{}{ + "hostname": sinkableDNS, }, }, }, @@ -669,8 +669,8 @@ var testCases = []controllertesting.TestCase{ "name": routeName, }, "status": map[string]interface{}{ - "targetable": map[string]interface{}{ - "domainInternal": targetDNS, + "address": map[string]interface{}{ + "hostname": targetDNS, }, }, }, @@ -688,8 +688,8 @@ var testCases = []controllertesting.TestCase{ "subscribable": map[string]interface{}{}, }, "status": map[string]interface{}{ - "sinkable": map[string]interface{}{ - "domainInternal": sinkableDNS, + "address": map[string]interface{}{ + "hostname": sinkableDNS, }, }, }, @@ -744,7 +744,7 @@ func rename(sub *eventingv1alpha1.Subscription) *eventingv1alpha1.Subscription { sub.Name = "renamed" sub.UID = "renamed-UID" sub.Status.PhysicalSubscription.SubscriberURI = "" - sub.Status.PhysicalSubscription.ReplyURI = otherSinkableDNS + sub.Status.PhysicalSubscription.ReplyURI = otherAddressableDNS return sub } @@ -975,7 +975,7 @@ func getChannelWithMultipleSubscriptions() *eventingv1alpha1.Channel { Name: "renamed", UID: "renamed-UID", }, - ReplyURI: otherSinkableDNS, + ReplyURI: otherAddressableDNS, }, }, }, diff --git a/pkg/sidecar/fanout/fanout_handler_test.go b/pkg/sidecar/fanout/fanout_handler_test.go index 464afcff9b2..4df6ef744ed 100644 --- a/pkg/sidecar/fanout/fanout_handler_test.go +++ b/pkg/sidecar/fanout/fanout_handler_test.go @@ -36,7 +36,7 @@ import ( // servers. const ( replaceSubscriber = "replaceSubscriber" - replaceSinkable = "replaceSinkable" + replaceChannel = "replaceChannel" ) var ( @@ -62,7 +62,7 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { timeout time.Duration subs []eventingduck.ChannelSubscriberSpec subscriber func(http.ResponseWriter, *http.Request) - sinkable func(http.ResponseWriter, *http.Request) + channel func(http.ResponseWriter, *http.Request) expectedStatus int }{ "rejected by receiver": { @@ -97,10 +97,10 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { "reply fails": { subs: []eventingduck.ChannelSubscriberSpec{ { - ReplyURI: replaceSinkable, + ReplyURI: replaceChannel, }, }, - sinkable: func(writer http.ResponseWriter, _ *http.Request) { + channel: func(writer http.ResponseWriter, _ *http.Request) { writer.WriteHeader(http.StatusNotFound) }, expectedStatus: http.StatusInternalServerError, @@ -116,15 +116,15 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { }, expectedStatus: http.StatusInternalServerError, }, - "subscriber succeeds, sinkable fails": { + "subscriber succeeds, result fails": { subs: []eventingduck.ChannelSubscriberSpec{ { SubscriberURI: replaceSubscriber, - ReplyURI: replaceSinkable, + ReplyURI: replaceChannel, }, }, subscriber: callableSucceed, - sinkable: func(writer http.ResponseWriter, _ *http.Request) { + channel: func(writer http.ResponseWriter, _ *http.Request) { writer.WriteHeader(http.StatusForbidden) }, expectedStatus: http.StatusInternalServerError, @@ -133,11 +133,11 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { subs: []eventingduck.ChannelSubscriberSpec{ { SubscriberURI: replaceSubscriber, - ReplyURI: replaceSinkable, + ReplyURI: replaceChannel, }, }, subscriber: callableSucceed, - sinkable: func(writer http.ResponseWriter, _ *http.Request) { + channel: func(writer http.ResponseWriter, _ *http.Request) { writer.WriteHeader(http.StatusAccepted) }, expectedStatus: http.StatusAccepted, @@ -146,34 +146,34 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { subs: []eventingduck.ChannelSubscriberSpec{ { SubscriberURI: replaceSubscriber, - ReplyURI: replaceSinkable, + ReplyURI: replaceChannel, }, { SubscriberURI: replaceSubscriber, - ReplyURI: replaceSinkable, + ReplyURI: replaceChannel, }, }, subscriber: callableSucceed, - sinkable: (&succeedOnce{}).handler, + channel: (&succeedOnce{}).handler, expectedStatus: http.StatusInternalServerError, }, "all subs succeed": { subs: []eventingduck.ChannelSubscriberSpec{ { SubscriberURI: replaceSubscriber, - ReplyURI: replaceSinkable, + ReplyURI: replaceChannel, }, { SubscriberURI: replaceSubscriber, - ReplyURI: replaceSinkable, + ReplyURI: replaceChannel, }, { SubscriberURI: replaceSubscriber, - ReplyURI: replaceSinkable, + ReplyURI: replaceChannel, }, }, subscriber: callableSucceed, - sinkable: func(writer http.ResponseWriter, _ *http.Request) { + channel: func(writer http.ResponseWriter, _ *http.Request) { writer.WriteHeader(http.StatusAccepted) }, expectedStatus: http.StatusAccepted, @@ -188,10 +188,10 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { handler: tc.subscriber, }) defer callableServer.Close() - sinkableServer := httptest.NewServer(&fakeHandler{ - handler: tc.sinkable, + channelServer := httptest.NewServer(&fakeHandler{ + handler: tc.channel, }) - defer sinkableServer.Close() + defer channelServer.Close() // Rewrite the subs to use the servers we just started. subs := make([]eventingduck.ChannelSubscriberSpec, 0) @@ -199,8 +199,8 @@ func TestFanoutHandler_ServeHTTP(t *testing.T) { if sub.SubscriberURI == replaceSubscriber { sub.SubscriberURI = callableServer.URL[7:] // strip the leading 'http://' } - if sub.ReplyURI == replaceSinkable { - sub.ReplyURI = sinkableServer.URL[7:] // strip the leading 'http://' + if sub.ReplyURI == replaceChannel { + sub.ReplyURI = channelServer.URL[7:] // strip the leading 'http://' } subs = append(subs, sub) } diff --git a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/addressable_types.go b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/addressable_types.go new file mode 100644 index 00000000000..0bf1f986dc0 --- /dev/null +++ b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/addressable_types.go @@ -0,0 +1,96 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + + "github.com/knative/pkg/apis" + "github.com/knative/pkg/apis/duck" +) + +// Addressable provides a generic mechanism for a custom resource +// definition to indicate a destination for message delivery. +// (Currently, only hostname is supported, and HTTP is implied. In the +// future, additional schemes may be supported, and path components +// ala UI may also be supported.) + +// Addressable is the schema for the destination information. This is +// typically stored in the object's `status`, as this information may +// be generated by the controller. +type Addressable struct { + Hostname string `json:"hostname,omitempty"` +} + + +// Addressable is an Implementable "duck type". +var _ duck.Implementable = (*Addressable)(nil) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// AddressableType is a skeleton type wrapping Addressable in the manner we expect +// resource writers defining compatible resources to embed it. We will +// typically use this type to deserialize Addressable ObjectReferences and +// access the Addressable data. This is not a real resource. +type AddressableType struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Status AddressStatus `json:"status"` +} + +// AddressStatus shows how we expect folks to embed Addressable in +// their Status field. +type AddressStatus struct { + Address *Addressable `json:"address,omitempty"` +} + +// Verify AddressableType resources meet duck contracts. +var _ duck.Populatable = (*AddressableType)(nil) +var _ apis.Listable = (*AddressableType)(nil) + +// GetFullType implements duck.Implementable +func (_ *Addressable) GetFullType() duck.Populatable { + return &AddressableType{} +} + +// Populate implements duck.Populatable +func (t *AddressableType) Populate() { + t.Status = AddressStatus{ + &Addressable{ + // Populate ALL fields + Hostname: "this is not empty", + }, + } +} + +// GetListType implements apis.Listable +func (r *AddressableType) GetListType() runtime.Object { + return &AddressableTypeList{} +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// AddressableTypeList is a list of AddressableType resources +type AddressableTypeList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []AddressableType `json:"items"` +} diff --git a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/channelable_types.go b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/channelable_types.go deleted file mode 100644 index ba60d3b8025..00000000000 --- a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/channelable_types.go +++ /dev/null @@ -1,104 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - - "github.com/knative/pkg/apis" - "github.com/knative/pkg/apis/duck" -) - -// Channelable is the schema for the channelable portion of the spec -// section of the resource. -type Channelable struct { - // TODO: What is actually required here for Channel spec. - // This is the list of subscriptions for this channel. - Subscribers []ChannelSubscriberSpec `json:"subscribers,omitempty"` -} - -// ChannelSubscriberSpec defines a single subscriber to a Channel. -// CallableURI is the endpoint for the call -// SinkableURI is the endpoint for the result -// At least one of them must be present. -type ChannelSubscriberSpec struct { - // +optional - CallableURI string `json:"callableURI,omitempty"` - // +optional - SinkableURI string `json:"sinkableURI,omitempty"` -} - - -// Channelable is an Implementable "duck type". -var _ duck.Implementable = (*Channelable)(nil) - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Channel is a skeleton type wrapping Channelable in the manner we expect -// resource writers defining compatible resources to embed it. We will -// typically use this type to deserialize Channelable ObjectReferences and -// access the Channelable data. This is not a real resource. -type Channel struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - // ChannelSpec is the part where Channelable object is - // configured as to be compatible with Channelable contract. - Spec ChannelSpec `json:"spec"` -} - -// ChannelSpec shows how we expect folks to embed Channelable in -// their Spec field. -type ChannelSpec struct { - Channelable *Channelable `json:"channelable,omitempty"` -} - -// In order for Channelable to be Implementable, Channel must be Populatable. -var _ duck.Populatable = (*Channel)(nil) - -// Ensure Channel satisfies apis.Listable -var _ apis.Listable = (*Channel)(nil) - -// GetFullType implements duck.Implementable -func (_ *Channelable) GetFullType() duck.Populatable { - return &Channel{} -} - -// Populate implements duck.Populatable -func (t *Channel) Populate() { - t.Spec.Channelable = &Channelable{ - // Populate ALL fields - Subscribers: []ChannelSubscriberSpec{{"call1", "sink2"}, {"call2", "sink2"}}, - } -} - -// GetListType implements apis.Listable -func (r *Channel) GetListType() runtime.Object { - return &ChannelList{} -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// ChannelList is a list of Channel resources -type ChannelList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []Channel `json:"items"` -} diff --git a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/legacy_targetable_types.go b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/legacy_targetable_types.go index 6bfc0881805..ee50201e83c 100644 --- a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/legacy_targetable_types.go +++ b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/legacy_targetable_types.go @@ -24,19 +24,19 @@ import ( "github.com/knative/pkg/apis/duck" ) -// LegacyTargetable left around until we migrate to Targetable in the -// dependent resources. Targetable has more structure in the way it +// LegacyTargetable left around until we migrate to Addressable in the +// dependent resources. Addressable has more structure in the way it // defines the fields. LegacyTargetable only assumed a single string // in the Status field and we're moving towards defining proper structs // under Status rather than strings. // This is to support existing resources until they migrate. // -// Do not use this for anything new, use Targetable +// Do not use this for anything new, use Addressable -// LegacyTargetable is the old schema for the targetable portion +// LegacyTargetable is the old schema for the addressable portion // of the payload // -// For new resources use Targetable. +// For new resources use Addressable. type LegacyTargetable struct { DomainInternal string `json:"domainInternal,omitempty"` } diff --git a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/register.go b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/register.go index f3bfc5ae1d1..a0264e576e0 100644 --- a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/register.go +++ b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/register.go @@ -45,20 +45,16 @@ var ( func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes( SchemeGroupVersion, - &Channel{}, - (&Channel{}).GetListType(), &KResource{}, (&KResource{}).GetListType(), &Generational{}, (&Generational{}).GetListType(), + &AddressableType{}, + (&AddressableType{}).GetListType(), &Target{}, (&Target{}).GetListType(), &LegacyTarget{}, (&LegacyTarget{}).GetListType(), - &Sink{}, - (&Sink{}).GetListType(), - &Subscription{}, - (&Subscription{}).GetListType(), ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/targetable_types.go b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/retired_targetable_types.go similarity index 84% rename from vendor/github.com/knative/pkg/apis/duck/v1alpha1/targetable_types.go rename to vendor/github.com/knative/pkg/apis/duck/v1alpha1/retired_targetable_types.go index 5cd454ba52b..695e11c3f65 100644 --- a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/targetable_types.go +++ b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/retired_targetable_types.go @@ -24,11 +24,15 @@ import ( "github.com/knative/pkg/apis/duck" ) -// Targetable is very similar concept as Sinkable. However, at the -// transport level they have different contracts and hence Sinkable -// and Targetable are two distinct resources. - -// Targetable is the schema for the targetable portion of the payload +// Targetable is an earlier version of the Callable interface. +// Callable is a higher-level interface which implements Addressable +// but further promises that the destination may synchronously return +// response messages in reply to a message. +// +// Targetable implementations should instead implement Addressable and +// include an `eventing.knative.dev/returns=any` annotation. + +// Targetable is retired; implement Addressable for now. type Targetable struct { DomainInternal string `json:"domainInternal,omitempty"` } diff --git a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/sinkable_types.go b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/sinkable_types.go deleted file mode 100644 index 2ad0ab5e3e9..00000000000 --- a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/sinkable_types.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - - "github.com/knative/pkg/apis" - "github.com/knative/pkg/apis/duck" -) - -// Sinkable is very similar concept as Targetable. However, at the -// transport level they have different contracts and hence Sinkable -// and Targetable are two distinct resources. - -// Sinkable is the schema for the sinkable portion of the payload -type Sinkable struct { - DomainInternal string `json:"domainInternal,omitempty"` -} - - -// Sinkable is an Implementable "duck type". -var _ duck.Implementable = (*Sinkable)(nil) - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Sink is a skeleton type wrapping Sinkable in the manner we expect -// resource writers defining compatible resources to embed it. We will -// typically use this type to deserialize Sinkable ObjectReferences and -// access the Sinkable data. This is not a real resource. -type Sink struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Status SinkStatus `json:"status"` -} - -// SinkStatus shows how we expect folks to embed Sinkable in -// their Status field. -type SinkStatus struct { - Sinkable *Sinkable `json:"sinkable,omitempty"` -} - -// In order for Sinkable to be Implementable, Sink must be Populatable. -var _ duck.Populatable = (*Sink)(nil) - -// Ensure Sink satisfies apis.Listable -var _ apis.Listable = (*Sink)(nil) - -// GetFullType implements duck.Implementable -func (_ *Sinkable) GetFullType() duck.Populatable { - return &Sink{} -} - -// Populate implements duck.Populatable -func (t *Sink) Populate() { - t.Status = SinkStatus{ - &Sinkable{ - // Populate ALL fields - DomainInternal: "this is not empty", - }, - } -} - -// GetListType implements apis.Listable -func (r *Sink) GetListType() runtime.Object { - return &SinkList{} -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// SinkList is a list of Sink resources -type SinkList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []Sink `json:"items"` -} diff --git a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/subscribable_types.go b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/subscribable_types.go deleted file mode 100644 index e8d03f890d4..00000000000 --- a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/subscribable_types.go +++ /dev/null @@ -1,105 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - - "github.com/knative/pkg/apis" - "github.com/knative/pkg/apis/duck" -) - -// Subscribable is the schema for the subscribable portion of the payload. -// It is a reference to actual object that implements Channelable duck -// type. -type Subscribable struct { - // Channelable is a reference to the actual resource - // that provides the ability to perform Subscription capabilities. - // This may point to object itself (for example Channel) or to another - // object providing the actual capabilities.. - Channelable corev1.ObjectReference `json:"channelable,omitempty"` -} - - -// Subscribable is an Implementable "duck type". -var _ duck.Implementable = (*Subscribable)(nil) - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Subscription is a skeleton type wrapping the notion that this object -// can be subscribed to. SubscriptionStatus provides the reference -// (in a form of Subscribable) to the object that you can actually create -// a subscription to. -// We will typically use this type to deserialize Subscription objects -// to access the Subscripion data. This is not a real resource. -type Subscription struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - // SubscriptionStatus is the part of the Status where a Subscribable - // object points to the underlying Channelable object that fullfills - // the SubscribableSpec contract. Note that this can be a self-link - // for example for concrete Channel implementations. - Status SubscriptionStatus `json:"status"` -} - -// SubscriptionStatus shows how we expect folks to embed Subscribable in -// their Status field. -type SubscriptionStatus struct { - Subscribable *Subscribable `json:"subscribable,omitempty"` -} - -// In order for Subscribable to be Implementable, Subscribable must be Populatable. -var _ duck.Populatable = (*Subscription)(nil) - -// Ensure Subscription satisfies apis.Listable -var _ apis.Listable = (*Subscription)(nil) - -// GetFullType implements duck.Implementable -func (_ *Subscribable) GetFullType() duck.Populatable { - return &Subscription{} -} - -// Populate implements duck.Populatable -func (t *Subscription) Populate() { - t.Status.Subscribable = &Subscribable{ - // Populate ALL fields - Channelable: corev1.ObjectReference{ - Name: "placeholdername", - APIVersion: "apiversionhere", - Kind: "ChannelKindHere", - }, - } -} - -// GetListType implements apis.Listable -func (r *Subscription) GetListType() runtime.Object { - return &SubscriptionList{} -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// SubscribableList is a list of Subscribable resources -type SubscriptionList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []Subscription `json:"items"` -} diff --git a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go index 203f1ec5b31..731c7059dc0 100644 --- a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go +++ b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/zz_generated.deepcopy.go @@ -25,123 +25,102 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Channel) DeepCopyInto(out *Channel) { +func (in *AddressStatus) DeepCopyInto(out *AddressStatus) { *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) + if in.Address != nil { + in, out := &in.Address, &out.Address + *out = new(Addressable) + **out = **in + } return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Channel. -func (in *Channel) DeepCopy() *Channel { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddressStatus. +func (in *AddressStatus) DeepCopy() *AddressStatus { if in == nil { return nil } - out := new(Channel) + out := new(AddressStatus) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Channel) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ChannelList) DeepCopyInto(out *ChannelList) { +func (in *Addressable) DeepCopyInto(out *Addressable) { *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Channel, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelList. -func (in *ChannelList) DeepCopy() *ChannelList { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Addressable. +func (in *Addressable) DeepCopy() *Addressable { if in == nil { return nil } - out := new(ChannelList) + out := new(Addressable) in.DeepCopyInto(out) return out } -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ChannelList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ChannelSpec) DeepCopyInto(out *ChannelSpec) { +func (in *AddressableType) DeepCopyInto(out *AddressableType) { *out = *in - if in.Channelable != nil { - in, out := &in.Channelable, &out.Channelable - *out = new(Channelable) - (*in).DeepCopyInto(*out) - } + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Status.DeepCopyInto(&out.Status) return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelSpec. -func (in *ChannelSpec) DeepCopy() *ChannelSpec { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddressableType. +func (in *AddressableType) DeepCopy() *AddressableType { if in == nil { return nil } - out := new(ChannelSpec) + out := new(AddressableType) in.DeepCopyInto(out) return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ChannelSubscriberSpec) DeepCopyInto(out *ChannelSubscriberSpec) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelSubscriberSpec. -func (in *ChannelSubscriberSpec) DeepCopy() *ChannelSubscriberSpec { - if in == nil { - return nil +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AddressableType) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c } - out := new(ChannelSubscriberSpec) - in.DeepCopyInto(out) - return out + return nil } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Channelable) DeepCopyInto(out *Channelable) { +func (in *AddressableTypeList) DeepCopyInto(out *AddressableTypeList) { *out = *in - if in.Subscribers != nil { - in, out := &in.Subscribers, &out.Subscribers - *out = make([]ChannelSubscriberSpec, len(*in)) - copy(*out, *in) + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]AddressableType, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } return } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Channelable. -func (in *Channelable) DeepCopy() *Channelable { +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddressableTypeList. +func (in *AddressableTypeList) DeepCopy() *AddressableTypeList { if in == nil { return nil } - out := new(Channelable) + out := new(AddressableTypeList) in.DeepCopyInto(out) return out } +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AddressableTypeList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Condition) DeepCopyInto(out *Condition) { *out = *in @@ -416,201 +395,6 @@ func (in *LegacyTargetable) DeepCopy() *LegacyTargetable { return out } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Sink) DeepCopyInto(out *Sink) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sink. -func (in *Sink) DeepCopy() *Sink { - if in == nil { - return nil - } - out := new(Sink) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Sink) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SinkList) DeepCopyInto(out *SinkList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Sink, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SinkList. -func (in *SinkList) DeepCopy() *SinkList { - if in == nil { - return nil - } - out := new(SinkList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *SinkList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SinkStatus) DeepCopyInto(out *SinkStatus) { - *out = *in - if in.Sinkable != nil { - in, out := &in.Sinkable, &out.Sinkable - *out = new(Sinkable) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SinkStatus. -func (in *SinkStatus) DeepCopy() *SinkStatus { - if in == nil { - return nil - } - out := new(SinkStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Sinkable) DeepCopyInto(out *Sinkable) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Sinkable. -func (in *Sinkable) DeepCopy() *Sinkable { - if in == nil { - return nil - } - out := new(Sinkable) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Subscribable) DeepCopyInto(out *Subscribable) { - *out = *in - out.Channelable = in.Channelable - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subscribable. -func (in *Subscribable) DeepCopy() *Subscribable { - if in == nil { - return nil - } - out := new(Subscribable) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Subscription) DeepCopyInto(out *Subscription) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subscription. -func (in *Subscription) DeepCopy() *Subscription { - if in == nil { - return nil - } - out := new(Subscription) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Subscription) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SubscriptionList) DeepCopyInto(out *SubscriptionList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Subscription, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscriptionList. -func (in *SubscriptionList) DeepCopy() *SubscriptionList { - if in == nil { - return nil - } - out := new(SubscriptionList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *SubscriptionList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SubscriptionStatus) DeepCopyInto(out *SubscriptionStatus) { - *out = *in - if in.Subscribable != nil { - in, out := &in.Subscribable, &out.Subscribable - *out = new(Subscribable) - **out = **in - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscriptionStatus. -func (in *SubscriptionStatus) DeepCopy() *SubscriptionStatus { - if in == nil { - return nil - } - out := new(SubscriptionStatus) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Target) DeepCopyInto(out *Target) { *out = *in From 9b7cec6bfa2d8a3d00a6f14261b746aeb4b2f0b5 Mon Sep 17 00:00:00 2001 From: Ville Aikas Date: Tue, 6 Nov 2018 11:29:37 -0800 Subject: [PATCH 23/54] move to new model with k8s event source from event-sources (#579) * move to new model with k8s event source from event-sources * match version from head * Dump the created resources before deleting them to get more debug information in test failure cases --- Gopkg.lock | 18 +- test/cleanup.go | 6 + test/clients.go | 7 + test/crd.go | 142 ++++--- test/e2e-tests.sh | 28 +- test/e2e/e2e.go | 75 +--- test/e2e/k8s_events_test.go | 51 +-- .../knative/eventing-sources/AUTHORS | 6 + .../knative/eventing-sources/LICENSE | 201 ++++++++++ .../sources/v1alpha1/containersource_types.go | 160 ++++++++ .../pkg/apis/sources/v1alpha1/doc.go | 23 ++ .../apis/sources/v1alpha1/gcp_pubsub_types.go | 168 +++++++++ .../sources/v1alpha1/gcp_pubsub_validation.go | 44 +++ .../v1alpha1/kuberneteseventsource_types.go | 121 ++++++ .../pkg/apis/sources/v1alpha1/register.go | 45 +++ .../sources/v1alpha1/zz_generated.deepcopy.go | 355 ++++++++++++++++++ .../client/clientset/versioned/clientset.go | 98 +++++ .../pkg/client/clientset/versioned/doc.go | 20 + .../client/clientset/versioned/scheme/doc.go | 20 + .../clientset/versioned/scheme/register.go | 54 +++ .../typed/sources/v1alpha1/containersource.go | 174 +++++++++ .../versioned/typed/sources/v1alpha1/doc.go | 20 + .../typed/sources/v1alpha1/gcppubsubsource.go | 174 +++++++++ .../sources/v1alpha1/generated_expansion.go | 25 ++ .../sources/v1alpha1/kuberneteseventsource.go | 174 +++++++++ .../typed/sources/v1alpha1/sources_client.go | 100 +++++ .../pkg/runtime/scheme/scheme.go | 56 +++ 27 files changed, 2190 insertions(+), 175 deletions(-) create mode 100644 vendor/github.com/knative/eventing-sources/AUTHORS create mode 100644 vendor/github.com/knative/eventing-sources/LICENSE create mode 100644 vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/containersource_types.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/doc.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/gcp_pubsub_types.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/gcp_pubsub_validation.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/kuberneteseventsource_types.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/register.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/clientset.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/doc.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/scheme/doc.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/scheme/register.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/containersource.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/doc.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/gcppubsubsource.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/generated_expansion.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/kuberneteseventsource.go create mode 100644 vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/sources_client.go create mode 100644 vendor/sigs.k8s.io/controller-runtime/pkg/runtime/scheme/scheme.go diff --git a/Gopkg.lock b/Gopkg.lock index a5d964dd8d0..32838a57d92 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -299,6 +299,19 @@ pruneopts = "NUT" revision = "5c1d8c8469d1ed34b2aecf4c2305b3a57fff2ee3" +[[projects]] + branch = "master" + digest = "1:e1ae53a5b5dc5fa6f8cb5fa4596a4436d2754921510766569386efc9c07d839c" + name = "github.com/knative/eventing-sources" + packages = [ + "pkg/apis/sources/v1alpha1", + "pkg/client/clientset/versioned", + "pkg/client/clientset/versioned/scheme", + "pkg/client/clientset/versioned/typed/sources/v1alpha1", + ] + pruneopts = "NUT" + revision = "aa113a015beb91c94a8e173c8f0ac18512a83954" + [[projects]] digest = "1:7777b78ab28a8f10918bdcdb043f4150d27da3181fafd69a1b15935197bf9d4c" name = "github.com/knative/pkg" @@ -1054,7 +1067,7 @@ revision = "8a9b82f00b3a86eac24681da3f9fe6c34c01cea2" [[projects]] - digest = "1:b18104b0089c9483846dfcd124fd25a65361697004684b03d4de86546e9808d6" + digest = "1:63139e4089c9b034ec98d8ae54e87c91cd3f5ce5bf9d50dba412c08a363659c4" name = "sigs.k8s.io/controller-runtime" packages = [ "pkg/cache", @@ -1076,6 +1089,7 @@ "pkg/recorder", "pkg/runtime/inject", "pkg/runtime/log", + "pkg/runtime/scheme", "pkg/runtime/signals", "pkg/source", "pkg/source/internal", @@ -1099,6 +1113,8 @@ "github.com/google/go-cmp/cmp/cmpopts", "github.com/google/go-github/github", "github.com/google/uuid", + "github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1", + "github.com/knative/eventing-sources/pkg/client/clientset/versioned", "github.com/knative/pkg/apis", "github.com/knative/pkg/apis/duck", "github.com/knative/pkg/apis/duck/v1alpha1", diff --git a/test/cleanup.go b/test/cleanup.go index 150160866e6..ddaf2b408d7 100644 --- a/test/cleanup.go +++ b/test/cleanup.go @@ -85,6 +85,12 @@ func (c *Cleaner) Add(group string, version string, resource string, namespace s // Clean will delete all registered resources func (c *Cleaner) Clean(awaitDeletion bool) error { for _, deleter := range c.resourcesToClean { + r, err := deleter.Resource.Get(deleter.Name, metav1.GetOptions{}) + if err != nil { + c.logger.Errorf("Failed to get to-be cleaned resource %q : %s", deleter.Name, err) + } else { + c.logger.Infof("Cleaning resource: %q\n%+v", deleter.Name, r) + } if err := deleter.Resource.Delete(deleter.Name, nil); err != nil { c.logger.Errorf("Error: %v", err) } else if awaitDeletion { diff --git a/test/clients.go b/test/clients.go index cf2e447da02..34ab9e01a81 100644 --- a/test/clients.go +++ b/test/clients.go @@ -18,6 +18,7 @@ limitations under the License. package test import ( + sources "github.com/knative/eventing-sources/pkg/client/clientset/versioned" eventing "github.com/knative/eventing/pkg/client/clientset/versioned" "github.com/knative/pkg/test" serving "github.com/knative/serving/pkg/client/clientset/versioned" @@ -32,6 +33,7 @@ type Clients struct { Serving *serving.Clientset Eventing *eventing.Clientset Dynamic dynamic.Interface + Sources *sources.Clientset } // NewClients instantiates and returns several clientsets required for making request to the @@ -63,6 +65,11 @@ func NewClients(configPath string, clusterName string, namespace string) (*Clien return nil, err } + clients.Sources, err = sources.NewForConfig(cfg) + if err != nil { + return nil, err + } + return clients, nil } diff --git a/test/crd.go b/test/crd.go index ca93978d4c8..a0e4a22a033 100644 --- a/test/crd.go +++ b/test/crd.go @@ -18,28 +18,24 @@ package test // crd contains functions that construct boilerplate CRD definitions. import ( - "fmt" - - channelsV1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - feedsV1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - flowsV1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - "github.com/knative/serving/pkg/apis/serving/v1alpha1" + sourcesv1alpha1 "github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1" + "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + servingv1alpha1 "github.com/knative/serving/pkg/apis/serving/v1alpha1" corev1 "k8s.io/api/core/v1" - rbacV1beta1 "k8s.io/api/rbac/v1beta1" + rbacv1beta1 "k8s.io/api/rbac/v1beta1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" ) // Route returns a Route object in namespace -func Route(name string, namespace string, configName string) *v1alpha1.Route { - return &v1alpha1.Route{ +func Route(name string, namespace string, configName string) *servingv1alpha1.Route { + return &servingv1alpha1.Route{ ObjectMeta: metav1.ObjectMeta{ Namespace: namespace, Name: name, }, - Spec: v1alpha1.RouteSpec{ - Traffic: []v1alpha1.TrafficTarget{ - v1alpha1.TrafficTarget{ + Spec: servingv1alpha1.RouteSpec{ + Traffic: []servingv1alpha1.TrafficTarget{ + servingv1alpha1.TrafficTarget{ ConfigurationName: configName, Percent: 100, }, @@ -50,18 +46,18 @@ func Route(name string, namespace string, configName string) *v1alpha1.Route { // Configuration returns a Configuration object in namespace with the name names.Config // that uses the image specifed by imagePath. -func Configuration(name string, namespace string, imagePath string) *v1alpha1.Configuration { - return &v1alpha1.Configuration{ +func Configuration(name string, namespace string, imagePath string) *servingv1alpha1.Configuration { + return &servingv1alpha1.Configuration{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Spec: v1alpha1.ConfigurationSpec{ - RevisionTemplate: v1alpha1.RevisionTemplateSpec{ + Spec: servingv1alpha1.ConfigurationSpec{ + RevisionTemplate: servingv1alpha1.RevisionTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{"knative.dev/type": "container"}, }, - Spec: v1alpha1.RevisionSpec{ + Spec: servingv1alpha1.RevisionSpec{ Container: corev1.Container{ Image: imagePath, }, @@ -81,20 +77,20 @@ func ServiceAccount(name string, namespace string) *corev1.ServiceAccount { } } -// ClusterRoleBinding create ClusterRoleBinding for given subject and role -func ClusterRoleBinding(name string, namespace string, serviceAccount string, role string) *rbacV1beta1.ClusterRoleBinding { - return &rbacV1beta1.ClusterRoleBinding{ +// ClusterRoleBinding returns ClusterRoleBinding for given subject and role +func ClusterRoleBinding(name string, namespace string, serviceAccount string, role string) *rbacv1beta1.ClusterRoleBinding { + return &rbacv1beta1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ Name: name, }, - Subjects: []rbacV1beta1.Subject{ + Subjects: []rbacv1beta1.Subject{ { Kind: "ServiceAccount", Name: serviceAccount, Namespace: namespace, }, }, - RoleRef: rbacV1beta1.RoleRef{ + RoleRef: rbacv1beta1.RoleRef{ Kind: "ClusterRole", Name: role, APIGroup: "rbac.authorization.k8s.io", @@ -102,82 +98,74 @@ func ClusterRoleBinding(name string, namespace string, serviceAccount string, ro } } -// ClusterBus returns ClusterBus object with given name and imagePath -func ClusterBus(name string, namespace string, imagePath string) *channelsV1alpha1.ClusterBus { - return &channelsV1alpha1.ClusterBus{ +// ClusterChannelProvisioner returns a ClusterChannelProvisioner for a given name +func ClusterChannelProvisioner(name string) *corev1.ObjectReference { + return &corev1.ObjectReference{ + Kind: "ClusterChannelProvisioner", + APIVersion: "eventing.knative.dev/v1alpha1", + Name: name, + } +} + +// ChannelRef returns an ObjectReference for a given Channel Name +func ChannelRef(name string) *corev1.ObjectReference { + return &corev1.ObjectReference{ + Kind: "Channel", + APIVersion: "eventing.knative.dev/v1alpha1", + Name: name, + } +} + +// Channel returns a Channel with the specified provisioner +func Channel(name string, namespace string, provisioner *corev1.ObjectReference) *v1alpha1.Channel { + return &v1alpha1.Channel{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, - Spec: channelsV1alpha1.ClusterBusSpec{ - Dispatcher: corev1.Container{ - Name: "dispatcher", - Image: imagePath, - Args: []string{"-logtostderr", "-stderrthreshold", "INFO"}, - }, + Spec: v1alpha1.ChannelSpec{ + Provisioner: provisioner, }, } } -// EventSource returns EventSource object using the given image paths -func EventSource(eventSource string, namespace string, eventSourceImagePath string, receiverAdapterImagePath string) *feedsV1alpha1.EventSource { - return &feedsV1alpha1.EventSource{ +// KubernetesEventSource returns a KubernetesEventSource sinking to specified channel +func KubernetesEventSource(name string, namespace string, targetNamespace string, serviceAccount string, channel *corev1.ObjectReference) *sourcesv1alpha1.KubernetesEventSource { + return &sourcesv1alpha1.KubernetesEventSource{ ObjectMeta: metav1.ObjectMeta{ - Name: eventSource, + Name: name, Namespace: namespace, }, - Spec: feedsV1alpha1.EventSourceSpec{ - CommonEventSourceSpec: feedsV1alpha1.CommonEventSourceSpec{ - Source: eventSource, - Image: eventSourceImagePath, - Parameters: &runtime.RawExtension{ - Raw: []byte(fmt.Sprintf(`{"image": "%s"}`, receiverAdapterImagePath)), - }, - }, + Spec: sourcesv1alpha1.KubernetesEventSourceSpec{ + Namespace: targetNamespace, + ServiceAccountName: serviceAccount, + Sink: channel, }, } } -// EventType returns an EventType object referencing given eventSource -func EventType(name string, namespace string, eventSource string) *feedsV1alpha1.EventType { - return &feedsV1alpha1.EventType{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - }, - Spec: feedsV1alpha1.EventTypeSpec{ - CommonEventTypeSpec: feedsV1alpha1.CommonEventTypeSpec{ - Description: "subscription for receiving k8s cluster events", - }, - EventSource: eventSource, +// SubscriberSpecForRoute returns a SubscriberSpec for a given Knative Service. +func SubscriberSpecForRoute(name string) *v1alpha1.SubscriberSpec { + return &v1alpha1.SubscriberSpec{ + Ref: &corev1.ObjectReference{ + Kind: "Route", + APIVersion: "serving.knative.dev/v1alpha1", + Name: name, }, } } -// Flow will return Flow object with given parameters -func Flow(flowName string, namespace string, serviceAccount string, eventType string, eventSource string, routeName string, testNamespace string) *flowsV1alpha1.Flow { - return &flowsV1alpha1.Flow{ +// Subscription returns a Subscription +func Subscription(name string, namespace string, channel *corev1.ObjectReference, subscriber *v1alpha1.SubscriberSpec, reply *v1alpha1.ReplyStrategy) *v1alpha1.Subscription { + return &v1alpha1.Subscription{ ObjectMeta: metav1.ObjectMeta{ - Name: flowName, + Name: name, Namespace: namespace, }, - Spec: flowsV1alpha1.FlowSpec{ - ServiceAccountName: serviceAccount, - Trigger: flowsV1alpha1.EventTrigger{ - EventType: eventType, - Resource: "k8sevents/receiveevent", - Service: eventSource, - Parameters: &runtime.RawExtension{ - Raw: []byte(fmt.Sprintf(`{"namespace": "%s"}`, testNamespace)), - }, - }, - Action: flowsV1alpha1.FlowAction{ - Target: &corev1.ObjectReference{ - Kind: "Route", - APIVersion: "serving.knative.dev/v1alpha1", - Name: routeName, - }, - }, + Spec: v1alpha1.SubscriptionSpec{ + Channel: *channel, + Subscriber: subscriber, + Reply: reply, }, } } diff --git a/test/e2e-tests.sh b/test/e2e-tests.sh index 4aa91c03de3..eaa5f108cd3 100755 --- a/test/e2e-tests.sh +++ b/test/e2e-tests.sh @@ -27,6 +27,8 @@ source $(dirname $0)/../vendor/github.com/knative/test-infra/scripts/e2e-tests.sh +readonly KNATIVE_EVENTING_SOURCES_RELEASE=https://knative-releases.storage.googleapis.com/eventing-sources/latest/release.yaml + # Names of the Resources used in the tests. readonly E2E_TEST_NAMESPACE=e2etest readonly E2E_TEST_FUNCTION_NAMESPACE=e2etestfn3 @@ -41,12 +43,26 @@ function run_e2e_tests() { # Helper functions. +# Install the latest stable Knative/serving in the current cluster. +function start_latest_eventing_sources() { + header "Starting Knative Eventing Sources" + subheader "Installing Knative Eventing Sources" + kubectl apply -f ${KNATIVE_EVENTING_SOURCES_RELEASE} || return 1 + wait_until_pods_running knative-sources || return 1 +} + + function teardown() { teardown_events_test_resources +# ko delete --ignore-not-found=true -f config/provisioners/in-memory-channel/in-memory-channel.yaml ko delete --ignore-not-found=true -f config/ + ko delete --ignore-not-found=true -f ${KNATIVE_EVENTING_SOURCES_RELEASE} + wait_until_object_does_not_exist namespaces knative-eventing - wait_until_object_does_not_exist customresourcedefinitions feeds.feeds.knative.dev - wait_until_object_does_not_exist customresourcedefinitions flows.flows.knative.dev + wait_until_object_does_not_exist namespaces knative-sources + + wait_until_object_does_not_exist customresourcedefinitions subscriptions.eventing.knative.dev + wait_until_object_does_not_exist customresourcedefinitions channels.eventing.knative.dev } function setup_events_test_resources() { @@ -92,6 +108,9 @@ if (( ! USING_EXISTING_CLUSTER )); then start_latest_knative_serving || fail_test fi +# Install Knative Eventing Sources +start_latest_eventing_sources || fail_test + # Clean up anything that might still be around teardown_events_test_resources @@ -105,6 +124,11 @@ ko resolve -f config/ ko apply -f config/ wait_until_pods_running knative-eventing +header "Standing up In-Memory ClusterChannelProvisioner" +ko resolve -f config/provisioners/in-memory-channel/in-memory-channel.yaml +ko apply -f config/provisioners/in-memory-channel/in-memory-channel.yaml +wait_until_pods_running knative-eventing + # Publish test images publish_test_images diff --git a/test/e2e/e2e.go b/test/e2e/e2e.go index 3760e410cb7..dc14139e4cf 100644 --- a/test/e2e/e2e.go +++ b/test/e2e/e2e.go @@ -6,9 +6,8 @@ import ( "testing" "time" - channelsV1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - feedsV1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - flowsV1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" + sourcesv1alpha1 "github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1" + "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/test" pkgTest "github.com/knative/pkg/test" "github.com/knative/pkg/test/logging" @@ -97,49 +96,36 @@ func WithRouteReady(clients *test.Clients, logger *logging.BaseLogger, cleaner * return nil } -// CreateFlow will create a Flow -func CreateFlow(clients *test.Clients, flow *flowsV1alpha1.Flow, logger *logging.BaseLogger, cleaner *test.Cleaner) error { - flows := clients.Eventing.FlowsV1alpha1().Flows(pkgTest.Flags.Namespace) - res, err := flows.Create(flow) +// CreateKubernetesEventSource creates a KubernetesEventSource +func CreateKubernetesEventSource(clients *test.Clients, source *sourcesv1alpha1.KubernetesEventSource, logger *logging.BaseLogger, cleaner *test.Cleaner) error { + k8sSources := clients.Sources.SourcesV1alpha1().KubernetesEventSources(pkgTest.Flags.Namespace) + res, err := k8sSources.Create(source) if err != nil { return err } - cleaner.Add(flowsV1alpha1.SchemeGroupVersion.Group, flowsV1alpha1.SchemeGroupVersion.Version, "flows", pkgTest.Flags.Namespace, res.ObjectMeta.Name) - return nil -} - -// WithFlowReady will create a Flow and wait until it is ready -func WithFlowReady(clients *test.Clients, flow *flowsV1alpha1.Flow, logger *logging.BaseLogger, cleaner *test.Cleaner) error { - err := CreateFlow(clients, flow, logger, cleaner) - if err != nil { - return err - } - flows := clients.Eventing.FlowsV1alpha1().Flows(pkgTest.Flags.Namespace) - if err := test.WaitForFlowState(flows, flow.ObjectMeta.Name, test.IsFlowReady, "FlowIsReady"); err != nil { - return err - } + cleaner.Add(sourcesv1alpha1.SchemeGroupVersion.Group, sourcesv1alpha1.SchemeGroupVersion.Version, "kuberneteseventsources", pkgTest.Flags.Namespace, res.ObjectMeta.Name) return nil } // CreateChannel will create a Channel -func CreateChannel(clients *test.Clients, channel *channelsV1alpha1.Channel, logger *logging.BaseLogger, cleaner *test.Cleaner) error { - channels := clients.Eventing.ChannelsV1alpha1().Channels(pkgTest.Flags.Namespace) +func CreateChannel(clients *test.Clients, channel *v1alpha1.Channel, logger *logging.BaseLogger, cleaner *test.Cleaner) error { + channels := clients.Eventing.EventingV1alpha1().Channels(pkgTest.Flags.Namespace) res, err := channels.Create(channel) if err != nil { return err } - cleaner.Add(channelsV1alpha1.SchemeGroupVersion.Group, channelsV1alpha1.SchemeGroupVersion.Version, "channels", pkgTest.Flags.Namespace, res.ObjectMeta.Name) + cleaner.Add(v1alpha1.SchemeGroupVersion.Group, v1alpha1.SchemeGroupVersion.Version, "channels", pkgTest.Flags.Namespace, res.ObjectMeta.Name) return nil } // CreateSubscription will create a Subscription -func CreateSubscription(clients *test.Clients, subs *channelsV1alpha1.Subscription, logger *logging.BaseLogger, cleaner *test.Cleaner) error { - subscriptions := clients.Eventing.ChannelsV1alpha1().Subscriptions(pkgTest.Flags.Namespace) +func CreateSubscription(clients *test.Clients, subs *v1alpha1.Subscription, logger *logging.BaseLogger, cleaner *test.Cleaner) error { + subscriptions := clients.Eventing.EventingV1alpha1().Subscriptions(pkgTest.Flags.Namespace) res, err := subscriptions.Create(subs) if err != nil { return err } - cleaner.Add(channelsV1alpha1.SchemeGroupVersion.Group, channelsV1alpha1.SchemeGroupVersion.Version, "subscriptions", pkgTest.Flags.Namespace, res.ObjectMeta.Name) + cleaner.Add(v1alpha1.SchemeGroupVersion.Group, v1alpha1.SchemeGroupVersion.Version, "subscriptions", pkgTest.Flags.Namespace, res.ObjectMeta.Name) return nil } @@ -180,7 +166,7 @@ func CreateServiceAccountAndBinding(clients *test.Clients, name string, logger * } crb := &rbacV1beta1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ - Name: "e2e-feed-admin", + Name: "e2e-tests-admin", }, Subjects: []rbacV1beta1.Subject{ { @@ -202,39 +188,6 @@ func CreateServiceAccountAndBinding(clients *test.Clients, name string, logger * return nil } -// CreateClusterBus will create a ClusterBus -func CreateClusterBus(clients *test.Clients, cbus *channelsV1alpha1.ClusterBus, logger *logging.BaseLogger, cleaner *test.Cleaner) error { - cbuses := clients.Eventing.ChannelsV1alpha1().ClusterBuses() - res, err := cbuses.Create(cbus) - if err != nil { - return err - } - cleaner.Add(channelsV1alpha1.SchemeGroupVersion.Group, channelsV1alpha1.SchemeGroupVersion.Version, "clusterbuses", pkgTest.Flags.Namespace, res.ObjectMeta.Name) - return nil -} - -// CreateEventSource will create an EventSource -func CreateEventSource(clients *test.Clients, es *feedsV1alpha1.EventSource, logger *logging.BaseLogger, cleaner *test.Cleaner) error { - esources := clients.Eventing.FeedsV1alpha1().EventSources(pkgTest.Flags.Namespace) - res, err := esources.Create(es) - if err != nil { - return err - } - cleaner.Add(feedsV1alpha1.SchemeGroupVersion.Group, feedsV1alpha1.SchemeGroupVersion.Version, "eventsources", pkgTest.Flags.Namespace, res.ObjectMeta.Name) - return nil -} - -// CreateEventType will create an EventType -func CreateEventType(clients *test.Clients, et *feedsV1alpha1.EventType, logger *logging.BaseLogger, cleaner *test.Cleaner) error { - eTypes := clients.Eventing.FeedsV1alpha1().EventTypes(pkgTest.Flags.Namespace) - res, err := eTypes.Create(et) - if err != nil { - return err - } - cleaner.Add(feedsV1alpha1.SchemeGroupVersion.Group, feedsV1alpha1.SchemeGroupVersion.Version, "eventtypes", pkgTest.Flags.Namespace, res.ObjectMeta.Name) - return nil -} - // CreatePod will create a Pod func CreatePod(clients *test.Clients, pod *corev1.Pod, logger *logging.BaseLogger, cleaner *test.Cleaner) error { pods := clients.Kube.Kube.CoreV1().Pods(pod.GetNamespace()) diff --git a/test/e2e/k8s_events_test.go b/test/e2e/k8s_events_test.go index cbe3a3ce39f..5cec98c8413 100644 --- a/test/e2e/k8s_events_test.go +++ b/test/e2e/k8s_events_test.go @@ -27,11 +27,12 @@ import ( ) const ( - serviceAccount = "e2e-feed" - eventSource = "k8sevents" - eventType = "dev.knative.k8s.event" - routeName = "e2e-k8s-events-function" - flowName = "e2e-k8s-events-example" + serviceAccount = "e2e-receive-adapter" + eventSource = "k8sevents" + routeName = "e2e-k8s-events-function" + channelName = "e2e-k8s-events-channel" + provisionerName = "in-memory-channel" + subscriptionName = "e2e-k8s-events-subscription" ) func TestKubernetesEvents(t *testing.T) { @@ -50,38 +51,21 @@ func TestKubernetesEvents(t *testing.T) { t.Fatalf("Failed to create ServiceAccount or Binding: %v", err) } - logger.Infof("Creating ClusterBus") - - // The dispatcher used by the ClusterBus to dispatch messages between channels and subscriptions - dispatcherImagePath := ImagePath("github.com/knative/eventing/pkg/buses/stub/dispatcher") - stub := test.ClusterBus("stub", defaultNamespaceName, dispatcherImagePath) - err = CreateClusterBus(clients, stub, logger, cleaner) + logger.Infof("Creating Channel") + channel := test.Channel(channelName, defaultNamespaceName, test.ClusterChannelProvisioner(provisionerName)) + err = CreateChannel(clients, channel, logger, cleaner) if err != nil { - t.Fatalf("Failed to create ClusterBus: %v", err) + t.Fatalf("Failed to create Channel: %v", err) } logger.Infof("Creating EventSource") - - // The event source wrapper for Kubernetes events - eventSourceImagePath := ImagePath("github.com/knative/eventing/pkg/sources/k8sevents") - // The actual event source which receives events and posts them to the given Route - receiverAdapterImagePath := ImagePath("github.com/knative/eventing/pkg/sources/k8sevents/receive_adapter") - eSource := test.EventSource(eventSource, defaultNamespaceName, eventSourceImagePath, receiverAdapterImagePath) - err = CreateEventSource(clients, eSource, logger, cleaner) - if err != nil { - t.Fatalf("Failed to create EventSource: %v", err) - } - - logger.Infof("Creating EventType") - - eType := test.EventType(eventType, defaultNamespaceName, eventSource) - err = CreateEventType(clients, eType, logger, cleaner) + k8sSource := test.KubernetesEventSource(eventSource, defaultNamespaceName, testNamespace, serviceAccount, test.ChannelRef(channelName)) + err = CreateKubernetesEventSource(clients, k8sSource, logger, cleaner) if err != nil { - t.Fatalf("Failed to create EventType: %v", err) + t.Fatalf("Failed to create KubernetesEventSource: %v", err) } logger.Infof("Creating Route and Config") - // The receiver of events which is accessible through Route configImagePath := ImagePath("github.com/knative/eventing/test/e2e/k8sevents") err = WithRouteReady(clients, logger, cleaner, routeName, configImagePath) @@ -89,12 +73,11 @@ func TestKubernetesEvents(t *testing.T) { t.Fatalf("The Route was not marked as Ready to serve traffic: %v", err) } - logger.Infof("Creating Flow") - - flow := test.Flow(flowName, defaultNamespaceName, serviceAccount, eventType, eventSource, routeName, testNamespace) - err = WithFlowReady(clients, flow, logger, cleaner) + logger.Infof("Creating Subscription") + subscription := test.Subscription(subscriptionName, defaultNamespaceName, test.ChannelRef(channelName), test.SubscriberSpecForRoute(routeName), nil) + err = CreateSubscription(clients, subscription, logger, cleaner) if err != nil { - t.Fatalf("Failed to create Flow: %v", err) + t.Fatalf("Failed to create Subscription: %v", err) } //Work around for: https://github.com/knative/eventing/issues/125 diff --git a/vendor/github.com/knative/eventing-sources/AUTHORS b/vendor/github.com/knative/eventing-sources/AUTHORS new file mode 100644 index 00000000000..9a8f2f769f4 --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/AUTHORS @@ -0,0 +1,6 @@ +# This is the list of Knative authors for copyright purposes. +# +# This does not necessarily list everyone who has contributed code, since in +# some cases, their employer may be the copyright holder. To see the full list +# of contributors, see the revision history in source control. +Google LLC diff --git a/vendor/github.com/knative/eventing-sources/LICENSE b/vendor/github.com/knative/eventing-sources/LICENSE new file mode 100644 index 00000000000..261eeb9e9f8 --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/containersource_types.go b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/containersource_types.go new file mode 100644 index 00000000000..a04e21e73fc --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/containersource_types.go @@ -0,0 +1,160 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "github.com/knative/pkg/apis/duck" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// Important: Run "make" to regenerate code after modifying this file +// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. + +// Check that ContainerSource can be validated and can be defaulted. +var _ runtime.Object = (*ContainerSource)(nil) + +// Check that ContainerSource implements the Conditions duck type. +var _ = duck.VerifyType(&ContainerSource{}, &duckv1alpha1.Conditions{}) + +// ContainerSourceSpec defines the desired state of ContainerSource +type ContainerSourceSpec struct { + // Image is the image to run inside of the container. + // +kubebuilder:validation:MinLength=1 + Image string `json:"image,omitempty"` + + // Args are passed to the ContainerSpec as they are. + Args []string `json:"args,omitempty"` + + // Env is the list of environment variables to set in the container. + // Cannot be updated. + // +optional + // +patchMergeKey=name + // +patchStrategy=merge + Env []corev1.EnvVar `json:"env,omitempty" patchStrategy:"merge" patchMergeKey:"name"` + + // ServiceAccountName is the name of the ServiceAccount to use to run this + // source. + // +optional + ServiceAccountName string `json:"serviceAccountName,omitempty"` + + // Sink is a reference to an object that will resolve to a domain name to use as the sink. + // +optional + Sink *corev1.ObjectReference `json:"sink,omitempty"` +} + +const ( + // ContainerSourceConditionReady has status True when the ContainerSource is ready to send events. + ContainerConditionReady = duckv1alpha1.ConditionReady + + // ContainerConditionSinkProvided has status True when the ContainerSource has been configured with a sink target. + ContainerConditionSinkProvided duckv1alpha1.ConditionType = "SinkProvided" + + // ContainerConditionDeployed has status True when the ContainerSource has had it's deployment created. + ContainerConditionDeployed duckv1alpha1.ConditionType = "Deployed" +) + +var containerCondSet = duckv1alpha1.NewLivingConditionSet( + ContainerConditionSinkProvided, + ContainerConditionDeployed) + +// ContainerSourceStatus defines the observed state of ContainerSource +type ContainerSourceStatus struct { + // Conditions holds the state of a source at a point in time. + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + Conditions duckv1alpha1.Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // SinkURI is the current active sink URI that has been configured for the ContainerSource. + // +optional + SinkURI string `json:"sinkUri,omitempty"` +} + +// GetCondition returns the condition currently associated with the given type, or nil. +func (s *ContainerSourceStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { + return containerCondSet.Manage(s).GetCondition(t) +} + +// IsReady returns true if the resource is ready overall. +func (s *ContainerSourceStatus) IsReady() bool { + return containerCondSet.Manage(s).IsHappy() +} + +// InitializeConditions sets relevant unset conditions to Unknown state. +func (s *ContainerSourceStatus) InitializeConditions() { + containerCondSet.Manage(s).InitializeConditions() +} + +// MarSink sets the condition that the source has a sink configured. +func (s *ContainerSourceStatus) MarkSink(uri string) { + s.SinkURI = uri + if len(uri) > 0 { + containerCondSet.Manage(s).MarkTrue(ContainerConditionSinkProvided) + } else { + containerCondSet.Manage(s).MarkUnknown(ContainerConditionSinkProvided, "SinkEmpty", "Sink has resolved to empty.%s", "") + } +} + +// MarkNoSink sets the condition that the source does not have a sink configured. +func (s *ContainerSourceStatus) MarkNoSink(reason, messageFormat string, messageA ...interface{}) { + containerCondSet.Manage(s).MarkFalse(ContainerConditionSinkProvided, reason, messageFormat, messageA...) +} + +// MarkDeployed sets the condition that the source has been deployed. +func (s *ContainerSourceStatus) MarkDeployed() { + containerCondSet.Manage(s).MarkTrue(ContainerConditionDeployed) +} + +// MarkDeploying sets the condition that the source is deploying. +func (s *ContainerSourceStatus) MarkDeploying(reason, messageFormat string, messageA ...interface{}) { + containerCondSet.Manage(s).MarkUnknown(ContainerConditionDeployed, reason, messageFormat, messageA...) +} + +// MarkNotDeployed sets the condition that the source has not been deployed. +func (s *ContainerSourceStatus) MarkNotDeployed(reason, messageFormat string, messageA ...interface{}) { + containerCondSet.Manage(s).MarkFalse(ContainerConditionDeployed, reason, messageFormat, messageA...) +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ContainerSource is the Schema for the containersources API +// +k8s:openapi-gen=true +// +kubebuilder:categories=all,knative,eventing,sources +type ContainerSource struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ContainerSourceSpec `json:"spec,omitempty"` + Status ContainerSourceStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ContainerSourceList contains a list of ContainerSource +type ContainerSourceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ContainerSource `json:"items"` +} + +func init() { + SchemeBuilder.Register(&ContainerSource{}, &ContainerSourceList{}) +} diff --git a/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/doc.go b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/doc.go new file mode 100644 index 00000000000..258a98c9176 --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1alpha1 contains API Schema definitions for the sources v1alpha1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=github.com/knative/eventing-sources/pkg/apis/sources +// +k8s:defaulter-gen=TypeMeta +// +groupName=sources.eventing.knative.dev +package v1alpha1 diff --git a/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/gcp_pubsub_types.go b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/gcp_pubsub_types.go new file mode 100644 index 00000000000..1a641bd961b --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/gcp_pubsub_types.go @@ -0,0 +1,168 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "github.com/knative/pkg/apis" + "github.com/knative/pkg/apis/duck" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// GcpPubSubSource is the Schema for the gcppubsubsources API. +// +k8s:openapi-gen=true +// +kubebuilder:categories=all,knative,eventing,sources +type GcpPubSubSource struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec GcpPubSubSourceSpec `json:"spec,omitempty"` + Status GcpPubSubSourceStatus `json:"status,omitempty"` +} + +// Check that GcpPubSubSource can be validated and can be defaulted. +var _ runtime.Object = (*GcpPubSubSource)(nil) + +// Check that GcpPubSubSource will be checked for immutable fields. +var _ apis.Immutable = (*GcpPubSubSource)(nil) + +// Check that GcpPubSubSource implements the Conditions duck type. +var _ = duck.VerifyType(&GcpPubSubSource{}, &duckv1alpha1.Conditions{}) + +// GcpPubSubSourceSpec defines the desired state of the GcpPubSubSource. +type GcpPubSubSourceSpec struct { + // GcpCredsSecret is the credential to use to poll the GCP PubSub Subscription. It is not used + // to create or delete the Subscription, only to poll it. The value of the secret entry must be + // a service account key in the JSON format (see + // https://cloud.google.com/iam/docs/creating-managing-service-account-keys). + GcpCredsSecret corev1.SecretKeySelector `json:"gcpCredsSecret,omitempty"` + + // GoogleCloudProject is the ID of the Google Cloud Project that the PubSub Topic exists in. + GoogleCloudProject string `json:"googleCloudProject,omitempty"` + + // Topic is the ID of the GCP PubSub Topic to Subscribe to. It must be in the form of the + // unique identifier within the project, not the entire name. E.g. it must be 'laconia', not + // 'projects/my-gcp-project/topics/laconia'. + Topic string `json:"topic,omitempty"` + + // Sink is a reference to an object that will resolve to a domain name to use as the sink. + // +optional + Sink *corev1.ObjectReference `json:"sink,omitempty"` + + // ServiceAccoutName is the name of the ServiceAccount that will be used to run the Receive + // Adapter Deployment. + ServiceAccountName string `json:"serviceAccountName,omitempty"` +} + +const ( + // GcpPubSubConditionReady has status True when the GcpPubSubSource is ready to send events. + GcpPubSubConditionReady = duckv1alpha1.ConditionReady + + // GcpPubSubConditionSinkProvided has status True when the GcpPubSubSource has been configured with a sink target. + GcpPubSubConditionSinkProvided duckv1alpha1.ConditionType = "SinkProvided" + + // GcpPubSubConditionDeployed has status True when the GcpPubSubSource has had it's receive adapter deployment created. + GcpPubSubConditionDeployed duckv1alpha1.ConditionType = "Deployed" + + // GcpPubSubConditionSubscribed has status True when a GCP PubSub Subscription has been created pointing at the created receive adapter deployment. + GcpPubSubConditionSubscribed duckv1alpha1.ConditionType = "Subscribed" +) + +var gcpPubSubSourceCondSet = duckv1alpha1.NewLivingConditionSet( + GcpPubSubConditionSinkProvided, + GcpPubSubConditionDeployed, + GcpPubSubConditionSubscribed) + +// GcpPubSubSourceStatus defines the observed state of GcpPubSubSource. +type GcpPubSubSourceStatus struct { + // Conditions holds the state of a source at a point in time. + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + Conditions duckv1alpha1.Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // SinkURI is the current active sink URI that has been configured for the GcpPubSubSource. + // +optional + SinkURI string `json:"sinkUri,omitempty"` +} + +// GetCondition returns the condition currently associated with the given type, or nil. +func (s *GcpPubSubSourceStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { + return gcpPubSubSourceCondSet.Manage(s).GetCondition(t) +} + +// IsReady returns true if the resource is ready overall. +func (s *GcpPubSubSourceStatus) IsReady() bool { + return gcpPubSubSourceCondSet.Manage(s).IsHappy() +} + +// InitializeConditions sets relevant unset conditions to Unknown state. +func (s *GcpPubSubSourceStatus) InitializeConditions() { + gcpPubSubSourceCondSet.Manage(s).InitializeConditions() +} + +// MarkSink sets the condition that the source has a sink configured. +func (s *GcpPubSubSourceStatus) MarkSink(uri string) { + s.SinkURI = uri + if len(uri) > 0 { + gcpPubSubSourceCondSet.Manage(s).MarkTrue(GcpPubSubConditionSinkProvided) + } else { + gcpPubSubSourceCondSet.Manage(s).MarkUnknown(GcpPubSubConditionSinkProvided, "SinkEmpty", "Sink has resolved to empty.%s", "") + } +} + +// MarkNoSink sets the condition that the source does not have a sink configured. +func (s *GcpPubSubSourceStatus) MarkNoSink(reason, messageFormat string, messageA ...interface{}) { + gcpPubSubSourceCondSet.Manage(s).MarkFalse(GcpPubSubConditionSinkProvided, reason, messageFormat, messageA...) +} + +// MarkDeployed sets the condition that the source has been deployed. +func (s *GcpPubSubSourceStatus) MarkDeployed() { + gcpPubSubSourceCondSet.Manage(s).MarkTrue(GcpPubSubConditionDeployed) +} + +// MarkDeploying sets the condition that the source is deploying. +func (s *GcpPubSubSourceStatus) MarkDeploying(reason, messageFormat string, messageA ...interface{}) { + gcpPubSubSourceCondSet.Manage(s).MarkUnknown(GcpPubSubConditionDeployed, reason, messageFormat, messageA...) +} + +// MarkNotDeployed sets the condition that the source has not been deployed. +func (s *GcpPubSubSourceStatus) MarkNotDeployed(reason, messageFormat string, messageA ...interface{}) { + gcpPubSubSourceCondSet.Manage(s).MarkFalse(GcpPubSubConditionDeployed, reason, messageFormat, messageA...) +} + +func (s *GcpPubSubSourceStatus) MarkSubscribed() { + gcpPubSubSourceCondSet.Manage(s).MarkTrue(GcpPubSubConditionSubscribed) +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// GcpPubSubSourceList contains a list of GcpPubSubSources. +type GcpPubSubSourceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []GcpPubSubSource `json:"items"` +} + +func init() { + SchemeBuilder.Register(&GcpPubSubSource{}, &GcpPubSubSourceList{}) +} diff --git a/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/gcp_pubsub_validation.go b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/gcp_pubsub_validation.go new file mode 100644 index 00000000000..fc563c8bc82 --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/gcp_pubsub_validation.go @@ -0,0 +1,44 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "github.com/google/go-cmp/cmp" + "github.com/knative/pkg/apis" +) + +func (current *GcpPubSubSource) CheckImmutableFields(og apis.Immutable) *apis.FieldError { + original, ok := og.(*GcpPubSubSource) + if !ok { + return &apis.FieldError{Message: "The provided original was not a GcpPubSubSource"} + } + if original == nil { + return nil + } + + // All of the fields are immutable because the controller doesn't understand when it would need + // to delete and create a new Receive Adapter with updated arguments. We could relax it slightly + // to allow a nil Sink -> non-nil Sink, but I don't think it is needed yet. + if diff := cmp.Diff(original.Spec, current.Spec); diff != "" { + return &apis.FieldError{ + Message: "Immutable fields changed (-old +new)", + Paths: []string{"spec"}, + Details: diff, + } + } + return nil +} diff --git a/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/kuberneteseventsource_types.go b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/kuberneteseventsource_types.go new file mode 100644 index 00000000000..f4281723397 --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/kuberneteseventsource_types.go @@ -0,0 +1,121 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "github.com/knative/pkg/apis/duck" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +// Check that KubernetesEventSource can be validated and can be defaulted. +var _ runtime.Object = (*KubernetesEventSource)(nil) + +// Check that KubernetesEventSource implements the Conditions duck type. +var _ = duck.VerifyType(&KubernetesEventSource{}, &duckv1alpha1.Conditions{}) + +// KubernetesEventSourceSpec defines the desired state of the source. +type KubernetesEventSourceSpec struct { + // Namespace that we watch kubernetes events in. + Namespace string `json:"namespace"` + + // ServiceAccountName is the name of the ServiceAccount to use to run this + // source. + // +optional + ServiceAccountName string `json:"serviceAccountName,omitempty"` + + // Sink is a reference to an object that will resolve to a domain name to use + // as the sink. + // +optional + Sink *corev1.ObjectReference `json:"sink,omitempty"` +} + +const ( + // KubernetesEventSourceConditionReady has status True when the + // source is ready to send events. + KubernetesEventSourceConditionReady = duckv1alpha1.ConditionReady +) + +var kubernetesEventSourceCondSet = duckv1alpha1.NewLivingConditionSet() + +// KubernetesEventSourceStatus defines the observed state of the source. +type KubernetesEventSourceStatus struct { + // Conditions holds the state of a source at a point in time. + // +optional + // +patchMergeKey=type + // +patchStrategy=merge + Conditions duckv1alpha1.Conditions `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` + + // SinkURI is the current active sink URI that has been configured for the source. + // +optional + SinkURI string `json:"sinkUri,omitempty"` +} + +// GetCondition returns the condition currently associated with the given type, or nil. +func (s *KubernetesEventSourceStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { + return kubernetesEventSourceCondSet.Manage(s).GetCondition(t) +} + +// IsReady returns true if the resource is ready overall. +func (s *KubernetesEventSourceStatus) IsReady() bool { + return kubernetesEventSourceCondSet.Manage(s).IsHappy() +} + +// InitializeConditions sets relevant unset conditions to Unknown state. +func (s *KubernetesEventSourceStatus) InitializeConditions() { + kubernetesEventSourceCondSet.Manage(s).InitializeConditions() +} + +// MarkReady sets the condition that the ContainerSource owned by +// the source has Ready status True. +func (s *KubernetesEventSourceStatus) MarkReady() { + kubernetesEventSourceCondSet.Manage(s).MarkTrue(KubernetesEventSourceConditionReady) +} + +// MarkUnready sets the condition that the ContainerSource owned by +// the source does not have Ready status True. +func (s *KubernetesEventSourceStatus) MarkUnready(reason, messageFormat string, messageA ...interface{}) { + kubernetesEventSourceCondSet.Manage(s).MarkFalse(KubernetesEventSourceConditionReady, reason, messageFormat, messageA...) +} + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// KubernetesEventSource is the Schema for the kuberneteseventsources API +// +k8s:openapi-gen=true +type KubernetesEventSource struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec KubernetesEventSourceSpec `json:"spec,omitempty"` + Status KubernetesEventSourceStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// KubernetesEventSourceList contains a list of KubernetesEventSource +type KubernetesEventSourceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []KubernetesEventSource `json:"items"` +} + +func init() { + SchemeBuilder.Register(&KubernetesEventSource{}, &KubernetesEventSourceList{}) +} diff --git a/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/register.go b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/register.go new file mode 100644 index 00000000000..fa5f80118ce --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/register.go @@ -0,0 +1,45 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// NOTE: Boilerplate only. Ignore this file. + +// Package v1alpha1 contains API Schema definitions for the sources v1alpha1 API group +// +k8s:openapi-gen=true +// +k8s:deepcopy-gen=package,register +// +k8s:conversion-gen=github.com/knative/eventing-sources/pkg/apis/sources +// +k8s:defaulter-gen=TypeMeta +// +groupName=sources.eventing.knative.dev +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/runtime/scheme" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: "sources.eventing.knative.dev", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} + + AddToScheme = SchemeBuilder.AddToScheme +) + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} diff --git a/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000000..d6feb2f7022 --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,355 @@ +// +build !ignore_autogenerated + +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by deepcopy-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + v1 "k8s.io/api/core/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerSource) DeepCopyInto(out *ContainerSource) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerSource. +func (in *ContainerSource) DeepCopy() *ContainerSource { + if in == nil { + return nil + } + out := new(ContainerSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ContainerSource) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerSourceList) DeepCopyInto(out *ContainerSourceList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ContainerSource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerSourceList. +func (in *ContainerSourceList) DeepCopy() *ContainerSourceList { + if in == nil { + return nil + } + out := new(ContainerSourceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ContainerSourceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerSourceSpec) DeepCopyInto(out *ContainerSourceSpec) { + *out = *in + if in.Args != nil { + in, out := &in.Args, &out.Args + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Env != nil { + in, out := &in.Env, &out.Env + *out = make([]v1.EnvVar, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Sink != nil { + in, out := &in.Sink, &out.Sink + *out = new(v1.ObjectReference) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerSourceSpec. +func (in *ContainerSourceSpec) DeepCopy() *ContainerSourceSpec { + if in == nil { + return nil + } + out := new(ContainerSourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ContainerSourceStatus) DeepCopyInto(out *ContainerSourceStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(duckv1alpha1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ContainerSourceStatus. +func (in *ContainerSourceStatus) DeepCopy() *ContainerSourceStatus { + if in == nil { + return nil + } + out := new(ContainerSourceStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GcpPubSubSource) DeepCopyInto(out *GcpPubSubSource) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GcpPubSubSource. +func (in *GcpPubSubSource) DeepCopy() *GcpPubSubSource { + if in == nil { + return nil + } + out := new(GcpPubSubSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GcpPubSubSource) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GcpPubSubSourceList) DeepCopyInto(out *GcpPubSubSourceList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]GcpPubSubSource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GcpPubSubSourceList. +func (in *GcpPubSubSourceList) DeepCopy() *GcpPubSubSourceList { + if in == nil { + return nil + } + out := new(GcpPubSubSourceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *GcpPubSubSourceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GcpPubSubSourceSpec) DeepCopyInto(out *GcpPubSubSourceSpec) { + *out = *in + in.GcpCredsSecret.DeepCopyInto(&out.GcpCredsSecret) + if in.Sink != nil { + in, out := &in.Sink, &out.Sink + *out = new(v1.ObjectReference) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GcpPubSubSourceSpec. +func (in *GcpPubSubSourceSpec) DeepCopy() *GcpPubSubSourceSpec { + if in == nil { + return nil + } + out := new(GcpPubSubSourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *GcpPubSubSourceStatus) DeepCopyInto(out *GcpPubSubSourceStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(duckv1alpha1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GcpPubSubSourceStatus. +func (in *GcpPubSubSourceStatus) DeepCopy() *GcpPubSubSourceStatus { + if in == nil { + return nil + } + out := new(GcpPubSubSourceStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesEventSource) DeepCopyInto(out *KubernetesEventSource) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesEventSource. +func (in *KubernetesEventSource) DeepCopy() *KubernetesEventSource { + if in == nil { + return nil + } + out := new(KubernetesEventSource) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubernetesEventSource) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesEventSourceList) DeepCopyInto(out *KubernetesEventSourceList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]KubernetesEventSource, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesEventSourceList. +func (in *KubernetesEventSourceList) DeepCopy() *KubernetesEventSourceList { + if in == nil { + return nil + } + out := new(KubernetesEventSourceList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *KubernetesEventSourceList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesEventSourceSpec) DeepCopyInto(out *KubernetesEventSourceSpec) { + *out = *in + if in.Sink != nil { + in, out := &in.Sink, &out.Sink + *out = new(v1.ObjectReference) + **out = **in + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesEventSourceSpec. +func (in *KubernetesEventSourceSpec) DeepCopy() *KubernetesEventSourceSpec { + if in == nil { + return nil + } + out := new(KubernetesEventSourceSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *KubernetesEventSourceStatus) DeepCopyInto(out *KubernetesEventSourceStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(duckv1alpha1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new KubernetesEventSourceStatus. +func (in *KubernetesEventSourceStatus) DeepCopy() *KubernetesEventSourceStatus { + if in == nil { + return nil + } + out := new(KubernetesEventSourceStatus) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/clientset.go b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/clientset.go new file mode 100644 index 00000000000..b87bad6733d --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/clientset.go @@ -0,0 +1,98 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package versioned + +import ( + sourcesv1alpha1 "github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1" + discovery "k8s.io/client-go/discovery" + rest "k8s.io/client-go/rest" + flowcontrol "k8s.io/client-go/util/flowcontrol" +) + +type Interface interface { + Discovery() discovery.DiscoveryInterface + SourcesV1alpha1() sourcesv1alpha1.SourcesV1alpha1Interface + // Deprecated: please explicitly pick a version if possible. + Sources() sourcesv1alpha1.SourcesV1alpha1Interface +} + +// Clientset contains the clients for groups. Each group has exactly one +// version included in a Clientset. +type Clientset struct { + *discovery.DiscoveryClient + sourcesV1alpha1 *sourcesv1alpha1.SourcesV1alpha1Client +} + +// SourcesV1alpha1 retrieves the SourcesV1alpha1Client +func (c *Clientset) SourcesV1alpha1() sourcesv1alpha1.SourcesV1alpha1Interface { + return c.sourcesV1alpha1 +} + +// Deprecated: Sources retrieves the default version of SourcesClient. +// Please explicitly pick a version. +func (c *Clientset) Sources() sourcesv1alpha1.SourcesV1alpha1Interface { + return c.sourcesV1alpha1 +} + +// Discovery retrieves the DiscoveryClient +func (c *Clientset) Discovery() discovery.DiscoveryInterface { + if c == nil { + return nil + } + return c.DiscoveryClient +} + +// NewForConfig creates a new Clientset for the given config. +func NewForConfig(c *rest.Config) (*Clientset, error) { + configShallowCopy := *c + if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { + configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) + } + var cs Clientset + var err error + cs.sourcesV1alpha1, err = sourcesv1alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + + cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + return &cs, nil +} + +// NewForConfigOrDie creates a new Clientset for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *Clientset { + var cs Clientset + cs.sourcesV1alpha1 = sourcesv1alpha1.NewForConfigOrDie(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) + return &cs +} + +// New creates a new Clientset for the given RESTClient. +func New(c rest.Interface) *Clientset { + var cs Clientset + cs.sourcesV1alpha1 = sourcesv1alpha1.New(c) + + cs.DiscoveryClient = discovery.NewDiscoveryClient(c) + return &cs +} diff --git a/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/doc.go b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/doc.go new file mode 100644 index 00000000000..3fe4685848a --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated clientset. +package versioned diff --git a/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/scheme/doc.go b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/scheme/doc.go new file mode 100644 index 00000000000..60ea8ba90eb --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/scheme/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package contains the scheme of the automatically generated clientset. +package scheme diff --git a/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/scheme/register.go b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/scheme/register.go new file mode 100644 index 00000000000..41e5040d2f7 --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/scheme/register.go @@ -0,0 +1,54 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package scheme + +import ( + sourcesv1alpha1 "github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + schema "k8s.io/apimachinery/pkg/runtime/schema" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" +) + +var Scheme = runtime.NewScheme() +var Codecs = serializer.NewCodecFactory(Scheme) +var ParameterCodec = runtime.NewParameterCodec(Scheme) + +func init() { + v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) + AddToScheme(Scheme) +} + +// AddToScheme adds all types of this clientset into the given scheme. This allows composition +// of clientsets, like in: +// +// import ( +// "k8s.io/client-go/kubernetes" +// clientsetscheme "k8s.io/client-go/kubernetes/scheme" +// aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" +// ) +// +// kclientset, _ := kubernetes.NewForConfig(c) +// aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) +// +// After this, RawExtensions in Kubernetes types will serialize kube-aggregator types +// correctly. +func AddToScheme(scheme *runtime.Scheme) { + sourcesv1alpha1.AddToScheme(scheme) +} diff --git a/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/containersource.go b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/containersource.go new file mode 100644 index 00000000000..7bb99182b62 --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/containersource.go @@ -0,0 +1,174 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1" + scheme "github.com/knative/eventing-sources/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ContainerSourcesGetter has a method to return a ContainerSourceInterface. +// A group's client should implement this interface. +type ContainerSourcesGetter interface { + ContainerSources(namespace string) ContainerSourceInterface +} + +// ContainerSourceInterface has methods to work with ContainerSource resources. +type ContainerSourceInterface interface { + Create(*v1alpha1.ContainerSource) (*v1alpha1.ContainerSource, error) + Update(*v1alpha1.ContainerSource) (*v1alpha1.ContainerSource, error) + UpdateStatus(*v1alpha1.ContainerSource) (*v1alpha1.ContainerSource, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.ContainerSource, error) + List(opts v1.ListOptions) (*v1alpha1.ContainerSourceList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ContainerSource, err error) + ContainerSourceExpansion +} + +// containerSources implements ContainerSourceInterface +type containerSources struct { + client rest.Interface + ns string +} + +// newContainerSources returns a ContainerSources +func newContainerSources(c *SourcesV1alpha1Client, namespace string) *containerSources { + return &containerSources{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the containerSource, and returns the corresponding containerSource object, and an error if there is any. +func (c *containerSources) Get(name string, options v1.GetOptions) (result *v1alpha1.ContainerSource, err error) { + result = &v1alpha1.ContainerSource{} + err = c.client.Get(). + Namespace(c.ns). + Resource("containersources"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ContainerSources that match those selectors. +func (c *containerSources) List(opts v1.ListOptions) (result *v1alpha1.ContainerSourceList, err error) { + result = &v1alpha1.ContainerSourceList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("containersources"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested containerSources. +func (c *containerSources) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("containersources"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Create takes the representation of a containerSource and creates it. Returns the server's representation of the containerSource, and an error, if there is any. +func (c *containerSources) Create(containerSource *v1alpha1.ContainerSource) (result *v1alpha1.ContainerSource, err error) { + result = &v1alpha1.ContainerSource{} + err = c.client.Post(). + Namespace(c.ns). + Resource("containersources"). + Body(containerSource). + Do(). + Into(result) + return +} + +// Update takes the representation of a containerSource and updates it. Returns the server's representation of the containerSource, and an error, if there is any. +func (c *containerSources) Update(containerSource *v1alpha1.ContainerSource) (result *v1alpha1.ContainerSource, err error) { + result = &v1alpha1.ContainerSource{} + err = c.client.Put(). + Namespace(c.ns). + Resource("containersources"). + Name(containerSource.Name). + Body(containerSource). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *containerSources) UpdateStatus(containerSource *v1alpha1.ContainerSource) (result *v1alpha1.ContainerSource, err error) { + result = &v1alpha1.ContainerSource{} + err = c.client.Put(). + Namespace(c.ns). + Resource("containersources"). + Name(containerSource.Name). + SubResource("status"). + Body(containerSource). + Do(). + Into(result) + return +} + +// Delete takes name of the containerSource and deletes it. Returns an error if one occurs. +func (c *containerSources) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("containersources"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *containerSources) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("containersources"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched containerSource. +func (c *containerSources) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ContainerSource, err error) { + result = &v1alpha1.ContainerSource{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("containersources"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/doc.go b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/doc.go new file mode 100644 index 00000000000..75445c17900 --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/doc.go @@ -0,0 +1,20 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/gcppubsubsource.go b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/gcppubsubsource.go new file mode 100644 index 00000000000..af8d095207e --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/gcppubsubsource.go @@ -0,0 +1,174 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1" + scheme "github.com/knative/eventing-sources/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// GcpPubSubSourcesGetter has a method to return a GcpPubSubSourceInterface. +// A group's client should implement this interface. +type GcpPubSubSourcesGetter interface { + GcpPubSubSources(namespace string) GcpPubSubSourceInterface +} + +// GcpPubSubSourceInterface has methods to work with GcpPubSubSource resources. +type GcpPubSubSourceInterface interface { + Create(*v1alpha1.GcpPubSubSource) (*v1alpha1.GcpPubSubSource, error) + Update(*v1alpha1.GcpPubSubSource) (*v1alpha1.GcpPubSubSource, error) + UpdateStatus(*v1alpha1.GcpPubSubSource) (*v1alpha1.GcpPubSubSource, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.GcpPubSubSource, error) + List(opts v1.ListOptions) (*v1alpha1.GcpPubSubSourceList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.GcpPubSubSource, err error) + GcpPubSubSourceExpansion +} + +// gcpPubSubSources implements GcpPubSubSourceInterface +type gcpPubSubSources struct { + client rest.Interface + ns string +} + +// newGcpPubSubSources returns a GcpPubSubSources +func newGcpPubSubSources(c *SourcesV1alpha1Client, namespace string) *gcpPubSubSources { + return &gcpPubSubSources{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the gcpPubSubSource, and returns the corresponding gcpPubSubSource object, and an error if there is any. +func (c *gcpPubSubSources) Get(name string, options v1.GetOptions) (result *v1alpha1.GcpPubSubSource, err error) { + result = &v1alpha1.GcpPubSubSource{} + err = c.client.Get(). + Namespace(c.ns). + Resource("gcppubsubsources"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of GcpPubSubSources that match those selectors. +func (c *gcpPubSubSources) List(opts v1.ListOptions) (result *v1alpha1.GcpPubSubSourceList, err error) { + result = &v1alpha1.GcpPubSubSourceList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("gcppubsubsources"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested gcpPubSubSources. +func (c *gcpPubSubSources) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("gcppubsubsources"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Create takes the representation of a gcpPubSubSource and creates it. Returns the server's representation of the gcpPubSubSource, and an error, if there is any. +func (c *gcpPubSubSources) Create(gcpPubSubSource *v1alpha1.GcpPubSubSource) (result *v1alpha1.GcpPubSubSource, err error) { + result = &v1alpha1.GcpPubSubSource{} + err = c.client.Post(). + Namespace(c.ns). + Resource("gcppubsubsources"). + Body(gcpPubSubSource). + Do(). + Into(result) + return +} + +// Update takes the representation of a gcpPubSubSource and updates it. Returns the server's representation of the gcpPubSubSource, and an error, if there is any. +func (c *gcpPubSubSources) Update(gcpPubSubSource *v1alpha1.GcpPubSubSource) (result *v1alpha1.GcpPubSubSource, err error) { + result = &v1alpha1.GcpPubSubSource{} + err = c.client.Put(). + Namespace(c.ns). + Resource("gcppubsubsources"). + Name(gcpPubSubSource.Name). + Body(gcpPubSubSource). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *gcpPubSubSources) UpdateStatus(gcpPubSubSource *v1alpha1.GcpPubSubSource) (result *v1alpha1.GcpPubSubSource, err error) { + result = &v1alpha1.GcpPubSubSource{} + err = c.client.Put(). + Namespace(c.ns). + Resource("gcppubsubsources"). + Name(gcpPubSubSource.Name). + SubResource("status"). + Body(gcpPubSubSource). + Do(). + Into(result) + return +} + +// Delete takes name of the gcpPubSubSource and deletes it. Returns an error if one occurs. +func (c *gcpPubSubSources) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("gcppubsubsources"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *gcpPubSubSources) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("gcppubsubsources"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched gcpPubSubSource. +func (c *gcpPubSubSources) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.GcpPubSubSource, err error) { + result = &v1alpha1.GcpPubSubSource{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("gcppubsubsources"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/generated_expansion.go b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/generated_expansion.go new file mode 100644 index 00000000000..eeb3f0886d9 --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/generated_expansion.go @@ -0,0 +1,25 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +type ContainerSourceExpansion interface{} + +type GcpPubSubSourceExpansion interface{} + +type KubernetesEventSourceExpansion interface{} diff --git a/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/kuberneteseventsource.go b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/kuberneteseventsource.go new file mode 100644 index 00000000000..75743b2e0dd --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/kuberneteseventsource.go @@ -0,0 +1,174 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1" + scheme "github.com/knative/eventing-sources/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// KubernetesEventSourcesGetter has a method to return a KubernetesEventSourceInterface. +// A group's client should implement this interface. +type KubernetesEventSourcesGetter interface { + KubernetesEventSources(namespace string) KubernetesEventSourceInterface +} + +// KubernetesEventSourceInterface has methods to work with KubernetesEventSource resources. +type KubernetesEventSourceInterface interface { + Create(*v1alpha1.KubernetesEventSource) (*v1alpha1.KubernetesEventSource, error) + Update(*v1alpha1.KubernetesEventSource) (*v1alpha1.KubernetesEventSource, error) + UpdateStatus(*v1alpha1.KubernetesEventSource) (*v1alpha1.KubernetesEventSource, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.KubernetesEventSource, error) + List(opts v1.ListOptions) (*v1alpha1.KubernetesEventSourceList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.KubernetesEventSource, err error) + KubernetesEventSourceExpansion +} + +// kubernetesEventSources implements KubernetesEventSourceInterface +type kubernetesEventSources struct { + client rest.Interface + ns string +} + +// newKubernetesEventSources returns a KubernetesEventSources +func newKubernetesEventSources(c *SourcesV1alpha1Client, namespace string) *kubernetesEventSources { + return &kubernetesEventSources{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the kubernetesEventSource, and returns the corresponding kubernetesEventSource object, and an error if there is any. +func (c *kubernetesEventSources) Get(name string, options v1.GetOptions) (result *v1alpha1.KubernetesEventSource, err error) { + result = &v1alpha1.KubernetesEventSource{} + err = c.client.Get(). + Namespace(c.ns). + Resource("kuberneteseventsources"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of KubernetesEventSources that match those selectors. +func (c *kubernetesEventSources) List(opts v1.ListOptions) (result *v1alpha1.KubernetesEventSourceList, err error) { + result = &v1alpha1.KubernetesEventSourceList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("kuberneteseventsources"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested kubernetesEventSources. +func (c *kubernetesEventSources) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("kuberneteseventsources"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Create takes the representation of a kubernetesEventSource and creates it. Returns the server's representation of the kubernetesEventSource, and an error, if there is any. +func (c *kubernetesEventSources) Create(kubernetesEventSource *v1alpha1.KubernetesEventSource) (result *v1alpha1.KubernetesEventSource, err error) { + result = &v1alpha1.KubernetesEventSource{} + err = c.client.Post(). + Namespace(c.ns). + Resource("kuberneteseventsources"). + Body(kubernetesEventSource). + Do(). + Into(result) + return +} + +// Update takes the representation of a kubernetesEventSource and updates it. Returns the server's representation of the kubernetesEventSource, and an error, if there is any. +func (c *kubernetesEventSources) Update(kubernetesEventSource *v1alpha1.KubernetesEventSource) (result *v1alpha1.KubernetesEventSource, err error) { + result = &v1alpha1.KubernetesEventSource{} + err = c.client.Put(). + Namespace(c.ns). + Resource("kuberneteseventsources"). + Name(kubernetesEventSource.Name). + Body(kubernetesEventSource). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *kubernetesEventSources) UpdateStatus(kubernetesEventSource *v1alpha1.KubernetesEventSource) (result *v1alpha1.KubernetesEventSource, err error) { + result = &v1alpha1.KubernetesEventSource{} + err = c.client.Put(). + Namespace(c.ns). + Resource("kuberneteseventsources"). + Name(kubernetesEventSource.Name). + SubResource("status"). + Body(kubernetesEventSource). + Do(). + Into(result) + return +} + +// Delete takes name of the kubernetesEventSource and deletes it. Returns an error if one occurs. +func (c *kubernetesEventSources) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("kuberneteseventsources"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *kubernetesEventSources) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("kuberneteseventsources"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched kubernetesEventSource. +func (c *kubernetesEventSources) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.KubernetesEventSource, err error) { + result = &v1alpha1.KubernetesEventSource{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("kuberneteseventsources"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/sources_client.go b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/sources_client.go new file mode 100644 index 00000000000..94601cf0321 --- /dev/null +++ b/vendor/github.com/knative/eventing-sources/pkg/client/clientset/versioned/typed/sources/v1alpha1/sources_client.go @@ -0,0 +1,100 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by client-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1" + "github.com/knative/eventing-sources/pkg/client/clientset/versioned/scheme" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + rest "k8s.io/client-go/rest" +) + +type SourcesV1alpha1Interface interface { + RESTClient() rest.Interface + ContainerSourcesGetter + GcpPubSubSourcesGetter + KubernetesEventSourcesGetter +} + +// SourcesV1alpha1Client is used to interact with features provided by the sources.eventing.knative.dev group. +type SourcesV1alpha1Client struct { + restClient rest.Interface +} + +func (c *SourcesV1alpha1Client) ContainerSources(namespace string) ContainerSourceInterface { + return newContainerSources(c, namespace) +} + +func (c *SourcesV1alpha1Client) GcpPubSubSources(namespace string) GcpPubSubSourceInterface { + return newGcpPubSubSources(c, namespace) +} + +func (c *SourcesV1alpha1Client) KubernetesEventSources(namespace string) KubernetesEventSourceInterface { + return newKubernetesEventSources(c, namespace) +} + +// NewForConfig creates a new SourcesV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*SourcesV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &SourcesV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new SourcesV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *SourcesV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new SourcesV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *SourcesV1alpha1Client { + return &SourcesV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *SourcesV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/scheme/scheme.go b/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/scheme/scheme.go new file mode 100644 index 00000000000..79868214e34 --- /dev/null +++ b/vendor/sigs.k8s.io/controller-runtime/pkg/runtime/scheme/scheme.go @@ -0,0 +1,56 @@ +/* +Copyright 2018 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package scheme + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// Builder builds a new Scheme for mapping go types to Kubernetes GroupVersionKinds. +type Builder struct { + GroupVersion schema.GroupVersion + runtime.SchemeBuilder +} + +// Register adds one or objects to the SchemeBuilder so they can be added to a Scheme. Register mutates bld. +func (bld *Builder) Register(object ...runtime.Object) *Builder { + bld.SchemeBuilder.Register(func(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(bld.GroupVersion, object...) + metav1.AddToGroupVersion(scheme, bld.GroupVersion) + return nil + }) + return bld +} + +// RegisterAll registers all types from the Builder argument. RegisterAll mutates bld. +func (bld *Builder) RegisterAll(b *Builder) *Builder { + bld.SchemeBuilder = append(bld.SchemeBuilder, b.SchemeBuilder...) + return bld +} + +// AddToScheme adds all registered types to s. +func (bld *Builder) AddToScheme(s *runtime.Scheme) error { + return bld.SchemeBuilder.AddToScheme(s) +} + +// Build returns a new Scheme containing the registered types. +func (bld *Builder) Build() (*runtime.Scheme, error) { + s := runtime.NewScheme() + return s, bld.AddToScheme(s) +} From 111089f9a3ef7aad2c9f3e5080a0b1a0b47b1e94 Mon Sep 17 00:00:00 2001 From: Scott Andrews Date: Tue, 6 Nov 2018 17:16:36 -0500 Subject: [PATCH 24/54] Remove legacy code (#532) * Remove legacy code remove API groups: - channels.knative.dev - feeds.knative.dev - flows.knative.dev Remove code and dependencies that depended directly on those API groups. This includes all Buses and EventSources. Where practical references to Channel and Subscription in channels.knative.dev have been updated to eventing.knative.dev. The e2e suite has been decimated and will need to be reestabilished. * Fix release script * maybe allow e2e tests to pass * ClusterProvisioner -> ClusterChannelProvisioner in e2etests * Remove deleted image paths for e2e tests * pune more dead code --- Gopkg.lock | 222 +- Gopkg.toml | 13 - cmd/controller/controller-runtime-main.go | 23 +- cmd/controller/main.go | 64 +- cmd/webhook/main.go | 19 - config/200-clusterrole.yaml | 27 - config/200-serviceaccount.yaml | 7 - config/201-clusterrolebinding.yaml | 14 - config/300-bus.yaml | 30 - config/300-channel.yaml | 4 +- config/300-channeleventing.yaml | 31 - config/300-clusterbus.yaml | 32 - config/300-clustereventsource.yaml | 30 - config/300-clustereventtype.yaml | 30 - config/300-eventsource.yaml | 30 - config/300-eventtype.yaml | 30 - config/300-feed.yaml | 29 - config/300-flow.yaml | 29 - config/300-subscription.yaml | 4 +- config/300-subscriptioneventing.yaml | 31 - config/400-flow-controller-config.yaml | 21 - config/buses/gcppubsub/README.md | 29 - config/buses/gcppubsub/gcppubsub-bus.yaml | 51 - .../gcppubsub/gcppubsub-serviceentry.yaml | 26 - config/buses/kafka/README.md | 43 - config/buses/kafka/broker/README.md | 13 - config/buses/kafka/broker/kafka-broker.yaml | 87 - config/buses/kafka/kafka-bus.yaml | 31 - config/buses/kafka/strimzi/README.md | 31 - .../buses/kafka/strimzi/kafka-ephemeral.yaml | 23 - .../buses/kafka/strimzi/kafka-persistent.yaml | 27 - config/buses/stub/README.md | 16 - config/buses/stub/stub-bus.yaml | 25 - hack/release.sh | 43 +- hack/update-codegen.sh | 2 +- pkg/apis/channels/logkey/constants.go | 30 - pkg/apis/channels/register.go | 21 - pkg/apis/channels/v1alpha1/bus_defaults.go | 33 - pkg/apis/channels/v1alpha1/bus_types.go | 174 - pkg/apis/channels/v1alpha1/bus_validation.go | 63 - .../channels/v1alpha1/bus_validation_test.go | 140 - .../channels/v1alpha1/channel_defaults.go | 25 - pkg/apis/channels/v1alpha1/channel_types.go | 152 - .../channels/v1alpha1/channel_validation.go | 68 - .../v1alpha1/channel_validation_test.go | 69 - .../channels/v1alpha1/clusterbus_defaults.go | 21 - .../channels/v1alpha1/clusterbus_types.go | 78 - .../v1alpha1/clusterbus_validation.go | 30 - pkg/apis/channels/v1alpha1/doc.go | 16 - pkg/apis/channels/v1alpha1/param_types.go | 43 - pkg/apis/channels/v1alpha1/register.go | 59 - .../v1alpha1/subscription_defaults.go | 25 - .../channels/v1alpha1/subscription_types.go | 131 - .../v1alpha1/subscription_validation.go | 61 - .../v1alpha1/subscription_validation_test.go | 139 - .../v1alpha1/zz_generated.deepcopy.go | 615 - pkg/apis/feeds/logkey/constants.go | 36 - pkg/apis/feeds/register.go | 21 - .../v1alpha1/cluster_event_source_defaults.go | 25 - .../v1alpha1/cluster_event_source_types.go | 74 - .../cluster_event_source_validation.go | 29 - .../v1alpha1/cluster_event_type_defaults.go | 26 - .../v1alpha1/cluster_event_type_types.go | 75 - .../v1alpha1/cluster_event_type_validation.go | 56 - .../cluster_event_type_validation_test.go | 121 - .../v1alpha1/common_event_source_defaults.go | 21 - .../v1alpha1/common_event_source_test.go | 128 - .../v1alpha1/common_event_source_types.go | 112 - .../common_event_source_validation.go | 26 - .../v1alpha1/common_event_type_defaults.go | 21 - .../feeds/v1alpha1/common_event_type_test.go | 128 - .../feeds/v1alpha1/common_event_type_types.go | 110 - .../v1alpha1/common_event_type_validation.go | 26 - pkg/apis/feeds/v1alpha1/doc.go | 16 - .../feeds/v1alpha1/event_source_defaults.go | 25 - pkg/apis/feeds/v1alpha1/event_source_types.go | 72 - .../feeds/v1alpha1/event_source_validation.go | 29 - .../feeds/v1alpha1/event_type_defaults.go | 26 - pkg/apis/feeds/v1alpha1/event_type_types.go | 75 - .../feeds/v1alpha1/event_type_validation.go | 56 - .../v1alpha1/event_type_validation_test.go | 121 - pkg/apis/feeds/v1alpha1/feed_defaults.go | 27 - pkg/apis/feeds/v1alpha1/feed_defaults_test.go | 79 - pkg/apis/feeds/v1alpha1/feed_types.go | 320 - pkg/apis/feeds/v1alpha1/feed_types_test.go | 112 - pkg/apis/feeds/v1alpha1/feed_validation.go | 72 - .../feeds/v1alpha1/feed_validation_test.go | 118 - pkg/apis/feeds/v1alpha1/register.go | 61 - .../feeds/v1alpha1/zz_generated.deepcopy.go | 752 -- pkg/apis/flows/logkey/constants.go | 24 - pkg/apis/flows/register.go | 21 - pkg/apis/flows/v1alpha1/doc.go | 20 - pkg/apis/flows/v1alpha1/flow_defaults.go | 34 - pkg/apis/flows/v1alpha1/flow_types.go | 410 - pkg/apis/flows/v1alpha1/flow_types_test.go | 356 - pkg/apis/flows/v1alpha1/flow_validation.go | 81 - .../flows/v1alpha1/flow_validation_test.go | 183 - pkg/apis/flows/v1alpha1/register.go | 53 - .../flows/v1alpha1/zz_generated.deepcopy.go | 218 - pkg/buses/bus.go | 198 - pkg/buses/cache.go | 114 - pkg/buses/cache_test.go | 201 - pkg/buses/gcppubsub/bus.go | 292 - pkg/buses/gcppubsub/bus_test.go | 24 - pkg/buses/gcppubsub/dispatcher/kodata/LICENSE | 1 - .../dispatcher/kodata/VENDOR-LICENSE | 1 - pkg/buses/gcppubsub/dispatcher/main.go | 69 - .../gcppubsub/provisioner/kodata/LICENSE | 1 - .../provisioner/kodata/VENDOR-LICENSE | 1 - pkg/buses/gcppubsub/provisioner/main.go | 68 - pkg/buses/handler_funcs.go | 258 - pkg/buses/kafka/bus.go | 287 - pkg/buses/kafka/bus_test.go | 24 - pkg/buses/kafka/dispatcher/kodata/LICENSE | 1 - .../kafka/dispatcher/kodata/VENDOR-LICENSE | 1 - pkg/buses/kafka/dispatcher/main.go | 72 - pkg/buses/kafka/provisioner/kodata/LICENSE | 1 - .../kafka/provisioner/kodata/VENDOR-LICENSE | 1 - pkg/buses/kafka/provisioner/main.go | 72 - pkg/buses/logger.go | 2 - pkg/buses/reconciler.go | 681 - pkg/buses/references.go | 69 +- pkg/buses/references_test.go | 154 +- pkg/buses/stub/bus.go | 147 - pkg/buses/stub/bus_test.go | 24 - pkg/buses/stub/dispatcher/kodata/LICENSE | 1 - .../stub/dispatcher/kodata/VENDOR-LICENSE | 1 - pkg/buses/stub/dispatcher/main.go | 61 - pkg/client/clientset/versioned/clientset.go | 66 - .../versioned/fake/clientset_generated.go | 36 - .../clientset/versioned/fake/register.go | 6 - .../clientset/versioned/scheme/register.go | 6 - .../versioned/typed/channels/v1alpha1/bus.go | 157 - .../typed/channels/v1alpha1/channel.go | 157 - .../channels/v1alpha1/channels_client.go | 105 - .../typed/channels/v1alpha1/clusterbus.go | 147 - .../versioned/typed/channels/v1alpha1/doc.go | 20 - .../typed/channels/v1alpha1/fake/doc.go | 20 - .../typed/channels/v1alpha1/fake/fake_bus.go | 128 - .../channels/v1alpha1/fake/fake_channel.go | 128 - .../v1alpha1/fake/fake_channels_client.go | 52 - .../channels/v1alpha1/fake/fake_clusterbus.go | 120 - .../v1alpha1/fake/fake_subscription.go | 128 - .../channels/v1alpha1/generated_expansion.go | 27 - .../typed/channels/v1alpha1/subscription.go | 157 - .../feeds/v1alpha1/clustereventsource.go | 163 - .../typed/feeds/v1alpha1/clustereventtype.go | 163 - .../versioned/typed/feeds/v1alpha1/doc.go | 20 - .../typed/feeds/v1alpha1/eventsource.go | 174 - .../typed/feeds/v1alpha1/eventtype.go | 174 - .../typed/feeds/v1alpha1/fake/doc.go | 20 - .../v1alpha1/fake/fake_clustereventsource.go | 131 - .../v1alpha1/fake/fake_clustereventtype.go | 131 - .../feeds/v1alpha1/fake/fake_eventsource.go | 140 - .../feeds/v1alpha1/fake/fake_eventtype.go | 140 - .../typed/feeds/v1alpha1/fake/fake_feed.go | 140 - .../feeds/v1alpha1/fake/fake_feeds_client.go | 56 - .../versioned/typed/feeds/v1alpha1/feed.go | 174 - .../typed/feeds/v1alpha1/feeds_client.go | 110 - .../feeds/v1alpha1/generated_expansion.go | 29 - .../versioned/typed/flows/v1alpha1/doc.go | 20 - .../typed/flows/v1alpha1/fake/doc.go | 20 - .../typed/flows/v1alpha1/fake/fake_flow.go | 140 - .../flows/v1alpha1/fake/fake_flows_client.go | 40 - .../versioned/typed/flows/v1alpha1/flow.go | 174 - .../typed/flows/v1alpha1/flows_client.go | 90 - .../flows/v1alpha1/generated_expansion.go | 21 - .../externalversions/channels/interface.go | 46 - .../externalversions/channels/v1alpha1/bus.go | 89 - .../channels/v1alpha1/channel.go | 89 - .../channels/v1alpha1/clusterbus.go | 88 - .../channels/v1alpha1/interface.go | 66 - .../channels/v1alpha1/subscription.go | 89 - .../informers/externalversions/factory.go | 18 - .../externalversions/feeds/interface.go | 46 - .../feeds/v1alpha1/clustereventsource.go | 88 - .../feeds/v1alpha1/clustereventtype.go | 88 - .../feeds/v1alpha1/eventsource.go | 89 - .../feeds/v1alpha1/eventtype.go | 89 - .../externalversions/feeds/v1alpha1/feed.go | 89 - .../feeds/v1alpha1/interface.go | 73 - .../externalversions/flows/interface.go | 46 - .../externalversions/flows/v1alpha1/flow.go | 89 - .../flows/v1alpha1/interface.go | 45 - .../informers/externalversions/generic.go | 37 +- pkg/client/listers/channels/v1alpha1/bus.go | 94 - .../listers/channels/v1alpha1/channel.go | 94 - .../listers/channels/v1alpha1/clusterbus.go | 65 - .../channels/v1alpha1/expansion_generated.go | 47 - .../listers/channels/v1alpha1/subscription.go | 94 - .../feeds/v1alpha1/clustereventsource.go | 65 - .../feeds/v1alpha1/clustereventtype.go | 65 - .../listers/feeds/v1alpha1/eventsource.go | 94 - .../listers/feeds/v1alpha1/eventtype.go | 94 - .../feeds/v1alpha1/expansion_generated.go | 51 - pkg/client/listers/feeds/v1alpha1/feed.go | 94 - .../flows/v1alpha1/expansion_generated.go | 27 - pkg/client/listers/flows/v1alpha1/flow.go | 94 - pkg/controller/bus/controller.go | 726 -- pkg/controller/bus/stub_test.go | 24 - pkg/controller/channel/controller.go | 563 - pkg/controller/channel/stub_test.go | 24 - pkg/controller/clusterbus/controller.go | 703 -- pkg/controller/clusterbus/stub_test.go | 24 - pkg/controller/eventtype/provider.go | 108 - pkg/controller/eventtype/provider_test.go | 67 - pkg/controller/eventtype/reconcile.go | 230 - pkg/controller/eventtype/reconcile_test.go | 299 - pkg/controller/feed/errors.go | 69 - pkg/controller/feed/provider.go | 71 - pkg/controller/feed/reconcile.go | 565 - pkg/controller/feed/reconcile_test.go | 903 -- pkg/controller/feed/resources/job.go | 254 - pkg/controller/feed/resources/job_test.go | 430 - pkg/controller/feed/resources/names.go | 42 - pkg/controller/flow/dyn_client.go | 74 - pkg/controller/flow/provider.go | 91 - pkg/controller/flow/reconcile.go | 355 - pkg/controller/flow/reconcile_test.go | 270 - pkg/controller/flow/resources/channel.go | 42 - pkg/controller/flow/resources/feed.go | 56 - pkg/controller/flow/resources/stub_test.go | 24 - pkg/controller/flow/resources/subscription.go | 43 - pkg/controller/owner_references.go | 24 - pkg/controller/util/bus_util.go | 105 - pkg/controller/util/channel_util.go | 103 - pkg/controller/util/stub_test.go | 24 - pkg/controller/util/subscription_util.go | 79 - pkg/sources/container_event_source_job.go | 164 - pkg/sources/event_source.go | 31 - pkg/sources/event_source_wrapper.go | 56 - pkg/sources/event_trigger.go | 17 - pkg/sources/gcppubsub/eventsource.yaml | 24 - pkg/sources/gcppubsub/eventtype.yaml | 22 - pkg/sources/gcppubsub/gcp_pubsub.go | 251 - .../gcppubsub/googleapis-serviceentry.yaml | 65 - pkg/sources/gcppubsub/kodata/LICENSE | 1 - pkg/sources/gcppubsub/kodata/VENDOR-LICENSE | 1 - .../gcppubsub/receive_adapter/kodata/LICENSE | 1 - .../receive_adapter/kodata/VENDOR-LICENSE | 1 - .../receive_adapter/receive_adapter.go | 97 - .../gcppubsub/receive_adapter/stub_test.go | 24 - pkg/sources/gcppubsub/stub_test.go | 24 - pkg/sources/gcppubsub/watcher_pod.go | 83 - pkg/sources/github/README.md | 99 - pkg/sources/github/events.go | 32 - pkg/sources/github/eventsource.yaml | 24 - pkg/sources/github/eventtype.yaml | 22 - pkg/sources/github/feedlet/feedlet.go | 463 - pkg/sources/github/feedlet/kodata/LICENSE | 1 - .../github/feedlet/kodata/VENDOR-LICENSE | 1 - pkg/sources/github/feedlet/stub_test.go | 24 - pkg/sources/github/github-serviceentry.yaml | 25 - .../github/receive_adapter/kodata/LICENSE | 1 - .../receive_adapter/kodata/VENDOR-LICENSE | 1 - .../github/receive_adapter/receive_adapter.go | 151 - .../github/receive_adapter/stub_test.go | 24 - pkg/sources/github/resources/service.go | 68 - pkg/sources/github/resources/stub_test.go | 24 - pkg/sources/github/stub_test.go | 24 - pkg/sources/k8sevents/eventsource.yaml | 24 - pkg/sources/k8sevents/eventtype.yaml | 24 - pkg/sources/k8sevents/k8s_events.go | 171 - pkg/sources/k8sevents/kodata/LICENSE | 1 - pkg/sources/k8sevents/kodata/VENDOR-LICENSE | 1 - .../k8sevents/receive_adapter/kodata/LICENSE | 1 - .../receive_adapter/kodata/VENDOR-LICENSE | 1 - .../receive_adapter/receive_adapter.go | 194 - .../k8sevents/receive_adapter/stub_test.go | 24 - pkg/sources/k8sevents/stub_test.go | 24 - pkg/sources/k8sevents/watcher_pod.go | 75 - pkg/sources/stub_test.go | 24 - sample/README.md | 11 +- test/crd_checks.go | 20 - test/image_paths.txt | 6 - test/states.go | 7 - third_party/VENDOR-LICENSE | 2398 +--- vendor/cloud.google.com/go/iam/iam.go | 284 - .../go/internal/optional/optional.go | 108 - .../go/internal/version/version.go | 71 - .../cloud.google.com/go/pubsub/apiv1/doc.go | 50 - .../go/pubsub/apiv1/path_funcs.go | 95 - .../go/pubsub/apiv1/publisher_client.go | 398 - .../go/pubsub/apiv1/subscriber_client.go | 593 - vendor/cloud.google.com/go/pubsub/doc.go | 125 - .../go/pubsub/flow_controller.go | 106 - vendor/cloud.google.com/go/pubsub/go18.go | 150 - .../internal/distribution/distribution.go | 70 - vendor/cloud.google.com/go/pubsub/iterator.go | 294 - vendor/cloud.google.com/go/pubsub/message.go | 100 - vendor/cloud.google.com/go/pubsub/not_go18.go | 54 - vendor/cloud.google.com/go/pubsub/pubsub.go | 113 - .../cloud.google.com/go/pubsub/pullstream.go | 173 - vendor/cloud.google.com/go/pubsub/service.go | 120 - vendor/cloud.google.com/go/pubsub/snapshot.go | 160 - .../go/pubsub/subscription.go | 522 - vendor/cloud.google.com/go/pubsub/topic.go | 397 - vendor/github.com/Shopify/sarama/LICENSE | 20 - .../github.com/Shopify/sarama/acl_bindings.go | 119 - .../Shopify/sarama/acl_create_request.go | 76 - .../Shopify/sarama/acl_create_response.go | 88 - .../Shopify/sarama/acl_delete_request.go | 48 - .../Shopify/sarama/acl_delete_response.go | 155 - .../Shopify/sarama/acl_describe_request.go | 25 - .../Shopify/sarama/acl_describe_response.go | 80 - .../github.com/Shopify/sarama/acl_filter.go | 61 - vendor/github.com/Shopify/sarama/acl_types.go | 42 - .../sarama/add_offsets_to_txn_request.go | 52 - .../sarama/add_offsets_to_txn_response.go | 44 - .../sarama/add_partitions_to_txn_request.go | 76 - .../sarama/add_partitions_to_txn_response.go | 108 - vendor/github.com/Shopify/sarama/admin.go | 375 - .../Shopify/sarama/alter_configs_request.go | 120 - .../Shopify/sarama/alter_configs_response.go | 95 - .../Shopify/sarama/api_versions_request.go | 24 - .../Shopify/sarama/api_versions_response.go | 87 - .../Shopify/sarama/async_producer.go | 932 -- vendor/github.com/Shopify/sarama/broker.go | 883 -- vendor/github.com/Shopify/sarama/client.go | 846 -- vendor/github.com/Shopify/sarama/config.go | 458 - .../Shopify/sarama/config_resource_type.go | 15 - vendor/github.com/Shopify/sarama/consumer.go | 807 -- .../Shopify/sarama/consumer_group_members.go | 94 - .../sarama/consumer_metadata_request.go | 33 - .../sarama/consumer_metadata_response.go | 77 - .../github.com/Shopify/sarama/crc32_field.go | 69 - .../sarama/create_partitions_request.go | 121 - .../sarama/create_partitions_response.go | 94 - .../Shopify/sarama/create_topics_request.go | 174 - .../Shopify/sarama/create_topics_response.go | 112 - .../Shopify/sarama/delete_groups_request.go | 30 - .../Shopify/sarama/delete_groups_response.go | 70 - .../Shopify/sarama/delete_records_request.go | 126 - .../Shopify/sarama/delete_records_response.go | 158 - .../Shopify/sarama/delete_topics_request.go | 48 - .../Shopify/sarama/delete_topics_response.go | 78 - .../sarama/describe_configs_request.go | 91 - .../sarama/describe_configs_response.go | 188 - .../Shopify/sarama/describe_groups_request.go | 30 - .../sarama/describe_groups_response.go | 187 - .../Shopify/sarama/encoder_decoder.go | 89 - .../Shopify/sarama/end_txn_request.go | 50 - .../Shopify/sarama/end_txn_response.go | 44 - vendor/github.com/Shopify/sarama/errors.go | 281 - .../Shopify/sarama/fetch_request.go | 170 - .../Shopify/sarama/fetch_response.go | 385 - .../sarama/find_coordinator_request.go | 61 - .../sarama/find_coordinator_response.go | 92 - .../Shopify/sarama/heartbeat_request.go | 47 - .../Shopify/sarama/heartbeat_response.go | 32 - .../sarama/init_producer_id_request.go | 43 - .../sarama/init_producer_id_response.go | 55 - .../Shopify/sarama/join_group_request.go | 163 - .../Shopify/sarama/join_group_response.go | 135 - .../Shopify/sarama/leave_group_request.go | 40 - .../Shopify/sarama/leave_group_response.go | 32 - .../github.com/Shopify/sarama/length_field.go | 69 - .../Shopify/sarama/list_groups_request.go | 24 - .../Shopify/sarama/list_groups_response.go | 69 - vendor/github.com/Shopify/sarama/message.go | 223 - .../github.com/Shopify/sarama/message_set.go | 102 - .../Shopify/sarama/metadata_request.go | 88 - .../Shopify/sarama/metadata_response.go | 310 - vendor/github.com/Shopify/sarama/metrics.go | 51 - .../github.com/Shopify/sarama/mockbroker.go | 330 - .../Shopify/sarama/mockresponses.go | 727 -- .../Shopify/sarama/offset_commit_request.go | 204 - .../Shopify/sarama/offset_commit_response.go | 85 - .../Shopify/sarama/offset_fetch_request.go | 81 - .../Shopify/sarama/offset_fetch_response.go | 143 - .../Shopify/sarama/offset_manager.go | 560 - .../Shopify/sarama/offset_request.go | 132 - .../Shopify/sarama/offset_response.go | 174 - .../Shopify/sarama/packet_decoder.go | 60 - .../Shopify/sarama/packet_encoder.go | 65 - .../github.com/Shopify/sarama/partitioner.go | 217 - .../github.com/Shopify/sarama/prep_encoder.go | 153 - .../Shopify/sarama/produce_request.go | 252 - .../Shopify/sarama/produce_response.go | 183 - .../github.com/Shopify/sarama/produce_set.go | 252 - .../github.com/Shopify/sarama/real_decoder.go | 324 - .../github.com/Shopify/sarama/real_encoder.go | 156 - vendor/github.com/Shopify/sarama/record.go | 113 - .../github.com/Shopify/sarama/record_batch.go | 268 - vendor/github.com/Shopify/sarama/records.go | 173 - vendor/github.com/Shopify/sarama/request.go | 149 - .../Shopify/sarama/response_header.go | 21 - vendor/github.com/Shopify/sarama/sarama.go | 99 - .../Shopify/sarama/sasl_handshake_request.go | 33 - .../Shopify/sarama/sasl_handshake_response.go | 38 - .../Shopify/sarama/sync_group_request.go | 100 - .../Shopify/sarama/sync_group_response.go | 41 - .../Shopify/sarama/sync_producer.go | 149 - vendor/github.com/Shopify/sarama/timestamp.go | 40 - .../sarama/txn_offset_commit_request.go | 126 - .../sarama/txn_offset_commit_response.go | 83 - vendor/github.com/Shopify/sarama/utils.go | 212 - vendor/github.com/bsm/sarama-cluster/LICENSE | 22 - .../github.com/bsm/sarama-cluster/balancer.go | 174 - .../github.com/bsm/sarama-cluster/client.go | 50 - .../github.com/bsm/sarama-cluster/cluster.go | 25 - .../github.com/bsm/sarama-cluster/config.go | 146 - .../github.com/bsm/sarama-cluster/consumer.go | 924 -- vendor/github.com/bsm/sarama-cluster/doc.go | 8 - .../github.com/bsm/sarama-cluster/offsets.go | 69 - .../bsm/sarama-cluster/partitions.go | 287 - vendor/github.com/bsm/sarama-cluster/util.go | 75 - .../github.com/eapache/go-resiliency/LICENSE | 22 - .../eapache/go-resiliency/breaker/breaker.go | 161 - .../eapache/go-xerial-snappy/LICENSE | 21 - .../eapache/go-xerial-snappy/snappy.go | 43 - vendor/github.com/eapache/queue/LICENSE | 21 - vendor/github.com/eapache/queue/queue.go | 102 - .../protoc-gen-go/descriptor/descriptor.pb.go | 2812 ----- .../golang/protobuf/ptypes/empty/empty.pb.go | 79 - vendor/github.com/golang/snappy/AUTHORS | 15 - vendor/github.com/golang/snappy/CONTRIBUTORS | 37 - vendor/github.com/golang/snappy/LICENSE | 27 - vendor/github.com/golang/snappy/decode.go | 237 - .../github.com/golang/snappy/decode_amd64.go | 14 - .../github.com/golang/snappy/decode_amd64.s | 490 - .../github.com/golang/snappy/decode_other.go | 101 - vendor/github.com/golang/snappy/encode.go | 285 - .../github.com/golang/snappy/encode_amd64.go | 29 - .../github.com/golang/snappy/encode_amd64.s | 730 -- .../github.com/golang/snappy/encode_other.go | 238 - vendor/github.com/golang/snappy/snappy.go | 98 - vendor/github.com/google/go-github/AUTHORS | 171 - vendor/github.com/google/go-github/LICENSE | 341 - .../google/go-github/github/activity.go | 69 - .../go-github/github/activity_events.go | 324 - .../github/activity_notifications.go | 223 - .../google/go-github/github/activity_star.go | 135 - .../go-github/github/activity_watching.go | 146 - .../google/go-github/github/admin.go | 101 - .../google/go-github/github/admin_stats.go | 171 - .../google/go-github/github/apps.go | 169 - .../go-github/github/apps_installation.go | 114 - .../go-github/github/apps_marketplace.go | 180 - .../google/go-github/github/authorizations.go | 435 - .../github.com/google/go-github/github/doc.go | 191 - .../google/go-github/github/event_types.go | 748 -- .../google/go-github/github/gen-accessors.go | 332 - .../google/go-github/github/gists.go | 388 - .../google/go-github/github/gists_comments.go | 119 - .../github.com/google/go-github/github/git.go | 12 - .../google/go-github/github/git_blobs.go | 57 - .../google/go-github/github/git_commits.go | 139 - .../google/go-github/github/git_refs.go | 233 - .../google/go-github/github/git_tags.go | 84 - .../google/go-github/github/git_trees.go | 93 - .../go-github/github/github-accessors.go | 10429 ---------------- .../google/go-github/github/github.go | 980 -- .../google/go-github/github/gitignore.go | 64 - .../google/go-github/github/issues.go | 330 - .../go-github/github/issues_assignees.go | 85 - .../go-github/github/issues_comments.go | 148 - .../google/go-github/github/issues_events.go | 151 - .../google/go-github/github/issues_labels.go | 251 - .../go-github/github/issues_milestones.go | 160 - .../go-github/github/issues_timeline.go | 149 - .../google/go-github/github/licenses.go | 103 - .../google/go-github/github/messages.go | 245 - .../google/go-github/github/migrations.go | 224 - .../github/migrations_source_import.go | 329 - .../google/go-github/github/misc.go | 253 - .../google/go-github/github/orgs.go | 209 - .../google/go-github/github/orgs_hooks.go | 107 - .../google/go-github/github/orgs_members.go | 299 - .../github/orgs_outside_collaborators.go | 81 - .../google/go-github/github/orgs_projects.go | 60 - .../google/go-github/github/orgs_teams.go | 512 - .../go-github/github/orgs_users_blocking.go | 91 - .../google/go-github/github/projects.go | 431 - .../google/go-github/github/pulls.go | 371 - .../google/go-github/github/pulls_comments.go | 157 - .../go-github/github/pulls_reviewers.go | 88 - .../google/go-github/github/pulls_reviews.go | 236 - .../google/go-github/github/reactions.go | 273 - .../google/go-github/github/repos.go | 1076 -- .../go-github/github/repos_collaborators.go | 140 - .../google/go-github/github/repos_comments.go | 161 - .../google/go-github/github/repos_commits.go | 237 - .../github/repos_community_health.go | 57 - .../google/go-github/github/repos_contents.go | 266 - .../go-github/github/repos_deployments.go | 237 - .../google/go-github/github/repos_forks.go | 85 - .../google/go-github/github/repos_hooks.go | 192 - .../go-github/github/repos_invitations.go | 98 - .../google/go-github/github/repos_keys.go | 111 - .../google/go-github/github/repos_merging.go | 38 - .../google/go-github/github/repos_pages.go | 143 - .../google/go-github/github/repos_projects.go | 69 - .../google/go-github/github/repos_releases.go | 327 - .../google/go-github/github/repos_stats.go | 226 - .../google/go-github/github/repos_statuses.go | 129 - .../google/go-github/github/repos_traffic.go | 141 - .../google/go-github/github/search.go | 210 - .../google/go-github/github/strings.go | 93 - .../google/go-github/github/timestamp.go | 41 - .../google/go-github/github/users.go | 230 - .../go-github/github/users_administration.go | 67 - .../google/go-github/github/users_blocking.go | 91 - .../google/go-github/github/users_emails.go | 71 - .../go-github/github/users_followers.go | 119 - .../google/go-github/github/users_gpg_keys.go | 140 - .../google/go-github/github/users_keys.go | 108 - .../google/go-github/github/with_appengine.go | 25 - .../go-github/github/without_appengine.go | 19 - .../github.com/google/go-querystring/LICENSE | 27 - .../google/go-querystring/query/encode.go | 320 - vendor/github.com/googleapis/gax-go/LICENSE | 27 - .../googleapis/gax-go/call_option.go | 157 - vendor/github.com/googleapis/gax-go/gax.go | 40 - vendor/github.com/googleapis/gax-go/header.go | 24 - vendor/github.com/googleapis/gax-go/invoke.go | 90 - vendor/github.com/pierrec/lz4/LICENSE | 28 - vendor/github.com/pierrec/lz4/block.go | 397 - vendor/github.com/pierrec/lz4/debug.go | 21 - vendor/github.com/pierrec/lz4/debug_stub.go | 5 - .../pierrec/lz4/internal/xxh32/xxh32.go | 230 - .../pierrec/lz4/internal/xxh32/xxh32zero.go | 180 - vendor/github.com/pierrec/lz4/lz4.go | 68 - vendor/github.com/pierrec/lz4/lz4_go1.10.go | 29 - .../github.com/pierrec/lz4/lz4_notgo1.10.go | 29 - vendor/github.com/pierrec/lz4/reader.go | 273 - vendor/github.com/pierrec/lz4/writer.go | 235 - vendor/github.com/rcrowley/go-metrics/LICENSE | 29 - .../github.com/rcrowley/go-metrics/counter.go | 112 - .../github.com/rcrowley/go-metrics/debug.go | 76 - vendor/github.com/rcrowley/go-metrics/ewma.go | 138 - .../github.com/rcrowley/go-metrics/gauge.go | 120 - .../rcrowley/go-metrics/gauge_float64.go | 125 - .../rcrowley/go-metrics/graphite.go | 113 - .../rcrowley/go-metrics/healthcheck.go | 61 - .../rcrowley/go-metrics/histogram.go | 202 - vendor/github.com/rcrowley/go-metrics/json.go | 31 - vendor/github.com/rcrowley/go-metrics/log.go | 80 - .../github.com/rcrowley/go-metrics/meter.go | 257 - .../github.com/rcrowley/go-metrics/metrics.go | 13 - .../rcrowley/go-metrics/opentsdb.go | 119 - .../rcrowley/go-metrics/registry.go | 363 - .../github.com/rcrowley/go-metrics/runtime.go | 212 - .../rcrowley/go-metrics/runtime_cgo.go | 10 - .../go-metrics/runtime_gccpufraction.go | 9 - .../rcrowley/go-metrics/runtime_no_cgo.go | 7 - .../go-metrics/runtime_no_gccpufraction.go | 9 - .../github.com/rcrowley/go-metrics/sample.go | 616 - .../github.com/rcrowley/go-metrics/syslog.go | 78 - .../github.com/rcrowley/go-metrics/timer.go | 329 - .../github.com/rcrowley/go-metrics/writer.go | 100 - .../exporter/stackdriver/propagation/http.go | 94 - .../go.opencensus.io/plugin/ocgrpc/client.go | 55 - .../plugin/ocgrpc/client_metrics.go | 116 - .../plugin/ocgrpc/client_stats_handler.go | 49 - vendor/go.opencensus.io/plugin/ocgrpc/doc.go | 19 - .../go.opencensus.io/plugin/ocgrpc/server.go | 80 - .../plugin/ocgrpc/server_metrics.go | 97 - .../plugin/ocgrpc/server_stats_handler.go | 63 - .../plugin/ocgrpc/stats_common.go | 199 - .../plugin/ocgrpc/trace_common.go | 107 - .../x/net/internal/timeseries/timeseries.go | 525 - vendor/golang.org/x/net/trace/events.go | 532 - vendor/golang.org/x/net/trace/histogram.go | 365 - vendor/golang.org/x/net/trace/trace.go | 1103 -- vendor/golang.org/x/net/trace/trace_go16.go | 21 - vendor/golang.org/x/net/trace/trace_go17.go | 21 - .../golang.org/x/sync/semaphore/semaphore.go | 131 - vendor/google.golang.org/api/AUTHORS | 10 - vendor/google.golang.org/api/CONTRIBUTORS | 54 - vendor/google.golang.org/api/LICENSE | 27 - .../googleapi/internal/uritemplates/LICENSE | 18 - .../api/googleapi/transport/apikey.go | 38 - .../google.golang.org/api/internal/creds.go | 42 - vendor/google.golang.org/api/internal/pool.go | 59 - .../api/internal/settings.go | 62 - .../api/iterator/iterator.go | 231 - .../api/option/credentials_go19.go | 32 - .../api/option/credentials_notgo19.go | 32 - vendor/google.golang.org/api/option/option.go | 175 - .../api/support/bundler/bundler.go | 349 - .../google.golang.org/api/transport/dial.go | 49 - .../google.golang.org/api/transport/go19.go | 34 - .../api/transport/grpc/dial.go | 87 - .../api/transport/grpc/dial_appengine.go | 41 - .../api/transport/grpc/go18.go | 26 - .../api/transport/grpc/not_go18.go | 21 - .../api/transport/http/dial.go | 138 - .../api/transport/http/dial_appengine.go | 30 - .../api/transport/http/go18.go | 31 - .../api/transport/http/not_go18.go | 21 - .../api/transport/not_go19.go | 34 - .../internal/socket/socket_service.pb.go | 1858 --- .../google.golang.org/appengine/socket/doc.go | 10 - .../appengine/socket/socket_classic.go | 290 - .../appengine/socket/socket_vm.go | 64 - vendor/google.golang.org/genproto/LICENSE | 202 - .../api/annotations/annotations.pb.go | 54 - .../googleapis/api/annotations/http.pb.go | 666 - .../googleapis/iam/v1/iam_policy.pb.go | 412 - .../genproto/googleapis/iam/v1/policy.pb.go | 366 - .../googleapis/pubsub/v1/pubsub.pb.go | 3239 ----- .../googleapis/rpc/status/status.pb.go | 156 - .../protobuf/field_mask/field_mask.pb.go | 288 - vendor/google.golang.org/grpc/AUTHORS | 1 - vendor/google.golang.org/grpc/LICENSE | 202 - vendor/google.golang.org/grpc/backoff.go | 96 - vendor/google.golang.org/grpc/balancer.go | 416 - .../grpc/balancer/balancer.go | 228 - .../grpc/balancer/base/balancer.go | 208 - .../grpc/balancer/base/base.go | 52 - .../grpc/balancer/roundrobin/roundrobin.go | 79 - .../grpc/balancer_conn_wrappers.go | 300 - .../grpc/balancer_v1_wrapper.go | 372 - vendor/google.golang.org/grpc/call.go | 93 - .../google.golang.org/grpc/channelz/funcs.go | 573 - .../google.golang.org/grpc/channelz/types.go | 418 - vendor/google.golang.org/grpc/clientconn.go | 1591 --- vendor/google.golang.org/grpc/codec.go | 50 - .../grpc/codes/code_string.go | 62 - vendor/google.golang.org/grpc/codes/codes.go | 184 - .../grpc/connectivity/connectivity.go | 72 - .../grpc/credentials/credentials.go | 220 - .../grpc/credentials/credentials_util_go17.go | 60 - .../grpc/credentials/credentials_util_go18.go | 38 - .../credentials/credentials_util_pre_go17.go | 57 - .../grpc/credentials/oauth/oauth.go | 173 - vendor/google.golang.org/grpc/doc.go | 24 - .../grpc/encoding/encoding.go | 118 - .../grpc/encoding/proto/proto.go | 110 - vendor/google.golang.org/grpc/envconfig.go | 37 - vendor/google.golang.org/grpc/go16.go | 70 - vendor/google.golang.org/grpc/go17.go | 71 - vendor/google.golang.org/grpc/grpclb.go | 341 - .../grpclb/grpc_lb_v1/messages/messages.pb.go | 799 -- .../google.golang.org/grpc/grpclb_picker.go | 159 - .../grpc/grpclb_remote_balancer.go | 266 - vendor/google.golang.org/grpc/grpclb_util.go | 214 - .../google.golang.org/grpc/grpclog/grpclog.go | 126 - .../google.golang.org/grpc/grpclog/logger.go | 85 - .../grpc/grpclog/loggerv2.go | 195 - vendor/google.golang.org/grpc/interceptor.go | 77 - .../grpc/internal/internal.go | 27 - .../grpc/keepalive/keepalive.go | 65 - .../grpc/metadata/metadata.go | 210 - .../grpc/naming/dns_resolver.go | 290 - vendor/google.golang.org/grpc/naming/go17.go | 34 - vendor/google.golang.org/grpc/naming/go18.go | 28 - .../google.golang.org/grpc/naming/naming.go | 69 - vendor/google.golang.org/grpc/peer/peer.go | 51 - .../google.golang.org/grpc/picker_wrapper.go | 331 - vendor/google.golang.org/grpc/pickfirst.go | 108 - vendor/google.golang.org/grpc/proxy.go | 130 - .../grpc/resolver/dns/dns_resolver.go | 379 - .../grpc/resolver/dns/go17.go | 35 - .../grpc/resolver/dns/go18.go | 29 - .../grpc/resolver/passthrough/passthrough.go | 57 - .../grpc/resolver/resolver.go | 154 - .../grpc/resolver_conn_wrapper.go | 158 - vendor/google.golang.org/grpc/rpc_util.go | 727 -- vendor/google.golang.org/grpc/server.go | 1486 --- .../google.golang.org/grpc/service_config.go | 233 - .../google.golang.org/grpc/stats/handlers.go | 64 - vendor/google.golang.org/grpc/stats/stats.go | 296 - .../google.golang.org/grpc/status/status.go | 189 - vendor/google.golang.org/grpc/stream.go | 765 -- vendor/google.golang.org/grpc/tap/tap.go | 51 - vendor/google.golang.org/grpc/trace.go | 113 - .../grpc/transport/bdp_estimator.go | 140 - .../grpc/transport/controlbuf.go | 769 -- .../grpc/transport/flowcontrol.go | 236 - .../google.golang.org/grpc/transport/go16.go | 51 - .../google.golang.org/grpc/transport/go17.go | 52 - .../grpc/transport/handler_server.go | 451 - .../grpc/transport/http2_client.go | 1284 -- .../grpc/transport/http2_server.go | 1136 -- .../grpc/transport/http_util.go | 574 - .../google.golang.org/grpc/transport/log.go | 50 - .../grpc/transport/transport.go | 683 - .../go-playground/webhooks.v3/LICENSE | 22 - .../webhooks.v3/github/github.go | 292 - .../webhooks.v3/github/payload.go | 5087 -------- .../go-playground/webhooks.v3/logger.go | 44 - .../go-playground/webhooks.v3/webhooks.go | 124 - 684 files changed, 381 insertions(+), 124618 deletions(-) delete mode 100644 config/200-clusterrole.yaml delete mode 100644 config/300-bus.yaml delete mode 100644 config/300-channeleventing.yaml delete mode 100644 config/300-clusterbus.yaml delete mode 100644 config/300-clustereventsource.yaml delete mode 100644 config/300-clustereventtype.yaml delete mode 100644 config/300-eventsource.yaml delete mode 100644 config/300-eventtype.yaml delete mode 100644 config/300-feed.yaml delete mode 100644 config/300-flow.yaml delete mode 100644 config/300-subscriptioneventing.yaml delete mode 100644 config/400-flow-controller-config.yaml delete mode 100644 config/buses/gcppubsub/README.md delete mode 100644 config/buses/gcppubsub/gcppubsub-bus.yaml delete mode 100644 config/buses/gcppubsub/gcppubsub-serviceentry.yaml delete mode 100644 config/buses/kafka/README.md delete mode 100644 config/buses/kafka/broker/README.md delete mode 100644 config/buses/kafka/broker/kafka-broker.yaml delete mode 100644 config/buses/kafka/kafka-bus.yaml delete mode 100644 config/buses/kafka/strimzi/README.md delete mode 100644 config/buses/kafka/strimzi/kafka-ephemeral.yaml delete mode 100644 config/buses/kafka/strimzi/kafka-persistent.yaml delete mode 100644 config/buses/stub/README.md delete mode 100644 config/buses/stub/stub-bus.yaml delete mode 100644 pkg/apis/channels/logkey/constants.go delete mode 100644 pkg/apis/channels/register.go delete mode 100644 pkg/apis/channels/v1alpha1/bus_defaults.go delete mode 100644 pkg/apis/channels/v1alpha1/bus_types.go delete mode 100644 pkg/apis/channels/v1alpha1/bus_validation.go delete mode 100644 pkg/apis/channels/v1alpha1/bus_validation_test.go delete mode 100644 pkg/apis/channels/v1alpha1/channel_defaults.go delete mode 100644 pkg/apis/channels/v1alpha1/channel_types.go delete mode 100644 pkg/apis/channels/v1alpha1/channel_validation.go delete mode 100644 pkg/apis/channels/v1alpha1/channel_validation_test.go delete mode 100644 pkg/apis/channels/v1alpha1/clusterbus_defaults.go delete mode 100644 pkg/apis/channels/v1alpha1/clusterbus_types.go delete mode 100644 pkg/apis/channels/v1alpha1/clusterbus_validation.go delete mode 100644 pkg/apis/channels/v1alpha1/doc.go delete mode 100644 pkg/apis/channels/v1alpha1/param_types.go delete mode 100644 pkg/apis/channels/v1alpha1/register.go delete mode 100644 pkg/apis/channels/v1alpha1/subscription_defaults.go delete mode 100644 pkg/apis/channels/v1alpha1/subscription_types.go delete mode 100644 pkg/apis/channels/v1alpha1/subscription_validation.go delete mode 100644 pkg/apis/channels/v1alpha1/subscription_validation_test.go delete mode 100644 pkg/apis/channels/v1alpha1/zz_generated.deepcopy.go delete mode 100644 pkg/apis/feeds/logkey/constants.go delete mode 100644 pkg/apis/feeds/register.go delete mode 100644 pkg/apis/feeds/v1alpha1/cluster_event_source_defaults.go delete mode 100644 pkg/apis/feeds/v1alpha1/cluster_event_source_types.go delete mode 100644 pkg/apis/feeds/v1alpha1/cluster_event_source_validation.go delete mode 100644 pkg/apis/feeds/v1alpha1/cluster_event_type_defaults.go delete mode 100644 pkg/apis/feeds/v1alpha1/cluster_event_type_types.go delete mode 100644 pkg/apis/feeds/v1alpha1/cluster_event_type_validation.go delete mode 100644 pkg/apis/feeds/v1alpha1/cluster_event_type_validation_test.go delete mode 100644 pkg/apis/feeds/v1alpha1/common_event_source_defaults.go delete mode 100644 pkg/apis/feeds/v1alpha1/common_event_source_test.go delete mode 100644 pkg/apis/feeds/v1alpha1/common_event_source_types.go delete mode 100644 pkg/apis/feeds/v1alpha1/common_event_source_validation.go delete mode 100644 pkg/apis/feeds/v1alpha1/common_event_type_defaults.go delete mode 100644 pkg/apis/feeds/v1alpha1/common_event_type_test.go delete mode 100644 pkg/apis/feeds/v1alpha1/common_event_type_types.go delete mode 100644 pkg/apis/feeds/v1alpha1/common_event_type_validation.go delete mode 100644 pkg/apis/feeds/v1alpha1/doc.go delete mode 100644 pkg/apis/feeds/v1alpha1/event_source_defaults.go delete mode 100644 pkg/apis/feeds/v1alpha1/event_source_types.go delete mode 100644 pkg/apis/feeds/v1alpha1/event_source_validation.go delete mode 100644 pkg/apis/feeds/v1alpha1/event_type_defaults.go delete mode 100644 pkg/apis/feeds/v1alpha1/event_type_types.go delete mode 100644 pkg/apis/feeds/v1alpha1/event_type_validation.go delete mode 100644 pkg/apis/feeds/v1alpha1/event_type_validation_test.go delete mode 100644 pkg/apis/feeds/v1alpha1/feed_defaults.go delete mode 100644 pkg/apis/feeds/v1alpha1/feed_defaults_test.go delete mode 100644 pkg/apis/feeds/v1alpha1/feed_types.go delete mode 100644 pkg/apis/feeds/v1alpha1/feed_types_test.go delete mode 100644 pkg/apis/feeds/v1alpha1/feed_validation.go delete mode 100644 pkg/apis/feeds/v1alpha1/feed_validation_test.go delete mode 100644 pkg/apis/feeds/v1alpha1/register.go delete mode 100644 pkg/apis/feeds/v1alpha1/zz_generated.deepcopy.go delete mode 100644 pkg/apis/flows/logkey/constants.go delete mode 100644 pkg/apis/flows/register.go delete mode 100644 pkg/apis/flows/v1alpha1/doc.go delete mode 100644 pkg/apis/flows/v1alpha1/flow_defaults.go delete mode 100644 pkg/apis/flows/v1alpha1/flow_types.go delete mode 100644 pkg/apis/flows/v1alpha1/flow_types_test.go delete mode 100644 pkg/apis/flows/v1alpha1/flow_validation.go delete mode 100644 pkg/apis/flows/v1alpha1/flow_validation_test.go delete mode 100644 pkg/apis/flows/v1alpha1/register.go delete mode 100644 pkg/apis/flows/v1alpha1/zz_generated.deepcopy.go delete mode 100644 pkg/buses/bus.go delete mode 100644 pkg/buses/cache.go delete mode 100644 pkg/buses/cache_test.go delete mode 100644 pkg/buses/gcppubsub/bus.go delete mode 100644 pkg/buses/gcppubsub/bus_test.go delete mode 120000 pkg/buses/gcppubsub/dispatcher/kodata/LICENSE delete mode 120000 pkg/buses/gcppubsub/dispatcher/kodata/VENDOR-LICENSE delete mode 100644 pkg/buses/gcppubsub/dispatcher/main.go delete mode 120000 pkg/buses/gcppubsub/provisioner/kodata/LICENSE delete mode 120000 pkg/buses/gcppubsub/provisioner/kodata/VENDOR-LICENSE delete mode 100644 pkg/buses/gcppubsub/provisioner/main.go delete mode 100644 pkg/buses/handler_funcs.go delete mode 100644 pkg/buses/kafka/bus.go delete mode 100644 pkg/buses/kafka/bus_test.go delete mode 120000 pkg/buses/kafka/dispatcher/kodata/LICENSE delete mode 120000 pkg/buses/kafka/dispatcher/kodata/VENDOR-LICENSE delete mode 100644 pkg/buses/kafka/dispatcher/main.go delete mode 120000 pkg/buses/kafka/provisioner/kodata/LICENSE delete mode 120000 pkg/buses/kafka/provisioner/kodata/VENDOR-LICENSE delete mode 100644 pkg/buses/kafka/provisioner/main.go delete mode 100644 pkg/buses/reconciler.go delete mode 100644 pkg/buses/stub/bus.go delete mode 100644 pkg/buses/stub/bus_test.go delete mode 120000 pkg/buses/stub/dispatcher/kodata/LICENSE delete mode 120000 pkg/buses/stub/dispatcher/kodata/VENDOR-LICENSE delete mode 100644 pkg/buses/stub/dispatcher/main.go delete mode 100644 pkg/client/clientset/versioned/typed/channels/v1alpha1/bus.go delete mode 100644 pkg/client/clientset/versioned/typed/channels/v1alpha1/channel.go delete mode 100644 pkg/client/clientset/versioned/typed/channels/v1alpha1/channels_client.go delete mode 100644 pkg/client/clientset/versioned/typed/channels/v1alpha1/clusterbus.go delete mode 100644 pkg/client/clientset/versioned/typed/channels/v1alpha1/doc.go delete mode 100644 pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/doc.go delete mode 100644 pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_bus.go delete mode 100644 pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_channel.go delete mode 100644 pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_channels_client.go delete mode 100644 pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_clusterbus.go delete mode 100644 pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_subscription.go delete mode 100644 pkg/client/clientset/versioned/typed/channels/v1alpha1/generated_expansion.go delete mode 100644 pkg/client/clientset/versioned/typed/channels/v1alpha1/subscription.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/clustereventsource.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/clustereventtype.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/doc.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/eventsource.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/eventtype.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/doc.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_clustereventsource.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_clustereventtype.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_eventsource.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_eventtype.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_feed.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_feeds_client.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/feed.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/feeds_client.go delete mode 100644 pkg/client/clientset/versioned/typed/feeds/v1alpha1/generated_expansion.go delete mode 100644 pkg/client/clientset/versioned/typed/flows/v1alpha1/doc.go delete mode 100644 pkg/client/clientset/versioned/typed/flows/v1alpha1/fake/doc.go delete mode 100644 pkg/client/clientset/versioned/typed/flows/v1alpha1/fake/fake_flow.go delete mode 100644 pkg/client/clientset/versioned/typed/flows/v1alpha1/fake/fake_flows_client.go delete mode 100644 pkg/client/clientset/versioned/typed/flows/v1alpha1/flow.go delete mode 100644 pkg/client/clientset/versioned/typed/flows/v1alpha1/flows_client.go delete mode 100644 pkg/client/clientset/versioned/typed/flows/v1alpha1/generated_expansion.go delete mode 100644 pkg/client/informers/externalversions/channels/interface.go delete mode 100644 pkg/client/informers/externalversions/channels/v1alpha1/bus.go delete mode 100644 pkg/client/informers/externalversions/channels/v1alpha1/channel.go delete mode 100644 pkg/client/informers/externalversions/channels/v1alpha1/clusterbus.go delete mode 100644 pkg/client/informers/externalversions/channels/v1alpha1/interface.go delete mode 100644 pkg/client/informers/externalversions/channels/v1alpha1/subscription.go delete mode 100644 pkg/client/informers/externalversions/feeds/interface.go delete mode 100644 pkg/client/informers/externalversions/feeds/v1alpha1/clustereventsource.go delete mode 100644 pkg/client/informers/externalversions/feeds/v1alpha1/clustereventtype.go delete mode 100644 pkg/client/informers/externalversions/feeds/v1alpha1/eventsource.go delete mode 100644 pkg/client/informers/externalversions/feeds/v1alpha1/eventtype.go delete mode 100644 pkg/client/informers/externalversions/feeds/v1alpha1/feed.go delete mode 100644 pkg/client/informers/externalversions/feeds/v1alpha1/interface.go delete mode 100644 pkg/client/informers/externalversions/flows/interface.go delete mode 100644 pkg/client/informers/externalversions/flows/v1alpha1/flow.go delete mode 100644 pkg/client/informers/externalversions/flows/v1alpha1/interface.go delete mode 100644 pkg/client/listers/channels/v1alpha1/bus.go delete mode 100644 pkg/client/listers/channels/v1alpha1/channel.go delete mode 100644 pkg/client/listers/channels/v1alpha1/clusterbus.go delete mode 100644 pkg/client/listers/channels/v1alpha1/expansion_generated.go delete mode 100644 pkg/client/listers/channels/v1alpha1/subscription.go delete mode 100644 pkg/client/listers/feeds/v1alpha1/clustereventsource.go delete mode 100644 pkg/client/listers/feeds/v1alpha1/clustereventtype.go delete mode 100644 pkg/client/listers/feeds/v1alpha1/eventsource.go delete mode 100644 pkg/client/listers/feeds/v1alpha1/eventtype.go delete mode 100644 pkg/client/listers/feeds/v1alpha1/expansion_generated.go delete mode 100644 pkg/client/listers/feeds/v1alpha1/feed.go delete mode 100644 pkg/client/listers/flows/v1alpha1/expansion_generated.go delete mode 100644 pkg/client/listers/flows/v1alpha1/flow.go delete mode 100644 pkg/controller/bus/controller.go delete mode 100644 pkg/controller/bus/stub_test.go delete mode 100644 pkg/controller/channel/controller.go delete mode 100644 pkg/controller/channel/stub_test.go delete mode 100644 pkg/controller/clusterbus/controller.go delete mode 100644 pkg/controller/clusterbus/stub_test.go delete mode 100644 pkg/controller/eventtype/provider.go delete mode 100644 pkg/controller/eventtype/provider_test.go delete mode 100644 pkg/controller/eventtype/reconcile.go delete mode 100644 pkg/controller/eventtype/reconcile_test.go delete mode 100644 pkg/controller/feed/errors.go delete mode 100644 pkg/controller/feed/provider.go delete mode 100644 pkg/controller/feed/reconcile.go delete mode 100644 pkg/controller/feed/reconcile_test.go delete mode 100644 pkg/controller/feed/resources/job.go delete mode 100644 pkg/controller/feed/resources/job_test.go delete mode 100644 pkg/controller/feed/resources/names.go delete mode 100644 pkg/controller/flow/dyn_client.go delete mode 100644 pkg/controller/flow/provider.go delete mode 100644 pkg/controller/flow/reconcile.go delete mode 100644 pkg/controller/flow/reconcile_test.go delete mode 100644 pkg/controller/flow/resources/channel.go delete mode 100644 pkg/controller/flow/resources/feed.go delete mode 100644 pkg/controller/flow/resources/stub_test.go delete mode 100644 pkg/controller/flow/resources/subscription.go delete mode 100644 pkg/controller/util/bus_util.go delete mode 100644 pkg/controller/util/channel_util.go delete mode 100644 pkg/controller/util/stub_test.go delete mode 100644 pkg/controller/util/subscription_util.go delete mode 100644 pkg/sources/container_event_source_job.go delete mode 100644 pkg/sources/event_source.go delete mode 100644 pkg/sources/event_source_wrapper.go delete mode 100644 pkg/sources/event_trigger.go delete mode 100644 pkg/sources/gcppubsub/eventsource.yaml delete mode 100644 pkg/sources/gcppubsub/eventtype.yaml delete mode 100644 pkg/sources/gcppubsub/gcp_pubsub.go delete mode 100644 pkg/sources/gcppubsub/googleapis-serviceentry.yaml delete mode 120000 pkg/sources/gcppubsub/kodata/LICENSE delete mode 120000 pkg/sources/gcppubsub/kodata/VENDOR-LICENSE delete mode 120000 pkg/sources/gcppubsub/receive_adapter/kodata/LICENSE delete mode 120000 pkg/sources/gcppubsub/receive_adapter/kodata/VENDOR-LICENSE delete mode 100644 pkg/sources/gcppubsub/receive_adapter/receive_adapter.go delete mode 100644 pkg/sources/gcppubsub/receive_adapter/stub_test.go delete mode 100644 pkg/sources/gcppubsub/stub_test.go delete mode 100644 pkg/sources/gcppubsub/watcher_pod.go delete mode 100644 pkg/sources/github/README.md delete mode 100644 pkg/sources/github/events.go delete mode 100644 pkg/sources/github/eventsource.yaml delete mode 100644 pkg/sources/github/eventtype.yaml delete mode 100644 pkg/sources/github/feedlet/feedlet.go delete mode 120000 pkg/sources/github/feedlet/kodata/LICENSE delete mode 120000 pkg/sources/github/feedlet/kodata/VENDOR-LICENSE delete mode 100644 pkg/sources/github/feedlet/stub_test.go delete mode 100644 pkg/sources/github/github-serviceentry.yaml delete mode 120000 pkg/sources/github/receive_adapter/kodata/LICENSE delete mode 120000 pkg/sources/github/receive_adapter/kodata/VENDOR-LICENSE delete mode 100644 pkg/sources/github/receive_adapter/receive_adapter.go delete mode 100644 pkg/sources/github/receive_adapter/stub_test.go delete mode 100644 pkg/sources/github/resources/service.go delete mode 100644 pkg/sources/github/resources/stub_test.go delete mode 100644 pkg/sources/github/stub_test.go delete mode 100644 pkg/sources/k8sevents/eventsource.yaml delete mode 100644 pkg/sources/k8sevents/eventtype.yaml delete mode 100644 pkg/sources/k8sevents/k8s_events.go delete mode 120000 pkg/sources/k8sevents/kodata/LICENSE delete mode 120000 pkg/sources/k8sevents/kodata/VENDOR-LICENSE delete mode 120000 pkg/sources/k8sevents/receive_adapter/kodata/LICENSE delete mode 120000 pkg/sources/k8sevents/receive_adapter/kodata/VENDOR-LICENSE delete mode 100644 pkg/sources/k8sevents/receive_adapter/receive_adapter.go delete mode 100644 pkg/sources/k8sevents/receive_adapter/stub_test.go delete mode 100644 pkg/sources/k8sevents/stub_test.go delete mode 100644 pkg/sources/k8sevents/watcher_pod.go delete mode 100644 pkg/sources/stub_test.go delete mode 100644 vendor/cloud.google.com/go/iam/iam.go delete mode 100644 vendor/cloud.google.com/go/internal/optional/optional.go delete mode 100644 vendor/cloud.google.com/go/internal/version/version.go delete mode 100644 vendor/cloud.google.com/go/pubsub/apiv1/doc.go delete mode 100644 vendor/cloud.google.com/go/pubsub/apiv1/path_funcs.go delete mode 100644 vendor/cloud.google.com/go/pubsub/apiv1/publisher_client.go delete mode 100644 vendor/cloud.google.com/go/pubsub/apiv1/subscriber_client.go delete mode 100644 vendor/cloud.google.com/go/pubsub/doc.go delete mode 100644 vendor/cloud.google.com/go/pubsub/flow_controller.go delete mode 100644 vendor/cloud.google.com/go/pubsub/go18.go delete mode 100644 vendor/cloud.google.com/go/pubsub/internal/distribution/distribution.go delete mode 100644 vendor/cloud.google.com/go/pubsub/iterator.go delete mode 100644 vendor/cloud.google.com/go/pubsub/message.go delete mode 100644 vendor/cloud.google.com/go/pubsub/not_go18.go delete mode 100644 vendor/cloud.google.com/go/pubsub/pubsub.go delete mode 100644 vendor/cloud.google.com/go/pubsub/pullstream.go delete mode 100644 vendor/cloud.google.com/go/pubsub/service.go delete mode 100644 vendor/cloud.google.com/go/pubsub/snapshot.go delete mode 100644 vendor/cloud.google.com/go/pubsub/subscription.go delete mode 100644 vendor/cloud.google.com/go/pubsub/topic.go delete mode 100644 vendor/github.com/Shopify/sarama/LICENSE delete mode 100644 vendor/github.com/Shopify/sarama/acl_bindings.go delete mode 100644 vendor/github.com/Shopify/sarama/acl_create_request.go delete mode 100644 vendor/github.com/Shopify/sarama/acl_create_response.go delete mode 100644 vendor/github.com/Shopify/sarama/acl_delete_request.go delete mode 100644 vendor/github.com/Shopify/sarama/acl_delete_response.go delete mode 100644 vendor/github.com/Shopify/sarama/acl_describe_request.go delete mode 100644 vendor/github.com/Shopify/sarama/acl_describe_response.go delete mode 100644 vendor/github.com/Shopify/sarama/acl_filter.go delete mode 100644 vendor/github.com/Shopify/sarama/acl_types.go delete mode 100644 vendor/github.com/Shopify/sarama/add_offsets_to_txn_request.go delete mode 100644 vendor/github.com/Shopify/sarama/add_offsets_to_txn_response.go delete mode 100644 vendor/github.com/Shopify/sarama/add_partitions_to_txn_request.go delete mode 100644 vendor/github.com/Shopify/sarama/add_partitions_to_txn_response.go delete mode 100644 vendor/github.com/Shopify/sarama/admin.go delete mode 100644 vendor/github.com/Shopify/sarama/alter_configs_request.go delete mode 100644 vendor/github.com/Shopify/sarama/alter_configs_response.go delete mode 100644 vendor/github.com/Shopify/sarama/api_versions_request.go delete mode 100644 vendor/github.com/Shopify/sarama/api_versions_response.go delete mode 100644 vendor/github.com/Shopify/sarama/async_producer.go delete mode 100644 vendor/github.com/Shopify/sarama/broker.go delete mode 100644 vendor/github.com/Shopify/sarama/client.go delete mode 100644 vendor/github.com/Shopify/sarama/config.go delete mode 100644 vendor/github.com/Shopify/sarama/config_resource_type.go delete mode 100644 vendor/github.com/Shopify/sarama/consumer.go delete mode 100644 vendor/github.com/Shopify/sarama/consumer_group_members.go delete mode 100644 vendor/github.com/Shopify/sarama/consumer_metadata_request.go delete mode 100644 vendor/github.com/Shopify/sarama/consumer_metadata_response.go delete mode 100644 vendor/github.com/Shopify/sarama/crc32_field.go delete mode 100644 vendor/github.com/Shopify/sarama/create_partitions_request.go delete mode 100644 vendor/github.com/Shopify/sarama/create_partitions_response.go delete mode 100644 vendor/github.com/Shopify/sarama/create_topics_request.go delete mode 100644 vendor/github.com/Shopify/sarama/create_topics_response.go delete mode 100644 vendor/github.com/Shopify/sarama/delete_groups_request.go delete mode 100644 vendor/github.com/Shopify/sarama/delete_groups_response.go delete mode 100644 vendor/github.com/Shopify/sarama/delete_records_request.go delete mode 100644 vendor/github.com/Shopify/sarama/delete_records_response.go delete mode 100644 vendor/github.com/Shopify/sarama/delete_topics_request.go delete mode 100644 vendor/github.com/Shopify/sarama/delete_topics_response.go delete mode 100644 vendor/github.com/Shopify/sarama/describe_configs_request.go delete mode 100644 vendor/github.com/Shopify/sarama/describe_configs_response.go delete mode 100644 vendor/github.com/Shopify/sarama/describe_groups_request.go delete mode 100644 vendor/github.com/Shopify/sarama/describe_groups_response.go delete mode 100644 vendor/github.com/Shopify/sarama/encoder_decoder.go delete mode 100644 vendor/github.com/Shopify/sarama/end_txn_request.go delete mode 100644 vendor/github.com/Shopify/sarama/end_txn_response.go delete mode 100644 vendor/github.com/Shopify/sarama/errors.go delete mode 100644 vendor/github.com/Shopify/sarama/fetch_request.go delete mode 100644 vendor/github.com/Shopify/sarama/fetch_response.go delete mode 100644 vendor/github.com/Shopify/sarama/find_coordinator_request.go delete mode 100644 vendor/github.com/Shopify/sarama/find_coordinator_response.go delete mode 100644 vendor/github.com/Shopify/sarama/heartbeat_request.go delete mode 100644 vendor/github.com/Shopify/sarama/heartbeat_response.go delete mode 100644 vendor/github.com/Shopify/sarama/init_producer_id_request.go delete mode 100644 vendor/github.com/Shopify/sarama/init_producer_id_response.go delete mode 100644 vendor/github.com/Shopify/sarama/join_group_request.go delete mode 100644 vendor/github.com/Shopify/sarama/join_group_response.go delete mode 100644 vendor/github.com/Shopify/sarama/leave_group_request.go delete mode 100644 vendor/github.com/Shopify/sarama/leave_group_response.go delete mode 100644 vendor/github.com/Shopify/sarama/length_field.go delete mode 100644 vendor/github.com/Shopify/sarama/list_groups_request.go delete mode 100644 vendor/github.com/Shopify/sarama/list_groups_response.go delete mode 100644 vendor/github.com/Shopify/sarama/message.go delete mode 100644 vendor/github.com/Shopify/sarama/message_set.go delete mode 100644 vendor/github.com/Shopify/sarama/metadata_request.go delete mode 100644 vendor/github.com/Shopify/sarama/metadata_response.go delete mode 100644 vendor/github.com/Shopify/sarama/metrics.go delete mode 100644 vendor/github.com/Shopify/sarama/mockbroker.go delete mode 100644 vendor/github.com/Shopify/sarama/mockresponses.go delete mode 100644 vendor/github.com/Shopify/sarama/offset_commit_request.go delete mode 100644 vendor/github.com/Shopify/sarama/offset_commit_response.go delete mode 100644 vendor/github.com/Shopify/sarama/offset_fetch_request.go delete mode 100644 vendor/github.com/Shopify/sarama/offset_fetch_response.go delete mode 100644 vendor/github.com/Shopify/sarama/offset_manager.go delete mode 100644 vendor/github.com/Shopify/sarama/offset_request.go delete mode 100644 vendor/github.com/Shopify/sarama/offset_response.go delete mode 100644 vendor/github.com/Shopify/sarama/packet_decoder.go delete mode 100644 vendor/github.com/Shopify/sarama/packet_encoder.go delete mode 100644 vendor/github.com/Shopify/sarama/partitioner.go delete mode 100644 vendor/github.com/Shopify/sarama/prep_encoder.go delete mode 100644 vendor/github.com/Shopify/sarama/produce_request.go delete mode 100644 vendor/github.com/Shopify/sarama/produce_response.go delete mode 100644 vendor/github.com/Shopify/sarama/produce_set.go delete mode 100644 vendor/github.com/Shopify/sarama/real_decoder.go delete mode 100644 vendor/github.com/Shopify/sarama/real_encoder.go delete mode 100644 vendor/github.com/Shopify/sarama/record.go delete mode 100644 vendor/github.com/Shopify/sarama/record_batch.go delete mode 100644 vendor/github.com/Shopify/sarama/records.go delete mode 100644 vendor/github.com/Shopify/sarama/request.go delete mode 100644 vendor/github.com/Shopify/sarama/response_header.go delete mode 100644 vendor/github.com/Shopify/sarama/sarama.go delete mode 100644 vendor/github.com/Shopify/sarama/sasl_handshake_request.go delete mode 100644 vendor/github.com/Shopify/sarama/sasl_handshake_response.go delete mode 100644 vendor/github.com/Shopify/sarama/sync_group_request.go delete mode 100644 vendor/github.com/Shopify/sarama/sync_group_response.go delete mode 100644 vendor/github.com/Shopify/sarama/sync_producer.go delete mode 100644 vendor/github.com/Shopify/sarama/timestamp.go delete mode 100644 vendor/github.com/Shopify/sarama/txn_offset_commit_request.go delete mode 100644 vendor/github.com/Shopify/sarama/txn_offset_commit_response.go delete mode 100644 vendor/github.com/Shopify/sarama/utils.go delete mode 100644 vendor/github.com/bsm/sarama-cluster/LICENSE delete mode 100644 vendor/github.com/bsm/sarama-cluster/balancer.go delete mode 100644 vendor/github.com/bsm/sarama-cluster/client.go delete mode 100644 vendor/github.com/bsm/sarama-cluster/cluster.go delete mode 100644 vendor/github.com/bsm/sarama-cluster/config.go delete mode 100644 vendor/github.com/bsm/sarama-cluster/consumer.go delete mode 100644 vendor/github.com/bsm/sarama-cluster/doc.go delete mode 100644 vendor/github.com/bsm/sarama-cluster/offsets.go delete mode 100644 vendor/github.com/bsm/sarama-cluster/partitions.go delete mode 100644 vendor/github.com/bsm/sarama-cluster/util.go delete mode 100644 vendor/github.com/eapache/go-resiliency/LICENSE delete mode 100644 vendor/github.com/eapache/go-resiliency/breaker/breaker.go delete mode 100644 vendor/github.com/eapache/go-xerial-snappy/LICENSE delete mode 100644 vendor/github.com/eapache/go-xerial-snappy/snappy.go delete mode 100644 vendor/github.com/eapache/queue/LICENSE delete mode 100644 vendor/github.com/eapache/queue/queue.go delete mode 100644 vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go delete mode 100644 vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go delete mode 100644 vendor/github.com/golang/snappy/AUTHORS delete mode 100644 vendor/github.com/golang/snappy/CONTRIBUTORS delete mode 100644 vendor/github.com/golang/snappy/LICENSE delete mode 100644 vendor/github.com/golang/snappy/decode.go delete mode 100644 vendor/github.com/golang/snappy/decode_amd64.go delete mode 100644 vendor/github.com/golang/snappy/decode_amd64.s delete mode 100644 vendor/github.com/golang/snappy/decode_other.go delete mode 100644 vendor/github.com/golang/snappy/encode.go delete mode 100644 vendor/github.com/golang/snappy/encode_amd64.go delete mode 100644 vendor/github.com/golang/snappy/encode_amd64.s delete mode 100644 vendor/github.com/golang/snappy/encode_other.go delete mode 100644 vendor/github.com/golang/snappy/snappy.go delete mode 100644 vendor/github.com/google/go-github/AUTHORS delete mode 100644 vendor/github.com/google/go-github/LICENSE delete mode 100644 vendor/github.com/google/go-github/github/activity.go delete mode 100644 vendor/github.com/google/go-github/github/activity_events.go delete mode 100644 vendor/github.com/google/go-github/github/activity_notifications.go delete mode 100644 vendor/github.com/google/go-github/github/activity_star.go delete mode 100644 vendor/github.com/google/go-github/github/activity_watching.go delete mode 100644 vendor/github.com/google/go-github/github/admin.go delete mode 100644 vendor/github.com/google/go-github/github/admin_stats.go delete mode 100644 vendor/github.com/google/go-github/github/apps.go delete mode 100644 vendor/github.com/google/go-github/github/apps_installation.go delete mode 100644 vendor/github.com/google/go-github/github/apps_marketplace.go delete mode 100644 vendor/github.com/google/go-github/github/authorizations.go delete mode 100644 vendor/github.com/google/go-github/github/doc.go delete mode 100644 vendor/github.com/google/go-github/github/event_types.go delete mode 100644 vendor/github.com/google/go-github/github/gen-accessors.go delete mode 100644 vendor/github.com/google/go-github/github/gists.go delete mode 100644 vendor/github.com/google/go-github/github/gists_comments.go delete mode 100644 vendor/github.com/google/go-github/github/git.go delete mode 100644 vendor/github.com/google/go-github/github/git_blobs.go delete mode 100644 vendor/github.com/google/go-github/github/git_commits.go delete mode 100644 vendor/github.com/google/go-github/github/git_refs.go delete mode 100644 vendor/github.com/google/go-github/github/git_tags.go delete mode 100644 vendor/github.com/google/go-github/github/git_trees.go delete mode 100644 vendor/github.com/google/go-github/github/github-accessors.go delete mode 100644 vendor/github.com/google/go-github/github/github.go delete mode 100644 vendor/github.com/google/go-github/github/gitignore.go delete mode 100644 vendor/github.com/google/go-github/github/issues.go delete mode 100644 vendor/github.com/google/go-github/github/issues_assignees.go delete mode 100644 vendor/github.com/google/go-github/github/issues_comments.go delete mode 100644 vendor/github.com/google/go-github/github/issues_events.go delete mode 100644 vendor/github.com/google/go-github/github/issues_labels.go delete mode 100644 vendor/github.com/google/go-github/github/issues_milestones.go delete mode 100644 vendor/github.com/google/go-github/github/issues_timeline.go delete mode 100644 vendor/github.com/google/go-github/github/licenses.go delete mode 100644 vendor/github.com/google/go-github/github/messages.go delete mode 100644 vendor/github.com/google/go-github/github/migrations.go delete mode 100644 vendor/github.com/google/go-github/github/migrations_source_import.go delete mode 100644 vendor/github.com/google/go-github/github/misc.go delete mode 100644 vendor/github.com/google/go-github/github/orgs.go delete mode 100644 vendor/github.com/google/go-github/github/orgs_hooks.go delete mode 100644 vendor/github.com/google/go-github/github/orgs_members.go delete mode 100644 vendor/github.com/google/go-github/github/orgs_outside_collaborators.go delete mode 100644 vendor/github.com/google/go-github/github/orgs_projects.go delete mode 100644 vendor/github.com/google/go-github/github/orgs_teams.go delete mode 100644 vendor/github.com/google/go-github/github/orgs_users_blocking.go delete mode 100644 vendor/github.com/google/go-github/github/projects.go delete mode 100644 vendor/github.com/google/go-github/github/pulls.go delete mode 100644 vendor/github.com/google/go-github/github/pulls_comments.go delete mode 100644 vendor/github.com/google/go-github/github/pulls_reviewers.go delete mode 100644 vendor/github.com/google/go-github/github/pulls_reviews.go delete mode 100644 vendor/github.com/google/go-github/github/reactions.go delete mode 100644 vendor/github.com/google/go-github/github/repos.go delete mode 100644 vendor/github.com/google/go-github/github/repos_collaborators.go delete mode 100644 vendor/github.com/google/go-github/github/repos_comments.go delete mode 100644 vendor/github.com/google/go-github/github/repos_commits.go delete mode 100644 vendor/github.com/google/go-github/github/repos_community_health.go delete mode 100644 vendor/github.com/google/go-github/github/repos_contents.go delete mode 100644 vendor/github.com/google/go-github/github/repos_deployments.go delete mode 100644 vendor/github.com/google/go-github/github/repos_forks.go delete mode 100644 vendor/github.com/google/go-github/github/repos_hooks.go delete mode 100644 vendor/github.com/google/go-github/github/repos_invitations.go delete mode 100644 vendor/github.com/google/go-github/github/repos_keys.go delete mode 100644 vendor/github.com/google/go-github/github/repos_merging.go delete mode 100644 vendor/github.com/google/go-github/github/repos_pages.go delete mode 100644 vendor/github.com/google/go-github/github/repos_projects.go delete mode 100644 vendor/github.com/google/go-github/github/repos_releases.go delete mode 100644 vendor/github.com/google/go-github/github/repos_stats.go delete mode 100644 vendor/github.com/google/go-github/github/repos_statuses.go delete mode 100644 vendor/github.com/google/go-github/github/repos_traffic.go delete mode 100644 vendor/github.com/google/go-github/github/search.go delete mode 100644 vendor/github.com/google/go-github/github/strings.go delete mode 100644 vendor/github.com/google/go-github/github/timestamp.go delete mode 100644 vendor/github.com/google/go-github/github/users.go delete mode 100644 vendor/github.com/google/go-github/github/users_administration.go delete mode 100644 vendor/github.com/google/go-github/github/users_blocking.go delete mode 100644 vendor/github.com/google/go-github/github/users_emails.go delete mode 100644 vendor/github.com/google/go-github/github/users_followers.go delete mode 100644 vendor/github.com/google/go-github/github/users_gpg_keys.go delete mode 100644 vendor/github.com/google/go-github/github/users_keys.go delete mode 100644 vendor/github.com/google/go-github/github/with_appengine.go delete mode 100644 vendor/github.com/google/go-github/github/without_appengine.go delete mode 100644 vendor/github.com/google/go-querystring/LICENSE delete mode 100644 vendor/github.com/google/go-querystring/query/encode.go delete mode 100644 vendor/github.com/googleapis/gax-go/LICENSE delete mode 100644 vendor/github.com/googleapis/gax-go/call_option.go delete mode 100644 vendor/github.com/googleapis/gax-go/gax.go delete mode 100644 vendor/github.com/googleapis/gax-go/header.go delete mode 100644 vendor/github.com/googleapis/gax-go/invoke.go delete mode 100644 vendor/github.com/pierrec/lz4/LICENSE delete mode 100644 vendor/github.com/pierrec/lz4/block.go delete mode 100644 vendor/github.com/pierrec/lz4/debug.go delete mode 100644 vendor/github.com/pierrec/lz4/debug_stub.go delete mode 100644 vendor/github.com/pierrec/lz4/internal/xxh32/xxh32.go delete mode 100644 vendor/github.com/pierrec/lz4/internal/xxh32/xxh32zero.go delete mode 100644 vendor/github.com/pierrec/lz4/lz4.go delete mode 100644 vendor/github.com/pierrec/lz4/lz4_go1.10.go delete mode 100644 vendor/github.com/pierrec/lz4/lz4_notgo1.10.go delete mode 100644 vendor/github.com/pierrec/lz4/reader.go delete mode 100644 vendor/github.com/pierrec/lz4/writer.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/LICENSE delete mode 100644 vendor/github.com/rcrowley/go-metrics/counter.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/debug.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/ewma.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/gauge.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/gauge_float64.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/graphite.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/healthcheck.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/histogram.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/json.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/log.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/meter.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/metrics.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/opentsdb.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/registry.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/runtime.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/runtime_cgo.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/sample.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/syslog.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/timer.go delete mode 100644 vendor/github.com/rcrowley/go-metrics/writer.go delete mode 100644 vendor/go.opencensus.io/exporter/stackdriver/propagation/http.go delete mode 100644 vendor/go.opencensus.io/plugin/ocgrpc/client.go delete mode 100644 vendor/go.opencensus.io/plugin/ocgrpc/client_metrics.go delete mode 100644 vendor/go.opencensus.io/plugin/ocgrpc/client_stats_handler.go delete mode 100644 vendor/go.opencensus.io/plugin/ocgrpc/doc.go delete mode 100644 vendor/go.opencensus.io/plugin/ocgrpc/server.go delete mode 100644 vendor/go.opencensus.io/plugin/ocgrpc/server_metrics.go delete mode 100644 vendor/go.opencensus.io/plugin/ocgrpc/server_stats_handler.go delete mode 100644 vendor/go.opencensus.io/plugin/ocgrpc/stats_common.go delete mode 100644 vendor/go.opencensus.io/plugin/ocgrpc/trace_common.go delete mode 100644 vendor/golang.org/x/net/internal/timeseries/timeseries.go delete mode 100644 vendor/golang.org/x/net/trace/events.go delete mode 100644 vendor/golang.org/x/net/trace/histogram.go delete mode 100644 vendor/golang.org/x/net/trace/trace.go delete mode 100644 vendor/golang.org/x/net/trace/trace_go16.go delete mode 100644 vendor/golang.org/x/net/trace/trace_go17.go delete mode 100644 vendor/golang.org/x/sync/semaphore/semaphore.go delete mode 100644 vendor/google.golang.org/api/AUTHORS delete mode 100644 vendor/google.golang.org/api/CONTRIBUTORS delete mode 100644 vendor/google.golang.org/api/LICENSE delete mode 100644 vendor/google.golang.org/api/googleapi/internal/uritemplates/LICENSE delete mode 100644 vendor/google.golang.org/api/googleapi/transport/apikey.go delete mode 100644 vendor/google.golang.org/api/internal/creds.go delete mode 100644 vendor/google.golang.org/api/internal/pool.go delete mode 100644 vendor/google.golang.org/api/internal/settings.go delete mode 100644 vendor/google.golang.org/api/iterator/iterator.go delete mode 100644 vendor/google.golang.org/api/option/credentials_go19.go delete mode 100644 vendor/google.golang.org/api/option/credentials_notgo19.go delete mode 100644 vendor/google.golang.org/api/option/option.go delete mode 100644 vendor/google.golang.org/api/support/bundler/bundler.go delete mode 100644 vendor/google.golang.org/api/transport/dial.go delete mode 100644 vendor/google.golang.org/api/transport/go19.go delete mode 100644 vendor/google.golang.org/api/transport/grpc/dial.go delete mode 100644 vendor/google.golang.org/api/transport/grpc/dial_appengine.go delete mode 100644 vendor/google.golang.org/api/transport/grpc/go18.go delete mode 100644 vendor/google.golang.org/api/transport/grpc/not_go18.go delete mode 100644 vendor/google.golang.org/api/transport/http/dial.go delete mode 100644 vendor/google.golang.org/api/transport/http/dial_appengine.go delete mode 100644 vendor/google.golang.org/api/transport/http/go18.go delete mode 100644 vendor/google.golang.org/api/transport/http/not_go18.go delete mode 100644 vendor/google.golang.org/api/transport/not_go19.go delete mode 100644 vendor/google.golang.org/appengine/internal/socket/socket_service.pb.go delete mode 100644 vendor/google.golang.org/appengine/socket/doc.go delete mode 100644 vendor/google.golang.org/appengine/socket/socket_classic.go delete mode 100644 vendor/google.golang.org/appengine/socket/socket_vm.go delete mode 100644 vendor/google.golang.org/genproto/LICENSE delete mode 100644 vendor/google.golang.org/genproto/googleapis/api/annotations/annotations.pb.go delete mode 100644 vendor/google.golang.org/genproto/googleapis/api/annotations/http.pb.go delete mode 100644 vendor/google.golang.org/genproto/googleapis/iam/v1/iam_policy.pb.go delete mode 100644 vendor/google.golang.org/genproto/googleapis/iam/v1/policy.pb.go delete mode 100644 vendor/google.golang.org/genproto/googleapis/pubsub/v1/pubsub.pb.go delete mode 100644 vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go delete mode 100644 vendor/google.golang.org/genproto/protobuf/field_mask/field_mask.pb.go delete mode 100644 vendor/google.golang.org/grpc/AUTHORS delete mode 100644 vendor/google.golang.org/grpc/LICENSE delete mode 100644 vendor/google.golang.org/grpc/backoff.go delete mode 100644 vendor/google.golang.org/grpc/balancer.go delete mode 100644 vendor/google.golang.org/grpc/balancer/balancer.go delete mode 100644 vendor/google.golang.org/grpc/balancer/base/balancer.go delete mode 100644 vendor/google.golang.org/grpc/balancer/base/base.go delete mode 100644 vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go delete mode 100644 vendor/google.golang.org/grpc/balancer_conn_wrappers.go delete mode 100644 vendor/google.golang.org/grpc/balancer_v1_wrapper.go delete mode 100644 vendor/google.golang.org/grpc/call.go delete mode 100644 vendor/google.golang.org/grpc/channelz/funcs.go delete mode 100644 vendor/google.golang.org/grpc/channelz/types.go delete mode 100644 vendor/google.golang.org/grpc/clientconn.go delete mode 100644 vendor/google.golang.org/grpc/codec.go delete mode 100644 vendor/google.golang.org/grpc/codes/code_string.go delete mode 100644 vendor/google.golang.org/grpc/codes/codes.go delete mode 100644 vendor/google.golang.org/grpc/connectivity/connectivity.go delete mode 100644 vendor/google.golang.org/grpc/credentials/credentials.go delete mode 100644 vendor/google.golang.org/grpc/credentials/credentials_util_go17.go delete mode 100644 vendor/google.golang.org/grpc/credentials/credentials_util_go18.go delete mode 100644 vendor/google.golang.org/grpc/credentials/credentials_util_pre_go17.go delete mode 100644 vendor/google.golang.org/grpc/credentials/oauth/oauth.go delete mode 100644 vendor/google.golang.org/grpc/doc.go delete mode 100644 vendor/google.golang.org/grpc/encoding/encoding.go delete mode 100644 vendor/google.golang.org/grpc/encoding/proto/proto.go delete mode 100644 vendor/google.golang.org/grpc/envconfig.go delete mode 100644 vendor/google.golang.org/grpc/go16.go delete mode 100644 vendor/google.golang.org/grpc/go17.go delete mode 100644 vendor/google.golang.org/grpc/grpclb.go delete mode 100644 vendor/google.golang.org/grpc/grpclb/grpc_lb_v1/messages/messages.pb.go delete mode 100644 vendor/google.golang.org/grpc/grpclb_picker.go delete mode 100644 vendor/google.golang.org/grpc/grpclb_remote_balancer.go delete mode 100644 vendor/google.golang.org/grpc/grpclb_util.go delete mode 100644 vendor/google.golang.org/grpc/grpclog/grpclog.go delete mode 100644 vendor/google.golang.org/grpc/grpclog/logger.go delete mode 100644 vendor/google.golang.org/grpc/grpclog/loggerv2.go delete mode 100644 vendor/google.golang.org/grpc/interceptor.go delete mode 100644 vendor/google.golang.org/grpc/internal/internal.go delete mode 100644 vendor/google.golang.org/grpc/keepalive/keepalive.go delete mode 100644 vendor/google.golang.org/grpc/metadata/metadata.go delete mode 100644 vendor/google.golang.org/grpc/naming/dns_resolver.go delete mode 100644 vendor/google.golang.org/grpc/naming/go17.go delete mode 100644 vendor/google.golang.org/grpc/naming/go18.go delete mode 100644 vendor/google.golang.org/grpc/naming/naming.go delete mode 100644 vendor/google.golang.org/grpc/peer/peer.go delete mode 100644 vendor/google.golang.org/grpc/picker_wrapper.go delete mode 100644 vendor/google.golang.org/grpc/pickfirst.go delete mode 100644 vendor/google.golang.org/grpc/proxy.go delete mode 100644 vendor/google.golang.org/grpc/resolver/dns/dns_resolver.go delete mode 100644 vendor/google.golang.org/grpc/resolver/dns/go17.go delete mode 100644 vendor/google.golang.org/grpc/resolver/dns/go18.go delete mode 100644 vendor/google.golang.org/grpc/resolver/passthrough/passthrough.go delete mode 100644 vendor/google.golang.org/grpc/resolver/resolver.go delete mode 100644 vendor/google.golang.org/grpc/resolver_conn_wrapper.go delete mode 100644 vendor/google.golang.org/grpc/rpc_util.go delete mode 100644 vendor/google.golang.org/grpc/server.go delete mode 100644 vendor/google.golang.org/grpc/service_config.go delete mode 100644 vendor/google.golang.org/grpc/stats/handlers.go delete mode 100644 vendor/google.golang.org/grpc/stats/stats.go delete mode 100644 vendor/google.golang.org/grpc/status/status.go delete mode 100644 vendor/google.golang.org/grpc/stream.go delete mode 100644 vendor/google.golang.org/grpc/tap/tap.go delete mode 100644 vendor/google.golang.org/grpc/trace.go delete mode 100644 vendor/google.golang.org/grpc/transport/bdp_estimator.go delete mode 100644 vendor/google.golang.org/grpc/transport/controlbuf.go delete mode 100644 vendor/google.golang.org/grpc/transport/flowcontrol.go delete mode 100644 vendor/google.golang.org/grpc/transport/go16.go delete mode 100644 vendor/google.golang.org/grpc/transport/go17.go delete mode 100644 vendor/google.golang.org/grpc/transport/handler_server.go delete mode 100644 vendor/google.golang.org/grpc/transport/http2_client.go delete mode 100644 vendor/google.golang.org/grpc/transport/http2_server.go delete mode 100644 vendor/google.golang.org/grpc/transport/http_util.go delete mode 100644 vendor/google.golang.org/grpc/transport/log.go delete mode 100644 vendor/google.golang.org/grpc/transport/transport.go delete mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/LICENSE delete mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/github/github.go delete mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/github/payload.go delete mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/logger.go delete mode 100644 vendor/gopkg.in/go-playground/webhooks.v3/webhooks.go diff --git a/Gopkg.lock b/Gopkg.lock index 32838a57d92..b8f23990b79 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -2,27 +2,12 @@ [[projects]] - digest = "1:97bdeb31587a8bee3e13eb4519db4cc592bff017632bb07cbcd7c02925d8d621" + digest = "1:f8ad8a53fa865a70efbe215b0ca34735523f50ea39e0efde319ab6fc80089b44" name = "cloud.google.com/go" - packages = [ - "compute/metadata", - "iam", - "internal/optional", - "internal/version", - "pubsub", - "pubsub/apiv1", - "pubsub/internal/distribution", - ] + packages = ["compute/metadata"] pruneopts = "NUT" revision = "90f2606161ee6a14efe2ca79fc05ac2b8efe250b" -[[projects]] - digest = "1:f2bb07cb70ceaecfffa034919e418793eef0960480474c276173b725449fdb1f" - name = "github.com/Shopify/sarama" - packages = ["."] - pruneopts = "NUT" - revision = "46cf3e2cf1acef7876068f66cf69ec31aad2d0b2" - [[projects]] branch = "master" digest = "1:707ebe952a8b3d00b343c01536c79c73771d100f63ec6babeaed5c79e2b8a8dd" @@ -31,14 +16,6 @@ pruneopts = "NUT" revision = "3a771d992973f24aa725d07868b467d1ddfceafb" -[[projects]] - digest = "1:4fb088ed7f384178cfc4552661e280a12ccc93be7f30a1ca994958a61a8e1d13" - name = "github.com/bsm/sarama-cluster" - packages = ["."] - pruneopts = "NUT" - revision = "cf455bc755fe41ac9bb2861e7a961833d9c2ecc3" - version = "v2.1.13" - [[projects]] digest = "1:a2c1d0e43bd3baaa071d1b9ed72c27d78169b2b269f71c105ac4ba34b1be4a39" name = "github.com/davecgh/go-spew" @@ -47,30 +24,6 @@ revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" -[[projects]] - digest = "1:08143362be979b087c2c1bae5dde986e988d3d5d4dc661727cbe436411b3f33a" - name = "github.com/eapache/go-resiliency" - packages = ["breaker"] - pruneopts = "NUT" - revision = "ea41b0fad31007accc7f806884dcdf3da98b79ce" - version = "v1.1.0" - -[[projects]] - branch = "master" - digest = "1:0b70d299db64766a4ee7128f0f94944f2356ca9d3499c79123dbc3a1d2ba803e" - name = "github.com/eapache/go-xerial-snappy" - packages = ["."] - pruneopts = "NUT" - revision = "bb955e01b9346ac19dc29eb16586c90ded99a98c" - -[[projects]] - digest = "1:0d36a2b325b9e75f8057f7f9fbe778d348d70ba652cb9335485b69d1a5c4e038" - name = "github.com/eapache/queue" - packages = ["."] - pruneopts = "NUT" - revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98" - version = "v1.1.0" - [[projects]] digest = "1:32598368f409bbee79deb9d43569fcd92b9fb27f39155f5e166b3371217f051f" name = "github.com/evanphx/json-patch" @@ -147,29 +100,19 @@ revision = "24b0969c4cb722950103eed87108c8d291a8df00" [[projects]] - digest = "1:0f7f0d9512487860d967bd31b4a9668316e53630fd71cb57a84ccf97c852df84" + digest = "1:03e14cff610a8a58b774e36bd337fa979482be86aab01be81fb8bbd6d0f07fc8" name = "github.com/golang/protobuf" packages = [ "proto", - "protoc-gen-go/descriptor", "ptypes", "ptypes/any", "ptypes/duration", - "ptypes/empty", "ptypes/timestamp", ] pruneopts = "NUT" revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" version = "v1.1.0" -[[projects]] - branch = "master" - digest = "1:7f114b78210bf5b75f307fc97cff293633c835bab1e0ea8a744a44b39c042dfe" - name = "github.com/golang/snappy" - packages = ["."] - pruneopts = "NUT" - revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" - [[projects]] branch = "master" digest = "1:245bd4eb633039cd66106a5d340ae826d87f4e36a8602fcc940e14176fd26ea7" @@ -192,22 +135,6 @@ revision = "3af367b6b30c263d47e8895973edcca9a49cf029" version = "v0.2.0" -[[projects]] - digest = "1:51bee9f1987dcdb9f9a1b4c20745d78f6bf6f5f14ad4e64ca883eb64df4c0045" - name = "github.com/google/go-github" - packages = ["github"] - pruneopts = "NUT" - revision = "e48060a28fac52d0f1cb758bc8b87c07bac4a87d" - version = "v15.0.0" - -[[projects]] - branch = "master" - digest = "1:a63cff6b5d8b95638bfe300385d93b2a6d9d687734b863da8e09dc834510a690" - name = "github.com/google/go-querystring" - packages = ["query"] - pruneopts = "NUT" - revision = "53e6ce116135b80d037921a7fdd5138cf32d7a8a" - [[projects]] branch = "master" digest = "1:52c5834e2bebac9030c97cc0798ac11c3aa8a39f098aeb419f142533da6cd3cc" @@ -224,14 +151,6 @@ revision = "064e2069ce9c359c118179501254f67d7d37ba24" version = "0.2" -[[projects]] - digest = "1:fe852c57b4fc4d11e6ef79bce1e930ee2f2f7d148b370afef8f8d012a80960ea" - name = "github.com/googleapis/gax-go" - packages = ["."] - pruneopts = "NUT" - revision = "317e0006254c44a0ac427cc52a0e083ff0b9622f" - version = "v2.0.0" - [[projects]] digest = "1:06a7dadb7b760767341ffb6c8d377238d68a1226f2b21b5d497d2e3f6ecf6b4e" name = "github.com/googleapis/gnostic" @@ -439,17 +358,6 @@ revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" version = "v2.0.1" -[[projects]] - digest = "1:43830c167ef48755903a1bb81e37f73b8f522c5fbecc80f269e2e25d741aa8ee" - name = "github.com/pierrec/lz4" - packages = [ - ".", - "internal/xxh32", - ] - pruneopts = "NUT" - revision = "6b9367c9ff401dbc54fabce3fb8d972e799b702d" - version = "v2.0.2" - [[projects]] digest = "1:03bca087b180bf24c4f9060775f137775550a0834e18f0bca0520a868679dbd7" name = "github.com/prometheus/client_golang" @@ -494,14 +402,6 @@ pruneopts = "NUT" revision = "94663424ae5ae9856b40a9f170762b4197024661" -[[projects]] - branch = "master" - digest = "1:7c522337040d4ec9a136cd9d64fe4677ee1d3eae4a7f8831c2108f9bec43fa48" - name = "github.com/rcrowley/go-metrics" - packages = ["."] - pruneopts = "NUT" - revision = "e2704e165165ec55d062f5919b4b29494e9fa790" - [[projects]] digest = "1:15e5c398fbd9d2c439b635a08ac161b13d04f0c2aa587fe256b65dc0c3efe8b7" name = "github.com/spf13/pflag" @@ -510,13 +410,11 @@ revision = "583c0c0531f06d5278b7d917446061adc344b5cd" [[projects]] - digest = "1:773b6907f497ea5abddc9ee90cdde179b119c0c8a04b0f02cb2fff955ed8cab4" + digest = "1:e4faec5275202abbc4e023e0e8930a9acf43194563d2032cdffa41940650f3e8" name = "go.opencensus.io" packages = [ - "exporter/stackdriver/propagation", "internal", "internal/tagencoding", - "plugin/ocgrpc", "plugin/ochttp", "plugin/ochttp/propagation/b3", "stats", @@ -572,7 +470,7 @@ [[projects]] branch = "master" - digest = "1:7e7c436f75db05dc112521a34811f383e5656abd083f678c5a6df2bf42ea6b2c" + digest = "1:e51ab843adff8d5de8889d7ad0488df404af09df8926e3e5a7b5ee1ef53af988" name = "golang.org/x/net" packages = [ "context", @@ -581,8 +479,6 @@ "http2", "http2/hpack", "idna", - "internal/timeseries", - "trace", ] pruneopts = "NUT" revision = "1e491301e022f8f977054da4c2d852decd59571f" @@ -602,12 +498,9 @@ [[projects]] branch = "master" - digest = "1:c313aef534e493304f3666fbd24dca5932ebf776a82b7a40f961c9355794a1b1" + digest = "1:39ebcc2b11457b703ae9ee2e8cca0f68df21969c6102cb3b705f76cca0ea0239" name = "golang.org/x/sync" - packages = [ - "errgroup", - "semaphore", - ] + packages = ["errgroup"] pruneopts = "NUT" revision = "1d60e4601c6fd243af51cc01ddf169918a5407ca" @@ -666,24 +559,7 @@ revision = "a5b4c53f6e8bdcafa95a94671bf2d1203365858b" [[projects]] - branch = "master" - digest = "1:cbb9ee36efb225152642c14192700636ae231d632315179129d79d2b6391acf9" - name = "google.golang.org/api" - packages = [ - "googleapi/transport", - "internal", - "iterator", - "option", - "support/bundler", - "transport", - "transport/grpc", - "transport/http", - ] - pruneopts = "NUT" - revision = "f71c6d4abd9757df4168c39856698c341d07251f" - -[[projects]] - digest = "1:626ac4e70ef18262989f8c52503259109e1a2e5580d23aeae0f0e0349819dade" + digest = "1:7206d98ec77c90c72ec2c405181a1dcf86965803b6dbc4f98ceab7a5047c37a9" name = "google.golang.org/appengine" packages = [ ".", @@ -694,74 +570,13 @@ "internal/log", "internal/modules", "internal/remote_api", - "internal/socket", "internal/urlfetch", - "socket", "urlfetch", ] pruneopts = "NUT" revision = "150dc57a1b433e64154302bdc40b6bb8aefa313a" version = "v1.0.0" -[[projects]] - branch = "master" - digest = "1:94bda8ad0190a989ad7768ce0df06e4c384a8fa7bca1cf0ce63501a4b26fe51e" - name = "google.golang.org/genproto" - packages = [ - "googleapis/api/annotations", - "googleapis/iam/v1", - "googleapis/pubsub/v1", - "googleapis/rpc/status", - "protobuf/field_mask", - ] - pruneopts = "NUT" - revision = "4065a77fc542a455295382a23a996a08ed18813a" - -[[projects]] - digest = "1:60d6a8209da1f48bd268d21ea37ddf8936913ee0b35a6eacf741e1bb8791ae5d" - name = "google.golang.org/grpc" - packages = [ - ".", - "balancer", - "balancer/base", - "balancer/roundrobin", - "channelz", - "codes", - "connectivity", - "credentials", - "credentials/oauth", - "encoding", - "encoding/proto", - "grpclb/grpc_lb_v1/messages", - "grpclog", - "internal", - "keepalive", - "metadata", - "naming", - "peer", - "resolver", - "resolver/dns", - "resolver/passthrough", - "stats", - "status", - "tap", - "transport", - ] - pruneopts = "NUT" - revision = "41344da2231b913fa3d983840a57a6b1b7b631a1" - version = "v1.12.0" - -[[projects]] - digest = "1:c776b8566e3415e9a434756ea1d3b96a3059be221555ff177666ba9edb2a38b5" - name = "gopkg.in/go-playground/webhooks.v3" - packages = [ - ".", - "github", - ] - pruneopts = "NUT" - revision = "be944ed461fc75c52f0f149acfa011f8da721f6c" - version = "v3.12.0" - [[projects]] digest = "1:2d1fbdc6777e5408cabeb02bf336305e724b925ff4546ded0fa8715a7267922a" name = "gopkg.in/inf.v0" @@ -1104,14 +919,10 @@ analyzer-name = "dep" analyzer-version = 1 input-imports = [ - "cloud.google.com/go/pubsub", - "github.com/Shopify/sarama", - "github.com/bsm/sarama-cluster", "github.com/fsnotify/fsnotify", "github.com/golang/glog", "github.com/google/go-cmp/cmp", "github.com/google/go-cmp/cmp/cmpopts", - "github.com/google/go-github/github", "github.com/google/uuid", "github.com/knative/eventing-sources/pkg/apis/sources/v1alpha1", "github.com/knative/eventing-sources/pkg/client/clientset/versioned", @@ -1121,7 +932,6 @@ "github.com/knative/pkg/apis/istio/v1alpha3", "github.com/knative/pkg/client/clientset/versioned", "github.com/knative/pkg/client/informers/externalversions", - "github.com/knative/pkg/client/listers/istio/v1alpha3", "github.com/knative/pkg/cloudevents", "github.com/knative/pkg/configmap", "github.com/knative/pkg/logging", @@ -1139,16 +949,8 @@ "go.uber.org/atomic", "go.uber.org/zap", "go.uber.org/zap/zapcore", - "golang.org/x/net/context", - "golang.org/x/oauth2", "golang.org/x/sync/errgroup", - "google.golang.org/grpc/codes", - "google.golang.org/grpc/status", - "gopkg.in/go-playground/webhooks.v3", - "gopkg.in/go-playground/webhooks.v3/github", "gopkg.in/yaml.v2", - "k8s.io/api/apps/v1", - "k8s.io/api/batch/v1", "k8s.io/api/core/v1", "k8s.io/api/rbac/v1beta1", "k8s.io/apimachinery/pkg/api/equality", @@ -1162,10 +964,8 @@ "k8s.io/apimachinery/pkg/runtime/serializer", "k8s.io/apimachinery/pkg/types", "k8s.io/apimachinery/pkg/util/intstr", - "k8s.io/apimachinery/pkg/util/runtime", "k8s.io/apimachinery/pkg/util/sets", "k8s.io/apimachinery/pkg/util/sets/types", - "k8s.io/apimachinery/pkg/util/validation", "k8s.io/apimachinery/pkg/util/wait", "k8s.io/apimachinery/pkg/util/yaml", "k8s.io/apimachinery/pkg/watch", @@ -1174,13 +974,8 @@ "k8s.io/client-go/dynamic", "k8s.io/client-go/dynamic/fake", "k8s.io/client-go/informers", - "k8s.io/client-go/informers/core/v1", "k8s.io/client-go/kubernetes", "k8s.io/client-go/kubernetes/scheme", - "k8s.io/client-go/kubernetes/typed/core/v1", - "k8s.io/client-go/listers/apps/v1", - "k8s.io/client-go/listers/core/v1", - "k8s.io/client-go/listers/rbac/v1beta1", "k8s.io/client-go/plugin/pkg/client/auth/gcp", "k8s.io/client-go/rest", "k8s.io/client-go/testing", @@ -1188,7 +983,6 @@ "k8s.io/client-go/tools/clientcmd", "k8s.io/client-go/tools/record", "k8s.io/client-go/util/flowcontrol", - "k8s.io/client-go/util/workqueue", "k8s.io/code-generator/cmd/client-gen", "k8s.io/code-generator/cmd/deepcopy-gen", "k8s.io/code-generator/cmd/defaulter-gen", diff --git a/Gopkg.toml b/Gopkg.toml index 159890fe31a..bb939be2027 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -70,10 +70,6 @@ required = [ name = "golang.org/x/oauth2" revision = "cdc340f7c179dbbfa4afd43b7614e8fcadde4269" -[[override]] - name = "cloud.google.com/go" - revision = "90f2606161ee6a14efe2ca79fc05ac2b8efe250b" - [[override]] name = "github.com/knative/pkg" # HEAD as of 2018-11-02 @@ -83,15 +79,6 @@ required = [ name = "github.com/knative/serving" version = "v0.1.1" -[[override]] - name = "github.com/Shopify/sarama" - revision = "46cf3e2cf1acef7876068f66cf69ec31aad2d0b2" # includes higher level admin client -# version = "1.17.0" - -[[constraint]] - name = "github.com/bsm/sarama-cluster" - version = "2.1.13" - [[constraint]] name = "sigs.k8s.io/controller-runtime" # HEAD as of 2018-09-19 diff --git a/cmd/controller/controller-runtime-main.go b/cmd/controller/controller-runtime-main.go index bac956c5037..f36858b6482 100644 --- a/cmd/controller/controller-runtime-main.go +++ b/cmd/controller/controller-runtime-main.go @@ -16,19 +16,12 @@ limitations under the License. package main import ( - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - flowsv1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - "github.com/knative/eventing/pkg/controller/eventing/subscription" - "github.com/knative/eventing/pkg/controller/feed" - "github.com/knative/eventing/pkg/controller/flow" - "go.uber.org/zap" "strings" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "github.com/knative/eventing/pkg/controller/eventing/subscription" istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" - - "github.com/knative/eventing/pkg/controller/eventtype" + "go.uber.org/zap" "k8s.io/apimachinery/pkg/runtime" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" "sigs.k8s.io/controller-runtime/pkg/client/config" @@ -65,9 +58,6 @@ func controllerRuntimeStart(logger *zap.SugaredLogger, experimental string) erro // Add custom types to this array to get them into the manager's scheme. schemeFuncs := []SchemeFunc{ - channelsv1alpha1.AddToScheme, - feedsv1alpha1.AddToScheme, - flowsv1alpha1.AddToScheme, istiov1alpha3.AddToScheme, eventingv1alpha1.AddToScheme, } @@ -77,12 +67,7 @@ func controllerRuntimeStart(logger *zap.SugaredLogger, experimental string) erro // Add each controller's ProvideController func to this list to have the // manager run it. - providers := []ProvideFunc{ - eventtype.ProvideController, - feed.ProvideController, - flow.ProvideController, - } - + providers := []ProvideFunc{} providers = append(providers, getExperimentalControllers(logger, experimental)...) for _, provider := range providers { diff --git a/cmd/controller/main.go b/cmd/controller/main.go index 16c1678fabf..21ab69c5221 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -23,31 +23,19 @@ import ( "net/http" "time" - controllerruntime "sigs.k8s.io/controller-runtime/pkg/client/config" - - kubeinformers "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - // Uncomment the following line to load the gcp plugin (only required to authenticate against GKE clusters). _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" - clientset "github.com/knative/eventing/pkg/client/clientset/versioned" - informers "github.com/knative/eventing/pkg/client/informers/externalversions" - "github.com/knative/eventing/pkg/controller" - "github.com/knative/eventing/pkg/controller/bus" - "github.com/knative/eventing/pkg/controller/channel" - "github.com/knative/eventing/pkg/controller/clusterbus" - sharedclientset "github.com/knative/pkg/client/clientset/versioned" - sharedinformers "github.com/knative/pkg/client/informers/externalversions" - "github.com/knative/pkg/signals" - "github.com/knative/eventing/pkg/logconfig" "github.com/knative/eventing/pkg/system" "github.com/knative/pkg/configmap" "github.com/knative/pkg/logging" "github.com/knative/pkg/logging/logkey" + "github.com/knative/pkg/signals" "github.com/prometheus/client_golang/prometheus/promhttp" "go.uber.org/zap" + "k8s.io/client-go/kubernetes" + controllerruntime "sigs.k8s.io/controller-runtime/pkg/client/config" ) const ( @@ -79,6 +67,7 @@ func main() { // set up signals so we handle the first shutdown signal gracefully stopCh := signals.SetupSignalHandler() + cfg, err := controllerruntime.GetConfig() if err != nil { logger.Fatalf("Error building kubeconfig: %v", err) @@ -89,20 +78,6 @@ func main() { logger.Fatalf("Error building kubernetes clientset: %v", err) } - client, err := clientset.NewForConfig(cfg) - if err != nil { - logger.Fatalf("Error building clientset: %v", err) - } - - sharedClient, err := sharedclientset.NewForConfig(cfg) - if err != nil { - logger.Fatalf("Error building shared clientset: %v", err) - } - - kubeInformerFactory := kubeinformers.NewSharedInformerFactory(kubeClient, time.Second*30) - informerFactory := informers.NewSharedInformerFactory(client, time.Second*30) - sharedInformerFactory := sharedinformers.NewSharedInformerFactory(sharedClient, time.Second*30) - // Watch the logging config map and dynamically update logging levels. configMapWatcher := configmap.NewInformedWatcher(kubeClient, system.Namespace) configMapWatcher.Watch(logconfig.ConfigName, logging.UpdateLevelFromConfigMap(logger, atomicLevel, logconfig.Controller, logconfig.Controller)) @@ -110,37 +85,6 @@ func main() { logger.Fatalf("failed to start controller config map watcher: %v", err) } - // Add new controllers here, except controllers that use controller-runtime. - // Those should be added to controller-runtime-main.go. - ctors := []controller.Constructor{ - bus.NewController, - clusterbus.NewController, - channel.NewController, - } - - // TODO(n3wscott): Send the logger to the controllers. - // Build all of our controllers, with the clients constructed above. - controllers := make([]controller.Interface, 0, len(ctors)) - for _, ctor := range ctors { - controllers = append(controllers, - ctor(kubeClient, client, sharedClient, cfg, kubeInformerFactory, informerFactory, sharedInformerFactory)) - } - - go kubeInformerFactory.Start(stopCh) - go informerFactory.Start(stopCh) - go sharedInformerFactory.Start(stopCh) - - // Start all of the controllers. - for _, ctrlr := range controllers { - go func(ctrlr controller.Interface) { - // We don't expect this to return until stop is called, - // but if it does, propagate it back. - if err := ctrlr.Run(threadsPerController, stopCh); err != nil { - logger.Fatalf("Error running controller: %v", err) - } - }(ctrlr) - } - // Start the controller-runtime controllers. go func() { if err := controllerRuntimeStart(logger, experimentalControllers); err != nil { diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go index 0a0c21af2a4..03dc3845a0b 100644 --- a/cmd/webhook/main.go +++ b/cmd/webhook/main.go @@ -27,10 +27,7 @@ import ( "github.com/knative/pkg/signals" "github.com/knative/pkg/webhook" - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - flowsv1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" "github.com/knative/eventing/pkg/logconfig" "github.com/knative/eventing/pkg/system" @@ -93,22 +90,6 @@ func main() { eventingv1alpha1.SchemeGroupVersion.WithKind("Channel"): &eventingv1alpha1.Channel{}, eventingv1alpha1.SchemeGroupVersion.WithKind("ClusterChannelProvisioner"): &eventingv1alpha1.ClusterChannelProvisioner{}, eventingv1alpha1.SchemeGroupVersion.WithKind("Subscription"): &eventingv1alpha1.Subscription{}, - - // For group channels.knative.dev, - channelsv1alpha1.SchemeGroupVersion.WithKind("Bus"): &channelsv1alpha1.Bus{}, - channelsv1alpha1.SchemeGroupVersion.WithKind("ClusterBus"): &channelsv1alpha1.ClusterBus{}, - channelsv1alpha1.SchemeGroupVersion.WithKind("Channel"): &channelsv1alpha1.Channel{}, - channelsv1alpha1.SchemeGroupVersion.WithKind("Subscription"): &channelsv1alpha1.Subscription{}, - - // For group feeds.knative.dev, - feedsv1alpha1.SchemeGroupVersion.WithKind("EventSource"): &feedsv1alpha1.EventSource{}, - feedsv1alpha1.SchemeGroupVersion.WithKind("ClusterEventSource"): &feedsv1alpha1.ClusterEventSource{}, - feedsv1alpha1.SchemeGroupVersion.WithKind("EventType"): &feedsv1alpha1.EventType{}, - feedsv1alpha1.SchemeGroupVersion.WithKind("ClusterEventType"): &feedsv1alpha1.ClusterEventType{}, - feedsv1alpha1.SchemeGroupVersion.WithKind("Feed"): &feedsv1alpha1.Feed{}, - - // For group flows.knative.dev, - flowsv1alpha1.SchemeGroupVersion.WithKind("Flow"): &flowsv1alpha1.Flow{}, }, Logger: logger, } diff --git a/config/200-clusterrole.yaml b/config/200-clusterrole.yaml deleted file mode 100644 index d9700d299f9..00000000000 --- a/config/200-clusterrole.yaml +++ /dev/null @@ -1,27 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -kind: ClusterRole -apiVersion: rbac.authorization.k8s.io/v1 -metadata: - name: knative-channels-bus -rules: -- apiGroups: ["channels.knative.dev"] - resources: ["buses", "clusterbuses"] - verbs: ["get", "watch", "list"] -- apiGroups: ["channels.knative.dev"] - resources: ["channels", "subscriptions"] - verbs: ["get", "watch", "list", "update", "patch"] -- apiGroups: [""] - resources: ["events"] - verbs: ["create", "patch"] diff --git a/config/200-serviceaccount.yaml b/config/200-serviceaccount.yaml index 7fcb5bdca24..994c753bb0f 100644 --- a/config/200-serviceaccount.yaml +++ b/config/200-serviceaccount.yaml @@ -16,10 +16,3 @@ kind: ServiceAccount metadata: name: eventing-controller namespace: knative-eventing - ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: bus-operator - namespace: knative-eventing diff --git a/config/201-clusterrolebinding.yaml b/config/201-clusterrolebinding.yaml index df10a93d2e8..ddf212a4cac 100644 --- a/config/201-clusterrolebinding.yaml +++ b/config/201-clusterrolebinding.yaml @@ -23,17 +23,3 @@ roleRef: kind: ClusterRole name: cluster-admin apiGroup: rbac.authorization.k8s.io - ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRoleBinding -metadata: - name: bus-operator-manage -subjects: - - kind: ServiceAccount - name: bus-operator - namespace: knative-eventing -roleRef: - kind: ClusterRole - name: knative-channels-bus - apiGroup: rbac.authorization.k8s.io diff --git a/config/300-bus.yaml b/config/300-bus.yaml deleted file mode 100644 index 3aa3d9bc245..00000000000 --- a/config/300-bus.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: buses.channels.knative.dev -spec: - group: channels.knative.dev - version: v1alpha1 - names: - kind: Bus - plural: buses - singular: bus - categories: - - all - - knative - - kbus - - eventing - scope: Namespaced diff --git a/config/300-channel.yaml b/config/300-channel.yaml index d5ddc7f370c..add65209481 100644 --- a/config/300-channel.yaml +++ b/config/300-channel.yaml @@ -14,9 +14,9 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: - name: channels.channels.knative.dev + name: channels.eventing.knative.dev spec: - group: channels.knative.dev + group: eventing.knative.dev version: v1alpha1 names: kind: Channel diff --git a/config/300-channeleventing.yaml b/config/300-channeleventing.yaml deleted file mode 100644 index add65209481..00000000000 --- a/config/300-channeleventing.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: channels.eventing.knative.dev -spec: - group: eventing.knative.dev - version: v1alpha1 - names: - kind: Channel - plural: channels - singular: channel - categories: - - all - - knative - - eventing - shortNames: - - chan - scope: Namespaced diff --git a/config/300-clusterbus.yaml b/config/300-clusterbus.yaml deleted file mode 100644 index 1c3d290b3e4..00000000000 --- a/config/300-clusterbus.yaml +++ /dev/null @@ -1,32 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: clusterbuses.channels.knative.dev -spec: - group: channels.knative.dev - version: v1alpha1 - names: - kind: ClusterBus - plural: clusterbuses - singular: clusterbus - categories: - - all - - knative - - kbus - - eventing - shortNames: - - cbus - scope: Cluster diff --git a/config/300-clustereventsource.yaml b/config/300-clustereventsource.yaml deleted file mode 100644 index 8537e37c1ed..00000000000 --- a/config/300-clustereventsource.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: clustereventsources.feeds.knative.dev -spec: - group: feeds.knative.dev - version: v1alpha1 - names: - kind: ClusterEventSource - plural: clustereventsources - singular: clustereventsource - categories: - - all - - knative - - keventsource - - eventing - scope: Cluster diff --git a/config/300-clustereventtype.yaml b/config/300-clustereventtype.yaml deleted file mode 100644 index 2aeeac477c0..00000000000 --- a/config/300-clustereventtype.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: clustereventtypes.feeds.knative.dev -spec: - group: feeds.knative.dev - version: v1alpha1 - names: - kind: ClusterEventType - plural: clustereventtypes - singular: clustereventsource - categories: - - all - - knative - - keventtype - - eventing - scope: Cluster diff --git a/config/300-eventsource.yaml b/config/300-eventsource.yaml deleted file mode 100644 index e03079fe292..00000000000 --- a/config/300-eventsource.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: eventsources.feeds.knative.dev -spec: - group: feeds.knative.dev - version: v1alpha1 - names: - kind: EventSource - plural: eventsources - singular: eventsource - categories: - - all - - knative - - keventsource - - eventing - scope: Namespaced diff --git a/config/300-eventtype.yaml b/config/300-eventtype.yaml deleted file mode 100644 index ce2f4c8aa24..00000000000 --- a/config/300-eventtype.yaml +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: eventtypes.feeds.knative.dev -spec: - group: feeds.knative.dev - version: v1alpha1 - names: - kind: EventType - plural: eventtypes - singular: eventtype - categories: - - all - - knative - - keventtype - - eventing - scope: Namespaced diff --git a/config/300-feed.yaml b/config/300-feed.yaml deleted file mode 100644 index 4fdcc0d5d90..00000000000 --- a/config/300-feed.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: feeds.feeds.knative.dev -spec: - group: feeds.knative.dev - version: v1alpha1 - names: - kind: Feed - plural: feeds - singular: feed - categories: - - all - - knative - - eventing - scope: Namespaced diff --git a/config/300-flow.yaml b/config/300-flow.yaml deleted file mode 100644 index 3d699bb70e3..00000000000 --- a/config/300-flow.yaml +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: flows.flows.knative.dev -spec: - group: flows.knative.dev - version: v1alpha1 - names: - kind: Flow - plural: flows - singular: flow - categories: - - all - - knative - - eventing - scope: Namespaced diff --git a/config/300-subscription.yaml b/config/300-subscription.yaml index 94ac409e743..cc974f51254 100644 --- a/config/300-subscription.yaml +++ b/config/300-subscription.yaml @@ -14,9 +14,9 @@ apiVersion: apiextensions.k8s.io/v1beta1 kind: CustomResourceDefinition metadata: - name: subscriptions.channels.knative.dev + name: subscriptions.eventing.knative.dev spec: - group: channels.knative.dev + group: eventing.knative.dev version: v1alpha1 names: kind: Subscription diff --git a/config/300-subscriptioneventing.yaml b/config/300-subscriptioneventing.yaml deleted file mode 100644 index cc974f51254..00000000000 --- a/config/300-subscriptioneventing.yaml +++ /dev/null @@ -1,31 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: subscriptions.eventing.knative.dev -spec: - group: eventing.knative.dev - version: v1alpha1 - names: - kind: Subscription - plural: subscriptions - singular: subscription - categories: - - all - - knative - - eventing - shortNames: - - sub - scope: Namespaced diff --git a/config/400-flow-controller-config.yaml b/config/400-flow-controller-config.yaml deleted file mode 100644 index a3b3e56e0e4..00000000000 --- a/config/400-flow-controller-config.yaml +++ /dev/null @@ -1,21 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://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. -apiVersion: v1 -kind: ConfigMap -metadata: - name: flow-controller-config - namespace: knative-eventing -data: - # Configuration for the flow controller - default-cluster-bus: stub diff --git a/config/buses/gcppubsub/README.md b/config/buses/gcppubsub/README.md deleted file mode 100644 index ca07926534c..00000000000 --- a/config/buses/gcppubsub/README.md +++ /dev/null @@ -1,29 +0,0 @@ -# GCP Cloud Pub/Sub - Knative Bus - -Deployment steps: -1. Setup [Knative Eventing](../../../DEVELOPMENT.md) -1. [Create a service account](https://console.cloud.google.com/iam-admin/serviceaccounts/project) with the 'Pub/Sub Editor' role, and download a new JSON private key. -1. Create a secret for the downloaded key `kubectl -n knative-eventing create secret generic gcppubsub-bus-key --from-file=key.json=PATH-TO-KEY-FILE.json` -1. Configure the bus, replacing `$PROJECT_ID` with your GCP Project ID, `kubectl -n knative-eventing create configmap gcppubsub-bus-config --from-literal=GOOGLE_CLOUD_PROJECT=$PROJECT_ID` -1. For cluster wide deployment, change the kind in `config/buses/gcppubsub/gcppubsub-bus.yaml` from `Bus` to `ClusterBus`. -1. Apply the 'gcppubsub' Bus `ko apply -f config/buses/gcppubsub/` -1. If you want to set the default Knative Bus to GCP Cloud Pub/Sub run the following command to edit the Knative Eventing configuration (requires the above change in kind from `Bus` to `ClusterBus`): - ```shell - kubectl get cm flow-controller-config -n knative-eventing -oyaml \ - | sed -e 's/default-cluster-bus: stub/ default-cluster-bus: gcppubsub/' \ - | kubectl replace -f - - ``` -1. Create Channels that reference the 'gcppubsub' Bus -1. (Optional) Install [Kail](https://github.com/boz/kail) - Kubernetes tail - -The bus has an independent provisioner and dispatcher. - -The provisioner will create GCP Pub/Sub Topics and Subscriptions for each Knative Channel and Subscription (respectively) targeting the Bus. Clients should avoid interacting with topics and subscriptions provisioned by the Bus. - -The dispatcher receives events via a Channel's Service from inside the cluster and sends them to the Pub/Sub Topic. Events on the Pub/Sub topic for an active subscription are forwarded via HTTP to the subscribers. HTTP responses with a 2xx status code are ack'ed while all other status codes will nack the event, delivery will be reattempted up to the limits defined by Cloud Pub/Sub. - -Note: Cloud Pub/Sub does not guarantee exactly once delivery, subscribers must guard against multiple deliveries of the same event. - -To view logs: -- for the dispatcher `kail -d gcppubsub-[namespace]-bus-dispatcher -c dispatcher` -- for the provisioner `kail -d gcppubsub-[namespace]-bus-provisioner -c provisioner` diff --git a/config/buses/gcppubsub/gcppubsub-bus.yaml b/config/buses/gcppubsub/gcppubsub-bus.yaml deleted file mode 100644 index 3921ba6a94e..00000000000 --- a/config/buses/gcppubsub/gcppubsub-bus.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: channels.knative.dev/v1alpha1 -kind: Bus -metadata: - name: gcppubsub -spec: - provisioner: - name: provisioner - image: github.com/knative/eventing/pkg/buses/gcppubsub/provisioner - args: [ - "-logtostderr", - "-stderrthreshold", "INFO", - ] - env: &env - - name: GOOGLE_CLOUD_PROJECT - valueFrom: - configMapKeyRef: - name: gcppubsub-bus-config - key: GOOGLE_CLOUD_PROJECT - - name: GOOGLE_APPLICATION_CREDENTIALS - value: /var/secrets/google/key.json - volumeMounts: - - name: google-cloud-key - mountPath: /var/secrets/google - dispatcher: - name: dispatcher - image: github.com/knative/eventing/pkg/buses/gcppubsub/dispatcher - args: [ - "-logtostderr", - "-stderrthreshold", "INFO", - ] - env: *env - volumeMounts: - - name: google-cloud-key - mountPath: /var/secrets/google - volumes: - - name: google-cloud-key - secret: - secretName: gcppubsub-bus-key diff --git a/config/buses/gcppubsub/gcppubsub-serviceentry.yaml b/config/buses/gcppubsub/gcppubsub-serviceentry.yaml deleted file mode 100644 index ee82c10bc12..00000000000 --- a/config/buses/gcppubsub/gcppubsub-serviceentry.yaml +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: gcppubsub-bus-ext -spec: - hosts: - - "*.googleapis.com" - - "accounts.google.com" - ports: - - number: 443 - name: https - protocol: HTTPS - location: MESH_EXTERNAL diff --git a/config/buses/kafka/README.md b/config/buses/kafka/README.md deleted file mode 100644 index 0f3ea4fe044..00000000000 --- a/config/buses/kafka/README.md +++ /dev/null @@ -1,43 +0,0 @@ -# Apache Kafka - Knative Bus - -Deployment steps: -1. Setup [Knative Eventing](../../../DEVELOPMENT.md) -1. Install an Apache Kafka cluster. There are two choices: - * Simple installation of [Apache Kafka](broker). - * A production grade installation using the [Strimzi Kafka Operator](strimzi). - -1. Now that the Apache Kafka is installed, configure the bus to use the Kafka broker, replace the broker URL if not using the provided broker: - ``` - kubectl -n knative-eventing create configmap kafka-bus-config --from-literal=KAFKA_BOOTSTRAP_SERVERS=kafkabroker.kafka:9092 - ``` - > Note: If you are using Strimzi, the value for the URL is `my-cluster-kafka-bootstrap.kafka.9092`. -1. For cluster wide deployment, change the kind in `config/buses/kafka/kafka-bus.yaml` from `Bus` to `ClusterBus`. -1. Apply the Kafka Bus: - ``` - ko apply -f config/buses/kafka/ - ``` -1. If you want to set the default Knative Bus to Kafka run the following command to edit the Knative Eventing configuration (requires the above change in kind from `Bus` to `ClusterBus`): - ```shell - kubectl get cm flow-controller-config -n knative-eventing -oyaml \ - | sed -e 's/default-cluster-bus: stub/ default-cluster-bus: kafka/' \ - | kubectl replace -f - - ``` -1. Create Channels that reference the 'kafka' Bus -1. (Optional) Install [Kail](https://github.com/boz/kail) - Kubernetes tail - -The bus has an independent provisioner and dispatcher. - -The provisioner will create Kafka topics for each Knative Channel -targeting the Bus (named `.`. -Clients should avoid interacting with topics provisioned by the bus. - -The dispatcher -- receives events via a Channel's Service from inside the cluster and -writes them to the corresponding Kafka topic -- creates a Kafka consumer for each `Subscription`, that reads events -from the subscription's channel and forwards them over HTTP to the -subscriber. - -To view logs: -- for the dispatcher `kail -d kafka-[namespace]-bus-dispatcher -c dispatcher` -- for the provisioner `kail -d kafka-[namespace]-bus-provisioner -c provisioner` diff --git a/config/buses/kafka/broker/README.md b/config/buses/kafka/broker/README.md deleted file mode 100644 index fe6dcff648f..00000000000 --- a/config/buses/kafka/broker/README.md +++ /dev/null @@ -1,13 +0,0 @@ -# Apache Kakfa - simple installation - -1. For an installation of a simple Apache Kafka cluster, a setup is provided: - ``` - kubectl create namespace kafka - kubectl apply -n kafka -f kafka-broker.yaml - ``` - > Note: If you are running Knative on OpenShift you will need to run the following command first to allow the Kafka broker to run as root: - ``` - oc adm policy add-scc-to-user anyuid -z default -n kafka - ``` - -Continue the configuration of Knative Eventing with [step `3`](../). diff --git a/config/buses/kafka/broker/kafka-broker.yaml b/config/buses/kafka/broker/kafka-broker.yaml deleted file mode 100644 index 469e697a526..00000000000 --- a/config/buses/kafka/broker/kafka-broker.yaml +++ /dev/null @@ -1,87 +0,0 @@ -########################################## KAFKA BROKER ###################################### -# The following does not need to live in the same namespace as the bus. ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: kafka-broker -spec: - replicas: 1 - template: - metadata: - labels: - app: kafka-broker - spec: - containers: - - name: kafka-broker - image: wurstmeister/kafka:1.1.0 - ports: - - containerPort: 9092 - env: - - name: MY_POD_NAMESPACE - valueFrom: - fieldRef: - fieldPath: metadata.namespace - - name: KAFKA_BROKER_ID - value: "0" - - name: KAFKA_LISTENERS - value: "INTERNAL://:9093,EXTERNAL://:9092" - - name: KAFKA_ADVERTISED_LISTENERS - value: "INTERNAL://:9093,EXTERNAL://kafkabroker.$(MY_POD_NAMESPACE):9092" - - name: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP - value: "INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT" - - name: KAFKA_INTER_BROKER_LISTENER_NAME - value: "INTERNAL" - - name: KAFKA_ZOOKEEPER_CONNECT - value: "zookeeper.$(MY_POD_NAMESPACE):2181" - - name: KAFKA_AUTO_CREATE_TOPICS_ENABLE - value: "false" ---- -apiVersion: v1 -kind: Service -metadata: - name: kafkabroker -spec: - type: NodePort - selector: - app: kafka-broker - ports: - - port: 9092 - name: kafka - protocol: TCP ---- -apiVersion: extensions/v1beta1 -kind: Deployment -metadata: - name: zookeeper -spec: - replicas: 1 - template: - metadata: - labels: - app: zookeeper - spec: - containers: - - name: zookeeper - image: wurstmeister/zookeeper:3.4.6 - ports: - - containerPort: 2181 - env: - - name: ZOOKEEPER_ID - value: "1" - - name: ZOOKEEPER_SERVER_1 - value: zookeeper - ---- -apiVersion: v1 -kind: Service -metadata: - name: zookeeper -spec: - selector: - app: zookeeper - ports: - - port: 2181 - name: zookeeper - protocol: TCP - diff --git a/config/buses/kafka/kafka-bus.yaml b/config/buses/kafka/kafka-bus.yaml deleted file mode 100644 index 7bc6c07ff00..00000000000 --- a/config/buses/kafka/kafka-bus.yaml +++ /dev/null @@ -1,31 +0,0 @@ -apiVersion: channels.knative.dev/v1alpha1 -kind: Bus -metadata: - name: kafka -spec: - parameters: - subscription: - - name: "initialOffset" - description: "The initial offset to use when subscribing, either Oldest or Newest. Defaults to Newest." - default: "Newest" - provisioner: - name: provisioner - image: github.com/knative/eventing/pkg/buses/kafka/provisioner - args: [ - "-logtostderr", - "-stderrthreshold", "INFO", - ] - env: &env - - name: KAFKA_BOOTSTRAP_SERVERS - valueFrom: - configMapKeyRef: - name: kafka-bus-config - key: KAFKA_BOOTSTRAP_SERVERS - dispatcher: - name: dispatcher - image: github.com/knative/eventing/pkg/buses/kafka/dispatcher - args: [ - "-logtostderr", - "-stderrthreshold", "INFO", - ] - env: *env diff --git a/config/buses/kafka/strimzi/README.md b/config/buses/kafka/strimzi/README.md deleted file mode 100644 index efd7d5437df..00000000000 --- a/config/buses/kafka/strimzi/README.md +++ /dev/null @@ -1,31 +0,0 @@ -# Strimzi - Apache Kafka Operator - -[Strimzi](http://strimzi.io) makes it easy to run a production grade Apache Kafka installation on OpenShift or Kubernetes. It implements the _Kubernetes Operator pattern_ for mananging `clusters`, `topics` or `users` based on custom resource files. - -Installing the Strimzi Cluster Operator is simple and requires only a few steps. - -1. Create the `kafka` namespace in your Kubernetes cluster: - ``` - kubectl create namespace kafka - ``` - -1. Install the Strimzi _Cluster Operator_: - - * Applying yaml files from the [Strimzi release bundle](https://github.com/strimzi/strimzi-kafka-operator/releases/latest) - * Using the Strimzi Helm Chart - - Both ways for installing the _Cluster Operator_ are described in the [Strimzi documentation](http://strimzi.io/docs/master/#cluster-operator-str) itself - - > Note: Once this is done, you will have a `strimzi-cluster-operator` pod, which is able to install the Apache Kafka broker based on a `Kafka` custom resource file. - -1. Install the Apache Kafka cluster by providing the `kafka-persistent.yaml` Strimzi resource file from _this_ folder: - ``` - kubectl apply -f kafka-persistent.yaml -n kafka - ``` - > Note: If you want to use ephemeral storage, you have to use the `kafka-ephemeral.yaml` file. - - This provisions the complete installation of your Apache Kafka cluster. - -> Note: For learning more about Strimiz, please consult its [website](http://strimzi.io). - -Continue the configuration of Knative Eventing with [step `3`](../). diff --git a/config/buses/kafka/strimzi/kafka-ephemeral.yaml b/config/buses/kafka/strimzi/kafka-ephemeral.yaml deleted file mode 100644 index 6423bd39de9..00000000000 --- a/config/buses/kafka/strimzi/kafka-ephemeral.yaml +++ /dev/null @@ -1,23 +0,0 @@ -apiVersion: kafka.strimzi.io/v1alpha1 -kind: Kafka -metadata: - name: my-cluster -spec: - kafka: - replicas: 1 - listeners: - plain: {} - tls: {} - config: - offsets.topic.replication.factor: 3 - transaction.state.log.replication.factor: 3 - transaction.state.log.min.isr: 2 - storage: - type: ephemeral - zookeeper: - replicas: 1 - storage: - type: ephemeral - entityOperator: - topicOperator: {} - userOperator: {} diff --git a/config/buses/kafka/strimzi/kafka-persistent.yaml b/config/buses/kafka/strimzi/kafka-persistent.yaml deleted file mode 100644 index ea5fd60ce4d..00000000000 --- a/config/buses/kafka/strimzi/kafka-persistent.yaml +++ /dev/null @@ -1,27 +0,0 @@ -apiVersion: kafka.strimzi.io/v1alpha1 -kind: Kafka -metadata: - name: my-cluster -spec: - kafka: - replicas: 1 - listeners: - plain: {} - tls: {} - config: - offsets.topic.replication.factor: 3 - transaction.state.log.replication.factor: 3 - transaction.state.log.min.isr: 2 - storage: - type: persistent-claim - size: 1Gi - deleteClaim: false - zookeeper: - replicas: 1 - storage: - type: persistent-claim - size: 1Gi - deleteClaim: false - entityOperator: - topicOperator: {} - userOperator: {} diff --git a/config/buses/stub/README.md b/config/buses/stub/README.md deleted file mode 100644 index 4f44fe8f111..00000000000 --- a/config/buses/stub/README.md +++ /dev/null @@ -1,16 +0,0 @@ -# Stub - Knative Bus - -Deployment steps: -1. Setup [Knative Eventing](../../../DEVELOPMENT.md) -1. For cluster wide deployment, change the kind in `config/buses/stub/stub-bus.yaml` from `Bus` to `ClusterBus`. -1. Apply the 'stub' Bus `ko apply -f config/buses/stub/` -1. Create Channels that reference the 'stub' Bus -1. (Optional) Install [Kail](https://github.com/boz/kail) - Kubernetes tail - -The bus is only a dispatcher. - -The dispatcher receives events via a Channel's Service from inside the cluster and forwarded via HTTP to the subscribers. - -Note: The stub bus does not guarantee delivery, errors will not be reattempted. - -To view logs: `kail -d stub-[namespace]-bus-dispatcher -c dispatcher` diff --git a/config/buses/stub/stub-bus.yaml b/config/buses/stub/stub-bus.yaml deleted file mode 100644 index 27cffb43c15..00000000000 --- a/config/buses/stub/stub-bus.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: channels.knative.dev/v1alpha1 -kind: Bus -metadata: - name: stub -spec: - dispatcher: - name: dispatcher - image: github.com/knative/eventing/pkg/buses/stub/dispatcher - args: [ - "-logtostderr", - "-stderrthreshold", "INFO", - ] diff --git a/hack/release.sh b/hack/release.sh index 7c381279dae..33699fc0b92 100755 --- a/hack/release.sh +++ b/hack/release.sh @@ -23,23 +23,15 @@ readonly EVENTING_RELEASE_GCS readonly EVENTING_RELEASE_GCR # Yaml files to generate, and the source config dir for them. +declare -A COMPONENTS +COMPONENTS["eventing.yaml"]="config" +COMPONENTS["in-memory-channel.yaml"]="config/provisioners/in-memory-channel" +readonly COMPONENTS + declare -A RELEASES -RELEASES["release.yaml"]="config" -RELEASES["release-bus-stub.yaml"]="config/buses/stub" -RELEASES["release-bus-gcppubsub.yaml"]="config/buses/gcppubsub" -RELEASES["release-bus-kafka.yaml"]="config/buses/kafka" -RELEASES["release-source-k8sevents.yaml"]="pkg/sources/k8sevents" -RELEASES["release-source-gcppubsub.yaml"]="pkg/sources/gcppubsub" -RELEASES["release-source-github.yaml"]="pkg/sources/github" +RELEASES["release.yaml"]="eventing.yaml;in-memory-channel.yaml" readonly RELEASES -# Yaml files that will be also released as ClusterBuses from Buses -readonly CLUSTERBUS_YAMLS=( - release-bus-stub.yaml - release-bus-gcppubsub.yaml - release-bus-kafka.yaml -) - # Script entry point. parse_flags $@ @@ -59,25 +51,28 @@ if (( PUBLISH_RELEASE )); then echo "- Destination GCS: ${EVENTING_RELEASE_GCS}" fi -# Build the release +# Build the components all_yamls=() -for yaml in "${!RELEASES[@]}"; do - config="${RELEASES[${yaml}]}" +for yaml in "${!COMPONENTS[@]}"; do + config="${COMPONENTS[${yaml}]}" echo "Building Knative Eventing - ${config}" ko resolve ${KO_FLAGS} -f ${config}/ > ${yaml} tag_images_in_yaml ${yaml} ${EVENTING_RELEASE_GCR} ${TAG} all_yamls+=(${yaml}) done -for yaml in ${CLUSTERBUS_YAMLS[@]}; do - clusterbus_yaml=${yaml/-bus-/-clusterbus-} - config="${RELEASES[${yaml}]}" - echo "Building Knative Eventing - ${config} (${clusterbus_yaml})" - sed -e 's/^kind: Bus$/kind: ClusterBus/g' ${yaml} > ${clusterbus_yaml} - tag_images_in_yaml ${clusterbus_yaml} ${EVENTING_RELEASE_GCR} ${TAG} - all_yamls+=(${clusterbus_yaml}) +# Assemble the release +for yaml in "${!RELEASES[@]}"; do + echo "Assembling Knative Eventing - ${yaml}" + echo -n "" > ${yaml} + for component in $(echo ${RELEASES[${yaml}]} | tr ";" "\n"); do + echo "---" >> ${yaml} + echo "# ${component}" >> ${yaml} + cat ${component} >> ${yaml} + done + all_yamls+=(${yaml}) done echo "New release built successfully" diff --git a/hack/update-codegen.sh b/hack/update-codegen.sh index 6422e616532..b651c1fc19a 100755 --- a/hack/update-codegen.sh +++ b/hack/update-codegen.sh @@ -28,7 +28,7 @@ CODEGEN_PKG=${CODEGEN_PKG:-$(cd ${REPO_ROOT_DIR}; ls -d -1 ./vendor/k8s.io/code- # instead of the $GOPATH directly. For normal projects this can be dropped. ${CODEGEN_PKG}/generate-groups.sh "deepcopy,client,informer,lister" \ github.com/knative/eventing/pkg/client github.com/knative/eventing/pkg/apis \ - "channels:v1alpha1 feeds:v1alpha1 flows:v1alpha1 eventing:v1alpha1" \ + "eventing:v1alpha1" \ --go-header-file ${REPO_ROOT_DIR}/hack/boilerplate/boilerplate.go.txt # Only deepcopy the Duck types, as they are not real resources. diff --git a/pkg/apis/channels/logkey/constants.go b/pkg/apis/channels/logkey/constants.go deleted file mode 100644 index f0071154a8e..00000000000 --- a/pkg/apis/channels/logkey/constants.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package logkey - -const ( - kNative = "knative.dev/" - - // ClusterBus is the key used for cluster scoped bus name in structured logs - ClusterBus = kNative + "clusterbus" - - // Bus is the key used for bus name in structured logs - Bus = kNative + "bus" - - // Channel is the key used for channel name in structured logs - Channel = kNative + "channel" -) diff --git a/pkg/apis/channels/register.go b/pkg/apis/channels/register.go deleted file mode 100644 index 44b041cde02..00000000000 --- a/pkg/apis/channels/register.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package channels - -const ( - GroupName = "channels.knative.dev" -) diff --git a/pkg/apis/channels/v1alpha1/bus_defaults.go b/pkg/apis/channels/v1alpha1/bus_defaults.go deleted file mode 100644 index d28033a3cc8..00000000000 --- a/pkg/apis/channels/v1alpha1/bus_defaults.go +++ /dev/null @@ -1,33 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -// TODO(n3wscott): This is staging work, the plan is another pass to bring up -// the test coverage, then remove unused after each type is stubbed. -// This is all prep for new serving style webhook integration. - -func (b *Bus) SetDefaults() { - b.Spec.SetDefaults() -} - -func (bs *BusSpec) SetDefaults() { - bs.Parameters.SetDefaults() -} - -func (bp *BusParameters) SetDefaults() { - // TODO anything? -} diff --git a/pkg/apis/channels/v1alpha1/bus_types.go b/pkg/apis/channels/v1alpha1/bus_types.go deleted file mode 100644 index 85e80600588..00000000000 --- a/pkg/apis/channels/v1alpha1/bus_types.go +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package v1alpha1 - -import ( - "encoding/json" - - "github.com/knative/pkg/apis" - "github.com/knative/pkg/webhook" - kapi "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// +genclient -// +genclient:noStatus -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +k8s:defaulter-gen=true - -// Bus represents how channels and subscriptions should be managed and -// corresponds to the buses.channels.knative.dev CRD. Buses will frequently, but -// not always, be backed by an event broker. -type Bus struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec BusSpec `json:"spec"` - Status BusStatus `json:"status,omitempty"` -} - -// Check that Bus can be validated, can be defaulted, and has immutable fields. -var _ apis.Validatable = (*Bus)(nil) -var _ apis.Defaultable = (*Bus)(nil) -var _ apis.Immutable = (*Bus)(nil) -var _ runtime.Object = (*Bus)(nil) -var _ webhook.GenericCRD = (*Bus)(nil) - -// BusSpec specifies the Bus' parameters for Channels and Subscriptions, how the -// provisioner and dispatcher for a bus should be run, and which volumes should -// be mounted into them. -type BusSpec struct { - // TODO: Generation does not work correctly with CRD. They are scrubbed - // by the APIserver (https://github.com/kubernetes/kubernetes/issues/58778) - // So, we add Generation here. Once that gets fixed, remove this and use - // ObjectMeta.Generation instead. - // +optional - Generation int64 `json:"generation,omitempty"` - - // Parameters defines the parameters that must be passed by this Bus' - // Channels and their Subscriptions. Channels and Subscriptions fulfill - // these parameters with Arguments. - Parameters *BusParameters `json:"parameters,omitempty"` - - // Provisioner defines how the provisioner container for this bus should be - // run. Provisioners are responsible for provisioning the underlying - // infrastructure for Channels and Subscriptions. The exact work done by the - // provisioner varies by Bus; one example of work done by a provisioner - // could be creating a messaging topic that backs a channel. - Provisioner *kapi.Container `json:"provisioner,omitempty"` - - // Dispatcher defines how the dispatcher container for this bus should be - // run. Dispatchers are responsible for performing two types of event - // dispatch: dispatching incoming events to the Bus' Channels and - // dispatching events in the Channel to the Channel's Subscriptions. - Dispatcher kapi.Container `json:"dispatcher"` - - // Volumes to be mounted inside the provisioner or dispatcher containers - Volumes *[]kapi.Volume `json:"volumes,omitempty"` -} - -// BusParameters represents the arguments that must be passed by Channels and -// Subscriptions. -type BusParameters struct { - // Channel configuration params for channels on the bus - Channel *[]Parameter `json:"channel,omitempty"` - - // Subscription configuration params for subscriptions on the bus - Subscription *[]Parameter `json:"subscription,omitempty"` -} - -type BusConditionType string - -const ( - // Ready is set when all other conditions are met and the bus is ready to accept traffic. - BusReady BusConditionType = "Ready" - - // Serviceable means the service addressing the bus exists. - BusServiceable BusConditionType = "Serviceable" - - // Provisioning means the deployment for the bus provisioner exists. - BusProvisioning BusConditionType = "Provisioning" - - // Dispatching means the deployment for the bus dispatcher exists. - BusDispatching = "Dispatching" -) - -// BusCondition describes the state of a bus at a point in time. -type BusCondition struct { - // Type of bus condition. - Type BusConditionType `json:"type"` - // Status of the condition, one of True, False, Unknown. - Status kapi.ConditionStatus `json:"status"` - // The last time this condition was updated. - LastUpdateTime meta_v1.Time `json:"lastUpdateTime,omitempty"` - // Last time the condition transitioned from one status to another. - LastTransitionTime meta_v1.Time `json:"lastTransitionTime,omitempty"` - // The reason for the condition's last transition. - Reason string `json:"reason,omitempty"` - // A human readable message indicating details about the transition. - Message string `json:"message,omitempty"` -} - -// BusStatus (computed) for a bus -type BusStatus struct { - // A reference to the k8s Service fronting this bus, if successfully synced. - Service *kapi.LocalObjectReference `json:"service,omitempty"` - - // Represents the latest available observations of a bus's current state. - // +patchMergeKey=type - // +patchStrategy=merge - Conditions []BusCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` -} - -func (b *Bus) BacksChannel(channel *Channel) bool { - return b.Namespace == channel.Namespace && b.Name == channel.Spec.Bus -} - -func (b *Bus) GetSpec() *BusSpec { - return &b.Spec -} - -func (b *Bus) GetStatus() *BusStatus { - return &b.Status -} - -func (b *Bus) GetSpecJSON() ([]byte, error) { - return json.Marshal(b.Spec) -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// BusList returned in list operations -type BusList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []Bus `json:"items"` -} - -// GenericBus may be backed by Bus or ClusterBus -type GenericBus interface { - runtime.Object - meta_v1.ObjectMetaAccessor - BacksChannel(channel *Channel) bool - GetSpec() *BusSpec - GetStatus() *BusStatus - - // Needed for generic webhook support - apis.Defaultable - apis.Immutable - apis.Validatable -} diff --git a/pkg/apis/channels/v1alpha1/bus_validation.go b/pkg/apis/channels/v1alpha1/bus_validation.go deleted file mode 100644 index 66fd785f9d0..00000000000 --- a/pkg/apis/channels/v1alpha1/bus_validation.go +++ /dev/null @@ -1,63 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "fmt" - "github.com/knative/pkg/apis" - "k8s.io/apimachinery/pkg/util/validation" -) - -// TODO(n3wscott): This is staging work, the plan is another pass to bring up -// the test coverage, then remove unused after each type is stubbed. -// This is all prep for new serving style webhook integration. - -func (b *Bus) Validate() *apis.FieldError { - return b.Spec.Validate().ViaField("spec") -} - -func (bs *BusSpec) Validate() *apis.FieldError { - if bs.Parameters != nil { - return bs.Parameters.Validate().ViaField("parameters") - } - return nil -} - -func (bp *BusParameters) Validate() *apis.FieldError { - if bp.Channel != nil { - for i, p := range *bp.Channel { - errs := validation.IsConfigMapKey(p.Name) - if len(errs) > 0 { - return apis.ErrInvalidKeyName(p.Name, "name", errs...).ViaField(fmt.Sprintf("channel[%d]", i)) - } - } - } - if bp.Subscription != nil { - for i, p := range *bp.Subscription { - errs := validation.IsConfigMapKey(p.Name) - if len(errs) > 0 { - return apis.ErrInvalidKeyName(p.Name, "name", errs...).ViaField(fmt.Sprintf("subscription[%d]", i)) - } - } - } - return nil -} - -func (current *Bus) CheckImmutableFields(og apis.Immutable) *apis.FieldError { - // TODO(n3wscott): Anything to check? - return nil -} diff --git a/pkg/apis/channels/v1alpha1/bus_validation_test.go b/pkg/apis/channels/v1alpha1/bus_validation_test.go deleted file mode 100644 index e74ed356315..00000000000 --- a/pkg/apis/channels/v1alpha1/bus_validation_test.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "fmt" - "github.com/google/go-cmp/cmp" - "github.com/knative/pkg/apis" - "strings" - "testing" -) - -var longName = strings.Repeat("A", 255) - -// TODO: add the following tests: -// 1. Multiple parameters to the same Bus. -// 2. Two parameters with the same Name in the same list. -// 3. Multiple errors in the same object. - -func TestBusSpecValidation(t *testing.T) { - tests := []struct { - name string - bs *BusSpec - want *apis.FieldError - }{{ - name: "valid", - bs: &BusSpec{ - Parameters: &BusParameters{ - Channel: &[]Parameter{ - { - Name: "foo", - Description: "bar", - }, - }, - Subscription: &[]Parameter{ - { - Name: "foo", - Description: "bar", - }, - }, - }, - }, - }, { - name: "valid no description", - bs: &BusSpec{ - Parameters: &BusParameters{ - Channel: &[]Parameter{ - { - Name: "foo", - }, - }, - Subscription: &[]Parameter{ - { - Name: "foo", - }, - }, - }, - }, - }, { - name: "invalid channel parameter", - bs: &BusSpec{ - Parameters: &BusParameters{ - Channel: &[]Parameter{ - { - Name: "foo@bar", - }, - }, - }, - }, - want: &apis.FieldError{ - Message: `invalid key name "foo@bar"`, - Paths: []string{ - "parameters.channel[0].name", - }, - Details: "a valid config key must consist of alphanumeric characters, '-', '_' or '.' (e.g. 'key.name', or 'KEY_NAME', or 'key-name', regex used for validation is '[-._a-zA-Z0-9]+')", - }, - }, { - name: "invalid subscription parameter", - bs: &BusSpec{ - Parameters: &BusParameters{ - Subscription: &[]Parameter{ - { - Name: "foo@bar", - }, - }, - }, - }, - want: &apis.FieldError{ - Message: `invalid key name "foo@bar"`, - Paths: []string{ - "parameters.subscription[0].name", - }, - Details: "a valid config key must consist of alphanumeric characters, '-', '_' or '.' (e.g. 'key.name', or 'KEY_NAME', or 'key-name', regex used for validation is '[-._a-zA-Z0-9]+')", - }, - }, { - name: "invalid channel too long", - bs: &BusSpec{ - Parameters: &BusParameters{ - Channel: &[]Parameter{ - { - Name: longName, - }, - }, - }, - }, - want: &apis.FieldError{ - Message: fmt.Sprintf("invalid key name %q", longName), - Paths: []string{ - "parameters.channel[0].name", - }, - Details: "must be no more than 253 characters", - }, - }, { - name: "empty bus", - bs: &BusSpec{}, - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.bs.Validate() - if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("validateBus (-want, +got) = %v", diff) - } - }) - } -} diff --git a/pkg/apis/channels/v1alpha1/channel_defaults.go b/pkg/apis/channels/v1alpha1/channel_defaults.go deleted file mode 100644 index 262ffca487d..00000000000 --- a/pkg/apis/channels/v1alpha1/channel_defaults.go +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -func (c *Channel) SetDefaults() { - c.Spec.SetDefaults() -} - -func (fs *ChannelSpec) SetDefaults() { - // TODO anything? -} diff --git a/pkg/apis/channels/v1alpha1/channel_types.go b/pkg/apis/channels/v1alpha1/channel_types.go deleted file mode 100644 index 150243f2771..00000000000 --- a/pkg/apis/channels/v1alpha1/channel_types.go +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package v1alpha1 - -import ( - "encoding/json" - - "github.com/knative/pkg/apis" - "github.com/knative/pkg/webhook" - "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// +genclient -// +genclient:noStatus -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +k8s:defaulter-gen=true - -// Channel represents a named endpoint on which a Bus accepts event delivery and -// corresponds to the channels.channels.knative.dev CRD. The Bus handles -// provisioning channels, delivering events to Channels, and delivering events -// from Channels to their Subscriptions. -type Channel struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec ChannelSpec `json:"spec"` - Status ChannelStatus `json:"status,omitempty"` -} - -// Check that Channel can be validated, can be defaulted, and has immutable fields. -var _ apis.Validatable = (*Channel)(nil) -var _ apis.Defaultable = (*Channel)(nil) -var _ apis.Immutable = (*Channel)(nil) -var _ runtime.Object = (*Channel)(nil) -var _ webhook.GenericCRD = (*Channel)(nil) - -// ChannelSpec specifies the Bus backing a channel and the configuration -// arguments for the channel. -type ChannelSpec struct { - // TODO: Generation does not work correctly with CRD. They are scrubbed - // by the APIserver (https://github.com/kubernetes/kubernetes/issues/58778) - // So, we add Generation here. Once that gets fixed, remove this and use - // ObjectMeta.Generation instead. - // +optional - Generation int64 `json:"generation,omitempty"` - - // Name of the bus backing this channel (optional) - Bus string `json:"bus,omitempty"` - - // ClusterBus name of the clusterbus backing this channel (mutually exclusive with Bus) - ClusterBus string `json:"clusterBus,omitempty"` - - // Arguments is a list of configuration arguments for the Channel. The - // Arguments for a channel must contain values for each of the Parameters - // specified by the Bus' spec.parameters.Channels field except the - // Parameters that have a default value. If a Parameter has a default value - // and it is not in the list of Arguments, the default value will be used; a - // Parameter without a default value that does not have an Argument will - // result in an error setting up the Channel. - Arguments *[]Argument `json:"arguments,omitempty"` -} - -type ChannelConditionType string - -const ( - - // Ready is set when all other conditions are met and the channel is ready to accept traffic. - ChannelReady ChannelConditionType = "Ready" - - // Serviceable means the service addressing the channel exists. - ChannelServiceable ChannelConditionType = "Serviceable" - - // Routable means the virtual service forwarding traffic from the channel service to the - // bus is created. - ChannelRoutable ChannelConditionType = "Routeable" - - // Provisioned means the channel backing construct on the bus middleware has been set up. - ChannelProvisioned ChannelConditionType = "Provisioned" -) - -// ChannelCondition describes the state of a channel at a point in time. -type ChannelCondition struct { - // Type of channel condition. - Type ChannelConditionType `json:"type"` - // Status of the condition, one of True, False, Unknown. - Status v1.ConditionStatus `json:"status"` - // The last time this condition was updated. - LastUpdateTime meta_v1.Time `json:"lastUpdateTime,omitempty"` - // Last time the condition transitioned from one status to another. - LastTransitionTime meta_v1.Time `json:"lastTransitionTime,omitempty"` - // The reason for the condition's last transition. - Reason string `json:"reason,omitempty"` - // A human readable message indicating details about the transition. - Message string `json:"message,omitempty"` -} - -// ChannelStatus (computed) for a channel -type ChannelStatus struct { - // A reference to the k8s Service backing this channel, if successfully synced. - Service *v1.LocalObjectReference `json:"service,omitempty"` - - // A reference to the istio VirtualService backing this channel, if successfully synced. - VirtualService *v1.LocalObjectReference `json:"virtualService,omitempty"` - - // Represents the latest available observations of a channel's current state. - // +patchMergeKey=type - // +patchStrategy=merge - Conditions []ChannelCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` - - // DomainInternal holds the top-level domain that will distribute traffic - // over the provided targets from inside the cluster. It generally has the - // form {channel}.{namespace}.svc.cluster.local - // +optional - DomainInternal string `json:"domainInternal,omitempty"` -} - -func (cs *ChannelStatus) GetCondition(t ChannelConditionType) *ChannelCondition { - for _, cond := range cs.Conditions { - if cond.Type == t { - return &cond - } - } - return nil -} - -func (c *Channel) GetSpecJSON() ([]byte, error) { - return json.Marshal(c.Spec) -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// ChannelList returned in list operations -type ChannelList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []Channel `json:"items"` -} diff --git a/pkg/apis/channels/v1alpha1/channel_validation.go b/pkg/apis/channels/v1alpha1/channel_validation.go deleted file mode 100644 index 338f122a7c0..00000000000 --- a/pkg/apis/channels/v1alpha1/channel_validation.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/knative/pkg/apis" - - "k8s.io/apimachinery/pkg/util/validation" -) - -func (c *Channel) Validate() *apis.FieldError { - return c.Spec.Validate().ViaField("spec") -} - -func (cs *ChannelSpec) Validate() *apis.FieldError { - switch { - case len(cs.Bus) != 0 && len(cs.ClusterBus) != 0: - return apis.ErrMultipleOneOf("bus", "clusterBus") - case len(cs.Bus) != 0: - if errs := validation.IsQualifiedName(cs.Bus); len(errs) > 0 { - return apis.ErrInvalidKeyName(cs.Bus, "bus", errs...) - } - return nil - case len(cs.ClusterBus) != 0: - if errs := validation.IsQualifiedName(cs.ClusterBus); len(errs) > 0 { - return apis.ErrInvalidKeyName(cs.ClusterBus, "clusterBus", errs...) - } - return nil - default: - return apis.ErrMissingOneOf("bus", "clusterBus") - } -} - -func (current *Channel) CheckImmutableFields(og apis.Immutable) *apis.FieldError { - original, ok := og.(*Channel) - if !ok { - return &apis.FieldError{Message: "The provided original was not a Channel"} - } - if original == nil { - return nil - } - - ignoreArguments := cmpopts.IgnoreFields(ChannelSpec{}, "Arguments") - if diff := cmp.Diff(original.Spec, current.Spec, ignoreArguments); diff != "" { - return &apis.FieldError{ - Message: "Immutable fields changed (-old +new)", - Paths: []string{"spec"}, - Details: diff, - } - } - return nil -} diff --git a/pkg/apis/channels/v1alpha1/channel_validation_test.go b/pkg/apis/channels/v1alpha1/channel_validation_test.go deleted file mode 100644 index 1f70b64dcf6..00000000000 --- a/pkg/apis/channels/v1alpha1/channel_validation_test.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/google/go-cmp/cmp" - "github.com/knative/pkg/apis" - "testing" -) - -func TestChannelSpecValidation(t *testing.T) { - tests := []struct { - name string - c *ChannelSpec - want *apis.FieldError - }{{ - name: "namespace valid", - c: &ChannelSpec{ - Bus: "foo", - }, - want: nil, - }, { - name: "cluster valid", - c: &ChannelSpec{ - ClusterBus: "bar", - }, - want: nil, - }, { - name: "empty", - c: &ChannelSpec{}, - want: apis.ErrMissingOneOf("bus", "clusterBus"), - }, { - name: "mutually exclusive missing", - c: &ChannelSpec{ - Arguments: &[]Argument{{Name: "foo", Value: "bar"}}, - }, - want: apis.ErrMissingOneOf("bus", "clusterBus"), - }, { - name: "mutually exclusive both", - c: &ChannelSpec{ - Bus: "foo", - ClusterBus: "bar", - }, - want: apis.ErrMultipleOneOf("bus", "clusterBus"), - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.c.Validate() - if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("validateChannel (-want, +got) = %v", diff) - } - }) - } -} diff --git a/pkg/apis/channels/v1alpha1/clusterbus_defaults.go b/pkg/apis/channels/v1alpha1/clusterbus_defaults.go deleted file mode 100644 index 2dad3ce2327..00000000000 --- a/pkg/apis/channels/v1alpha1/clusterbus_defaults.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -func (b *ClusterBus) SetDefaults() { - b.Spec.SetDefaults() -} diff --git a/pkg/apis/channels/v1alpha1/clusterbus_types.go b/pkg/apis/channels/v1alpha1/clusterbus_types.go deleted file mode 100644 index 4d891400983..00000000000 --- a/pkg/apis/channels/v1alpha1/clusterbus_types.go +++ /dev/null @@ -1,78 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package v1alpha1 - -import ( - "encoding/json" - - "github.com/knative/pkg/apis" - "github.com/knative/pkg/webhook" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// +genclient -// +genclient:noStatus -// +genclient:nonNamespaced -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +k8s:defaulter-gen=true - -// ClusterBus represents the clusterbuses.channels.knative.dev CRD -type ClusterBus struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec ClusterBusSpec `json:"spec"` - Status ClusterBusStatus `json:"status,omitempty"` -} - -// Check that Bus can be validated, can be defaulted, and has immutable fields. -var _ apis.Validatable = (*ClusterBus)(nil) -var _ apis.Defaultable = (*ClusterBus)(nil) -var _ apis.Immutable = (*ClusterBus)(nil) -var _ runtime.Object = (*ClusterBus)(nil) -var _ webhook.GenericCRD = (*ClusterBus)(nil) - -// ClusterBusSpec (what the user wants) for a clusterbus -type ClusterBusSpec = BusSpec - -// ClusterBusStatus (computed) for a clusterbus -type ClusterBusStatus = BusStatus - -func (b *ClusterBus) BacksChannel(channel *Channel) bool { - return len(b.Namespace) == 0 && b.Name == channel.Spec.ClusterBus -} - -func (b *ClusterBus) GetSpec() *ClusterBusSpec { - return &b.Spec -} - -func (b *ClusterBus) GetStatus() *ClusterBusStatus { - return &b.Status -} - -func (b *ClusterBus) GetSpecJSON() ([]byte, error) { - return json.Marshal(b.Spec) -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// ClusterBusList returned in list operations -type ClusterBusList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []ClusterBus `json:"items"` -} diff --git a/pkg/apis/channels/v1alpha1/clusterbus_validation.go b/pkg/apis/channels/v1alpha1/clusterbus_validation.go deleted file mode 100644 index 2d95941948b..00000000000 --- a/pkg/apis/channels/v1alpha1/clusterbus_validation.go +++ /dev/null @@ -1,30 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/pkg/apis" -) - -func (b *ClusterBus) Validate() *apis.FieldError { - return b.Spec.Validate().ViaField("spec") -} - -func (current *ClusterBus) CheckImmutableFields(og apis.Immutable) *apis.FieldError { - // TODO(n3wscott): Anything to check? - return nil -} diff --git a/pkg/apis/channels/v1alpha1/doc.go b/pkg/apis/channels/v1alpha1/doc.go deleted file mode 100644 index 1fd7726b2c2..00000000000 --- a/pkg/apis/channels/v1alpha1/doc.go +++ /dev/null @@ -1,16 +0,0 @@ -/* -Copyright 2018 The Knative Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -// +k8s:deepcopy-gen=package -// Package v1alpha1 is the v1alpha1 version of the API. -// +groupName=channels.knative.dev -package v1alpha1 diff --git a/pkg/apis/channels/v1alpha1/param_types.go b/pkg/apis/channels/v1alpha1/param_types.go deleted file mode 100644 index b9e2fc9c63f..00000000000 --- a/pkg/apis/channels/v1alpha1/param_types.go +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package v1alpha1 - -// inspired by the parameter/argument from buildtemplate/build - -// Parameter represents a named configuration parameter that must be supplied to -// create a particular resource by an Argument. Parameters may optionally have a -// default value that will be used if an Argument is not supplied. -type Parameter struct { - // Name is the name of the Parameter. - Name string `json:"name"` - - // Description is the human friendly description of the parameter. - Description string `json:"description"` - - // Default is the value to use if an Argument for this Parameter is not - // explicitly set. - Default *string `json:"default,omitempty"` -} - -// Argument represents a value for a named parameter. -type Argument struct { - // Name is the name of the Parameter this Argument is for. - Name string `json:"name"` - - // Value is the value for the Parameter. - Value string `json:"value"` -} diff --git a/pkg/apis/channels/v1alpha1/register.go b/pkg/apis/channels/v1alpha1/register.go deleted file mode 100644 index 32ce1eb5370..00000000000 --- a/pkg/apis/channels/v1alpha1/register.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/eventing/pkg/apis/channels" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -// SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: channels.GroupName, Version: "v1alpha1"} - -// Kind takes an unqualified kind and returns back a Group qualified GroupKind -func Kind(kind string) schema.GroupKind { - return SchemeGroupVersion.WithKind(kind).GroupKind() -} - -// Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -var ( - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - AddToScheme = SchemeBuilder.AddToScheme -) - -// Adds the list of known types to Scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &Bus{}, - &BusList{}, - &ClusterBus{}, - &ClusterBusList{}, - &Channel{}, - &ChannelList{}, - &Subscription{}, - &SubscriptionList{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} diff --git a/pkg/apis/channels/v1alpha1/subscription_defaults.go b/pkg/apis/channels/v1alpha1/subscription_defaults.go deleted file mode 100644 index ed8fdf5b16f..00000000000 --- a/pkg/apis/channels/v1alpha1/subscription_defaults.go +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -func (s *Subscription) SetDefaults() { - s.Spec.SetDefaults() -} - -func (ss *SubscriptionSpec) SetDefaults() { - // TODO anything? -} diff --git a/pkg/apis/channels/v1alpha1/subscription_types.go b/pkg/apis/channels/v1alpha1/subscription_types.go deleted file mode 100644 index 93747ae103f..00000000000 --- a/pkg/apis/channels/v1alpha1/subscription_types.go +++ /dev/null @@ -1,131 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package v1alpha1 - -import ( - "encoding/json" - - "github.com/knative/pkg/apis" - "github.com/knative/pkg/webhook" - "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// +genclient -// +genclient:noStatus -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object -// +k8s:defaulter-gen=true - -// Subscription routes events received on a Channel to a DNS name and -// corresponds to the subscriptions.channels.knative.dev CRD. -type Subscription struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ObjectMeta `json:"metadata"` - Spec SubscriptionSpec `json:"spec"` - Status SubscriptionStatus `json:"status,omitempty"` -} - -// Check that Subscription can be validated, can be defaulted, and has immutable fields. -var _ apis.Validatable = (*Subscription)(nil) -var _ apis.Defaultable = (*Subscription)(nil) -var _ apis.Immutable = (*Subscription)(nil) -var _ runtime.Object = (*Subscription)(nil) -var _ webhook.GenericCRD = (*ClusterBus)(nil) - -// SubscriptionSpec specifies the Channel and Subscriber and the configuration -// arguments for the Subscription. -type SubscriptionSpec struct { - // TODO: Generation does not work correctly with CRD. They are scrubbed - // by the APIserver (https://github.com/kubernetes/kubernetes/issues/58778) - // So, we add Generation here. Once that gets fixed, remove this and use - // ObjectMeta.Generation instead. - // +optional - Generation int64 `json:"generation,omitempty"` - - // Channel is the name of the channel to subscribe to. - Channel string `json:"channel"` - - // Subscriber is the name of the subscriber service DNS name. - Subscriber string `json:"subscriber"` - - // Target service DNS name for replies returned by the subscriber. - ReplyTo string `json:"replyTo,omitempty"` - - // Arguments is a list of configuration arguments for the Subscription. The - // Arguments for a channel must contain values for each of the Parameters - // specified by the Bus' spec.parameters.Subscriptions field except the - // Parameters that have a default value. If a Parameter has a default value - // and it is not in the list of Arguments, the default value will be used; a - // Parameter without a default value that does not have an Argument will - // result in an error setting up the Subscription. - Arguments *[]Argument `json:"arguments,omitempty"` -} - -type SubscriptionConditionType string - -const ( - // Dispatching means the subscription is actively listening for incoming events on its channel and dispatching them. - SubscriptionDispatching SubscriptionConditionType = "Dispatching" -) - -// SubscriptionCondition describes the state of a subscription at a point in time. -type SubscriptionCondition struct { - // Type of subscription condition. - Type SubscriptionConditionType `json:"type"` - // Status of the condition, one of True, False, Unknown. - Status v1.ConditionStatus `json:"status"` - // The last time this condition was updated. - LastUpdateTime meta_v1.Time `json:"lastUpdateTime,omitempty"` - // Last time the condition transitioned from one status to another. - LastTransitionTime meta_v1.Time `json:"lastTransitionTime,omitempty"` - // The reason for the condition's last transition. - Reason string `json:"reason,omitempty"` - // A human readable message indicating details about the transition. - Message string `json:"message,omitempty"` -} - -// SubscriptionStatus (computed) for a subscription -type SubscriptionStatus struct { - - // Represents the latest available observations of a subscription's current state. - // +patchMergeKey=type - // +patchStrategy=merge - Conditions []SubscriptionCondition `json:"conditions,omitempty" patchStrategy:"merge" patchMergeKey:"type"` -} - -func (ss *SubscriptionStatus) GetCondition(t SubscriptionConditionType) *SubscriptionCondition { - for _, cond := range ss.Conditions { - if cond.Type == t { - return &cond - } - } - return nil -} - -func (s *Subscription) GetSpecJSON() ([]byte, error) { - return json.Marshal(s.Spec) -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// SubscriptionList returned in list operations -type SubscriptionList struct { - meta_v1.TypeMeta `json:",inline"` - meta_v1.ListMeta `json:"metadata"` - Items []Subscription `json:"items"` -} diff --git a/pkg/apis/channels/v1alpha1/subscription_validation.go b/pkg/apis/channels/v1alpha1/subscription_validation.go deleted file mode 100644 index c2f7ae8ab91..00000000000 --- a/pkg/apis/channels/v1alpha1/subscription_validation.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" - "github.com/knative/pkg/apis" -) - -func (s *Subscription) Validate() *apis.FieldError { - return s.Spec.Validate().ViaField("spec") -} - -func (ss *SubscriptionSpec) Validate() *apis.FieldError { - if ss.Channel == "" { - fe := apis.ErrMissingField("channel") - fe.Details = "the Subscription must reference a Channel" - return fe - } - if ss.Subscriber == "" { - fe := apis.ErrMissingField("subscriber") - fe.Details = "the Subscription must reference a Subscriber" - return fe - } - return nil -} - -func (current *Subscription) CheckImmutableFields(og apis.Immutable) *apis.FieldError { - original, ok := og.(*Subscription) - if !ok { - return &apis.FieldError{Message: "The provided original was not a Subscription"} - } - if original == nil { - return nil - } - - ignoreArguments := cmpopts.IgnoreFields(SubscriptionSpec{}, "Subscriber", "Arguments") - if diff := cmp.Diff(original.Spec, current.Spec, ignoreArguments); diff != "" { - return &apis.FieldError{ - Message: "Immutable fields changed (-old +new)", - Paths: []string{"spec"}, - Details: diff, - } - } - return nil -} diff --git a/pkg/apis/channels/v1alpha1/subscription_validation_test.go b/pkg/apis/channels/v1alpha1/subscription_validation_test.go deleted file mode 100644 index 091a5b7f3b2..00000000000 --- a/pkg/apis/channels/v1alpha1/subscription_validation_test.go +++ /dev/null @@ -1,139 +0,0 @@ -/* -Copyright 2018 The Knative Authors. All Rights Reserved. -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "testing" - - "github.com/google/go-cmp/cmp" - "github.com/knative/pkg/apis" -) - -func TestSubscriptionSpecValidation(t *testing.T) { - tests := []struct { - name string - c *SubscriptionSpec - want *apis.FieldError - }{{ - name: "valid", - c: &SubscriptionSpec{ - Channel: "bar", - Subscriber: "foo", - }, - want: nil, - }, { - name: "valid with arguments", - c: &SubscriptionSpec{ - Channel: "bar", - Subscriber: "foo", - Arguments: &[]Argument{{Name: "foo", Value: "bar"}}, - }, - want: nil, - }, { - name: "missing subscriber", - c: &SubscriptionSpec{ - Channel: "foo", - }, - want: func() *apis.FieldError { - fe := apis.ErrMissingField("subscriber") - fe.Details = "the Subscription must reference a Subscriber" - return fe - }(), - }, { - name: "empty", - c: &SubscriptionSpec{}, - want: func() *apis.FieldError { - fe := apis.ErrMissingField("channel") - fe.Details = "the Subscription must reference a Channel" - return fe - }(), - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.c.Validate() - if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("validateChannel (-want, +got) = %v", diff) - } - }) - } -} - -func TestSubscriptionImmutable(t *testing.T) { - tests := []struct { - name string - c *Subscription - og *Subscription - want *apis.FieldError - }{{ - name: "valid", - c: &Subscription{ - Spec: SubscriptionSpec{ - Channel: "foo", - }, - }, - og: &Subscription{ - Spec: SubscriptionSpec{ - Channel: "foo", - }, - }, - want: nil, - }, { - name: "valid, new subscriber", - c: &Subscription{ - Spec: SubscriptionSpec{ - Channel: "foo", - Subscriber: "bar", - }, - }, - og: &Subscription{ - Spec: SubscriptionSpec{ - Channel: "foo", - Subscriber: "baz", - }, - }, - want: nil, - }, { - name: "channel changed", - c: &Subscription{ - Spec: SubscriptionSpec{ - Channel: "foo", - }, - }, - og: &Subscription{ - Spec: SubscriptionSpec{ - Channel: "bar", - }, - }, - want: &apis.FieldError{ - Message: "Immutable fields changed (-old +new)", - Paths: []string{"spec"}, - Details: `{v1alpha1.SubscriptionSpec}.Channel: - -: "bar" - +: "foo" -`, - }, - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.c.CheckImmutableFields(test.og) - if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("CheckImmutableFields (-want, +got) = %v", diff) - } - }) - } -} diff --git a/pkg/apis/channels/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/channels/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index 3f815bbcd0a..00000000000 --- a/pkg/apis/channels/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,615 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1 "k8s.io/api/core/v1" - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Argument) DeepCopyInto(out *Argument) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Argument. -func (in *Argument) DeepCopy() *Argument { - if in == nil { - return nil - } - out := new(Argument) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Bus) DeepCopyInto(out *Bus) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Bus. -func (in *Bus) DeepCopy() *Bus { - if in == nil { - return nil - } - out := new(Bus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Bus) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BusCondition) DeepCopyInto(out *BusCondition) { - *out = *in - in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BusCondition. -func (in *BusCondition) DeepCopy() *BusCondition { - if in == nil { - return nil - } - out := new(BusCondition) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BusList) DeepCopyInto(out *BusList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Bus, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BusList. -func (in *BusList) DeepCopy() *BusList { - if in == nil { - return nil - } - out := new(BusList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *BusList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BusParameters) DeepCopyInto(out *BusParameters) { - *out = *in - if in.Channel != nil { - in, out := &in.Channel, &out.Channel - if *in == nil { - *out = nil - } else { - *out = new([]Parameter) - if **in != nil { - in, out := *in, *out - *out = make([]Parameter, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - } - } - if in.Subscription != nil { - in, out := &in.Subscription, &out.Subscription - if *in == nil { - *out = nil - } else { - *out = new([]Parameter) - if **in != nil { - in, out := *in, *out - *out = make([]Parameter, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BusParameters. -func (in *BusParameters) DeepCopy() *BusParameters { - if in == nil { - return nil - } - out := new(BusParameters) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BusSpec) DeepCopyInto(out *BusSpec) { - *out = *in - if in.Parameters != nil { - in, out := &in.Parameters, &out.Parameters - if *in == nil { - *out = nil - } else { - *out = new(BusParameters) - (*in).DeepCopyInto(*out) - } - } - if in.Provisioner != nil { - in, out := &in.Provisioner, &out.Provisioner - if *in == nil { - *out = nil - } else { - *out = new(v1.Container) - (*in).DeepCopyInto(*out) - } - } - in.Dispatcher.DeepCopyInto(&out.Dispatcher) - if in.Volumes != nil { - in, out := &in.Volumes, &out.Volumes - if *in == nil { - *out = nil - } else { - *out = new([]v1.Volume) - if **in != nil { - in, out := *in, *out - *out = make([]v1.Volume, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BusSpec. -func (in *BusSpec) DeepCopy() *BusSpec { - if in == nil { - return nil - } - out := new(BusSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BusStatus) DeepCopyInto(out *BusStatus) { - *out = *in - if in.Service != nil { - in, out := &in.Service, &out.Service - if *in == nil { - *out = nil - } else { - *out = new(v1.LocalObjectReference) - **out = **in - } - } - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]BusCondition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BusStatus. -func (in *BusStatus) DeepCopy() *BusStatus { - if in == nil { - return nil - } - out := new(BusStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Channel) DeepCopyInto(out *Channel) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Channel. -func (in *Channel) DeepCopy() *Channel { - if in == nil { - return nil - } - out := new(Channel) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Channel) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ChannelCondition) DeepCopyInto(out *ChannelCondition) { - *out = *in - in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelCondition. -func (in *ChannelCondition) DeepCopy() *ChannelCondition { - if in == nil { - return nil - } - out := new(ChannelCondition) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ChannelList) DeepCopyInto(out *ChannelList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Channel, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelList. -func (in *ChannelList) DeepCopy() *ChannelList { - if in == nil { - return nil - } - out := new(ChannelList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ChannelList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ChannelSpec) DeepCopyInto(out *ChannelSpec) { - *out = *in - if in.Arguments != nil { - in, out := &in.Arguments, &out.Arguments - if *in == nil { - *out = nil - } else { - *out = new([]Argument) - if **in != nil { - in, out := *in, *out - *out = make([]Argument, len(*in)) - copy(*out, *in) - } - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelSpec. -func (in *ChannelSpec) DeepCopy() *ChannelSpec { - if in == nil { - return nil - } - out := new(ChannelSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ChannelStatus) DeepCopyInto(out *ChannelStatus) { - *out = *in - if in.Service != nil { - in, out := &in.Service, &out.Service - if *in == nil { - *out = nil - } else { - *out = new(v1.LocalObjectReference) - **out = **in - } - } - if in.VirtualService != nil { - in, out := &in.VirtualService, &out.VirtualService - if *in == nil { - *out = nil - } else { - *out = new(v1.LocalObjectReference) - **out = **in - } - } - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]ChannelCondition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ChannelStatus. -func (in *ChannelStatus) DeepCopy() *ChannelStatus { - if in == nil { - return nil - } - out := new(ChannelStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterBus) DeepCopyInto(out *ClusterBus) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterBus. -func (in *ClusterBus) DeepCopy() *ClusterBus { - if in == nil { - return nil - } - out := new(ClusterBus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ClusterBus) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterBusList) DeepCopyInto(out *ClusterBusList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]ClusterBus, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterBusList. -func (in *ClusterBusList) DeepCopy() *ClusterBusList { - if in == nil { - return nil - } - out := new(ClusterBusList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ClusterBusList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Parameter) DeepCopyInto(out *Parameter) { - *out = *in - if in.Default != nil { - in, out := &in.Default, &out.Default - if *in == nil { - *out = nil - } else { - *out = new(string) - **out = **in - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Parameter. -func (in *Parameter) DeepCopy() *Parameter { - if in == nil { - return nil - } - out := new(Parameter) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Subscription) DeepCopyInto(out *Subscription) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Subscription. -func (in *Subscription) DeepCopy() *Subscription { - if in == nil { - return nil - } - out := new(Subscription) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Subscription) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SubscriptionCondition) DeepCopyInto(out *SubscriptionCondition) { - *out = *in - in.LastUpdateTime.DeepCopyInto(&out.LastUpdateTime) - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscriptionCondition. -func (in *SubscriptionCondition) DeepCopy() *SubscriptionCondition { - if in == nil { - return nil - } - out := new(SubscriptionCondition) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SubscriptionList) DeepCopyInto(out *SubscriptionList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Subscription, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscriptionList. -func (in *SubscriptionList) DeepCopy() *SubscriptionList { - if in == nil { - return nil - } - out := new(SubscriptionList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *SubscriptionList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SubscriptionSpec) DeepCopyInto(out *SubscriptionSpec) { - *out = *in - if in.Arguments != nil { - in, out := &in.Arguments, &out.Arguments - if *in == nil { - *out = nil - } else { - *out = new([]Argument) - if **in != nil { - in, out := *in, *out - *out = make([]Argument, len(*in)) - copy(*out, *in) - } - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscriptionSpec. -func (in *SubscriptionSpec) DeepCopy() *SubscriptionSpec { - if in == nil { - return nil - } - out := new(SubscriptionSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SubscriptionStatus) DeepCopyInto(out *SubscriptionStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]SubscriptionCondition, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SubscriptionStatus. -func (in *SubscriptionStatus) DeepCopy() *SubscriptionStatus { - if in == nil { - return nil - } - out := new(SubscriptionStatus) - in.DeepCopyInto(out) - return out -} diff --git a/pkg/apis/feeds/logkey/constants.go b/pkg/apis/feeds/logkey/constants.go deleted file mode 100644 index 4bfb3cb2fdc..00000000000 --- a/pkg/apis/feeds/logkey/constants.go +++ /dev/null @@ -1,36 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package logkey - -const ( - kNative = "knative.dev/" - - // ClusterEventSource is the key used for cluster scoped event source name in structured logs - ClusterEventSource = kNative + "clustereventsource" - - // ClusterEventType is the key used for cluster event type name in structured logs - ClusterEventType = kNative + "clustereventtype" - - // EventSource is the key used for event source name in structured logs - EventSource = kNative + "eventsource" - - // EventType is the key used for event type name in structured logs - EventType = kNative + "eventtype" - - // Feed is the key used for feed name in structured logs - Feed = kNative + "feed" -) diff --git a/pkg/apis/feeds/register.go b/pkg/apis/feeds/register.go deleted file mode 100644 index b47f805856a..00000000000 --- a/pkg/apis/feeds/register.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package feeds - -const ( - GroupName = "feeds.knative.dev" -) diff --git a/pkg/apis/feeds/v1alpha1/cluster_event_source_defaults.go b/pkg/apis/feeds/v1alpha1/cluster_event_source_defaults.go deleted file mode 100644 index ba86a0d2894..00000000000 --- a/pkg/apis/feeds/v1alpha1/cluster_event_source_defaults.go +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -func (es *ClusterEventSource) SetDefaults() { - es.Spec.SetDefaults() -} - -func (ess *ClusterEventSourceSpec) SetDefaults() { - ess.CommonEventSourceSpec.SetDefaults() -} diff --git a/pkg/apis/feeds/v1alpha1/cluster_event_source_types.go b/pkg/apis/feeds/v1alpha1/cluster_event_source_types.go deleted file mode 100644 index 08fd3f98539..00000000000 --- a/pkg/apis/feeds/v1alpha1/cluster_event_source_types.go +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "encoding/json" - - "github.com/knative/pkg/apis" - "github.com/knative/pkg/webhook" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// +genclient -// +genclient:nonNamespaced -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// ClusterEventSource represents a software system which wishes to make changes in -// state discoverable via eventing, without prior knowledge of systems which -// might consume state changes. ClusterEventSources produce events that the Feed -// resource connects to consumers. -type ClusterEventSource struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec ClusterEventSourceSpec `json:"spec"` - Status ClusterEventSourceStatus `json:"status"` -} - -// Check that ClusterEventSource can be validated, can be defaulted, and has immutable fields. -var _ apis.Validatable = (*ClusterEventSource)(nil) -var _ apis.Defaultable = (*ClusterEventSource)(nil) -var _ runtime.Object = (*ClusterEventSource)(nil) -var _ webhook.GenericCRD = (*ClusterEventSource)(nil) - -// ClusterEventSourceSpec describes the type and source of an event, a container image -// to run for feed lifecycle operations, and configuration options for the -// ClusterEventSource. -type ClusterEventSourceSpec struct { - CommonEventSourceSpec `json:",inline"` -} - -// ClusterEventSourceStatus is the status for a ClusterEventSource resource -type ClusterEventSourceStatus struct { - CommonEventSourceStatus `json:",inline"` -} - -func (es *ClusterEventSource) GetSpecJSON() ([]byte, error) { - return json.Marshal(es.Spec) -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// ClusterEventSourceList is a list of ClusterEventSource resources -type ClusterEventSourceList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []ClusterEventSource `json:"items"` -} diff --git a/pkg/apis/feeds/v1alpha1/cluster_event_source_validation.go b/pkg/apis/feeds/v1alpha1/cluster_event_source_validation.go deleted file mode 100644 index 96451fc1d5d..00000000000 --- a/pkg/apis/feeds/v1alpha1/cluster_event_source_validation.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/pkg/apis" -) - -func (es *ClusterEventSource) Validate() *apis.FieldError { - return es.Spec.Validate().ViaField("spec") -} - -func (ess *ClusterEventSourceSpec) Validate() *apis.FieldError { - return ess.CommonEventSourceSpec.Validate() -} diff --git a/pkg/apis/feeds/v1alpha1/cluster_event_type_defaults.go b/pkg/apis/feeds/v1alpha1/cluster_event_type_defaults.go deleted file mode 100644 index dc8c615029a..00000000000 --- a/pkg/apis/feeds/v1alpha1/cluster_event_type_defaults.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -func (cet *ClusterEventType) SetDefaults() { - cet.Spec.SetDefaults() -} - -func (cets *ClusterEventTypeSpec) SetDefaults() { - cets.CommonEventTypeSpec.SetDefaults() - // TODO anything? -} diff --git a/pkg/apis/feeds/v1alpha1/cluster_event_type_types.go b/pkg/apis/feeds/v1alpha1/cluster_event_type_types.go deleted file mode 100644 index f54a32537b7..00000000000 --- a/pkg/apis/feeds/v1alpha1/cluster_event_type_types.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "encoding/json" - "github.com/knative/pkg/apis" - "github.com/knative/pkg/webhook" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// +genclient -// +genclient:nonNamespaced -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// ClusterEventType is a specification for a ClusterEventType resource -// EventSource can expose multiple event types. For example, github -// has PullRequest events as well as Issues and Comments, etc. -type ClusterEventType struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec ClusterEventTypeSpec `json:"spec"` - Status ClusterEventTypeStatus `json:"status"` -} - -// Check that ClusterEventType can be validated, can be defaulted, and has immutable fields. -var _ apis.Validatable = (*ClusterEventType)(nil) -var _ apis.Defaultable = (*ClusterEventType)(nil) -var _ apis.Immutable = (*ClusterEventType)(nil) -var _ runtime.Object = (*ClusterEventType)(nil) -var _ webhook.GenericCRD = (*ClusterEventType)(nil) - -// ClusterEventTypeSpec specifies information about the ClusterEventType, including a schema -// for the event and information about the parameters needed to create a Feed to -// the event. -type ClusterEventTypeSpec struct { - CommonEventTypeSpec `json:",inline"` - // ClusterEventSource is the name of the ClusterEventSource that produces this ClusterEventType. - ClusterEventSource string `json:"eventSource"` -} - -// ClusterEventTypeStatus is the status for a ClusterEventType resource -type ClusterEventTypeStatus struct { - CommonEventTypeStatus `json:",inline"` -} - -func (et *ClusterEventType) GetSpecJSON() ([]byte, error) { - return json.Marshal(et.Spec) -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// ClusterEventTypeList is a list of ClusterEventType resources -type ClusterEventTypeList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []ClusterEventType `json:"items"` -} diff --git a/pkg/apis/feeds/v1alpha1/cluster_event_type_validation.go b/pkg/apis/feeds/v1alpha1/cluster_event_type_validation.go deleted file mode 100644 index b809348f459..00000000000 --- a/pkg/apis/feeds/v1alpha1/cluster_event_type_validation.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/pkg/apis" - "k8s.io/apimachinery/pkg/util/validation" -) - -func (et *ClusterEventType) Validate() *apis.FieldError { - return et.Spec.Validate().ViaField("spec") -} - -func (ets *ClusterEventTypeSpec) Validate() *apis.FieldError { - if ets.ClusterEventSource == "" { - return apis.ErrMissingField("eventSource") - } - if errs := validation.IsQualifiedName(ets.ClusterEventSource); len(errs) > 0 { - return apis.ErrInvalidValue(ets.ClusterEventSource, "eventSource") - } - return ets.CommonEventTypeSpec.Validate() -} - -func (current *ClusterEventType) CheckImmutableFields(og apis.Immutable) *apis.FieldError { - original, ok := og.(*ClusterEventType) - if !ok { - return &apis.FieldError{Message: "The provided original was not a ClusterEventType"} - } - if original == nil { - return nil - } - - // EventSource for an EventType should not change. - if original.Spec.ClusterEventSource != current.Spec.ClusterEventSource { - return &apis.FieldError{ - Message: "Immutable fields changed", - Paths: []string{"spec.eventSource"}, - } - } - - return nil -} diff --git a/pkg/apis/feeds/v1alpha1/cluster_event_type_validation_test.go b/pkg/apis/feeds/v1alpha1/cluster_event_type_validation_test.go deleted file mode 100644 index 6bcb34d51b1..00000000000 --- a/pkg/apis/feeds/v1alpha1/cluster_event_type_validation_test.go +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/google/go-cmp/cmp" - "github.com/knative/pkg/apis" - "testing" -) - -func TestClusterEventTypeSpecValidation(t *testing.T) { - tests := []struct { - name string - et *ClusterEventTypeSpec - want *apis.FieldError - }{{ - name: "valid", - et: &ClusterEventTypeSpec{ - ClusterEventSource: "foo", - }, - }, { - name: "invalid source", - et: &ClusterEventTypeSpec{ - ClusterEventSource: "f@o", - }, - want: apis.ErrInvalidValue("f@o", "eventSource"), - }, { - name: "empty", - et: &ClusterEventTypeSpec{}, - want: apis.ErrMissingField("eventSource"), - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.et.Validate() - if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("validateClusterEventType (-want, +got) = %v", diff) - } - }) - } -} - -func TestClusterEventTypeImmutableFields(t *testing.T) { - tests := []struct { - name string - new apis.Immutable - old apis.Immutable - want *apis.FieldError - }{{ - name: "good (no change)", - new: &ClusterEventType{ - Spec: ClusterEventTypeSpec{ - ClusterEventSource: "foo", - }, - }, - old: &ClusterEventType{ - Spec: ClusterEventTypeSpec{ - ClusterEventSource: "foo", - }, - }, - want: nil, - }, { - name: "good (description change)", - new: &ClusterEventType{ - Spec: ClusterEventTypeSpec{ - ClusterEventSource: "foo", - CommonEventTypeSpec: CommonEventTypeSpec{ - Description: "Foo foo foo.", - }, - }, - }, - old: &ClusterEventType{ - Spec: ClusterEventTypeSpec{ - ClusterEventSource: "foo", - CommonEventTypeSpec: CommonEventTypeSpec{ - Description: "Bar bar bar.", - }, - }, - }, - want: nil, - }, { - name: "bad (source changes)", - new: &ClusterEventType{ - Spec: ClusterEventTypeSpec{ - ClusterEventSource: "foo", - }, - }, - old: &ClusterEventType{ - Spec: ClusterEventTypeSpec{ - ClusterEventSource: "bar", - }, - }, - want: &apis.FieldError{ - Message: "Immutable fields changed", - Paths: []string{"spec.eventSource"}, - }, - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.new.CheckImmutableFields(test.old) - if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("Validate (-want, +got) = %v", diff) - } - }) - } -} diff --git a/pkg/apis/feeds/v1alpha1/common_event_source_defaults.go b/pkg/apis/feeds/v1alpha1/common_event_source_defaults.go deleted file mode 100644 index fc51100a254..00000000000 --- a/pkg/apis/feeds/v1alpha1/common_event_source_defaults.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -func (cess *CommonEventSourceSpec) SetDefaults() { - // nothing to default -} diff --git a/pkg/apis/feeds/v1alpha1/common_event_source_test.go b/pkg/apis/feeds/v1alpha1/common_event_source_test.go deleted file mode 100644 index c812aa40cfa..00000000000 --- a/pkg/apis/feeds/v1alpha1/common_event_source_test.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "fmt" - "testing" -) - -func TestCommonEventSourceCondition_SetCondition(t *testing.T) { - testcases := []struct { - name string - types []CommonEventSourceConditionType - want int - }{ - {"Simple", []CommonEventSourceConditionType{EventSourceComplete}, 1}, - {"Two", []CommonEventSourceConditionType{EventSourceComplete, EventSourceFailed}, 2}, - {"Override", []CommonEventSourceConditionType{EventSourceComplete, EventSourceComplete}, 1}, - {"Invalid", []CommonEventSourceConditionType{""}, 0}, - } - - // EventSource - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "EventSource", tc.name) - t.Run(testName, func(t *testing.T) { - src := EventSource{} - for _, t := range tc.types { - c := &CommonEventSourceCondition{ - Type: t, - } - src.Status.SetCondition(c) - } - got := len(src.Status.Conditions) - if tc.want != got { - t.Fatalf("Failed to return expecected number of conditions. \nwant:\t%#v\ngot:\t%#v", tc.want, got) - } - }) - } - // ClusterEventSource - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "ClusterEventSource", tc.name) - t.Run(testName, func(t *testing.T) { - src := ClusterEventSource{} - for _, t := range tc.types { - c := &CommonEventSourceCondition{ - Type: t, - } - src.Status.SetCondition(c) - } - got := len(src.Status.Conditions) - if tc.want != got { - t.Fatalf("Failed to return expecected number of conditions. \nwant:\t%#v\ngot:\t%#v", tc.want, got) - } - }) - } -} - -func TestCommonEventSourceCondition_RemoveCondition(t *testing.T) { - testcases := []struct { - name string - set []CommonEventSourceConditionType - remove []CommonEventSourceConditionType - want int - }{ - {"Simple", []CommonEventSourceConditionType{EventSourceComplete}, []CommonEventSourceConditionType{EventSourceComplete}, 0}, - {"One", []CommonEventSourceConditionType{EventSourceComplete, EventSourceFailed}, []CommonEventSourceConditionType{EventSourceComplete}, 1}, - {"Missing", []CommonEventSourceConditionType{EventSourceComplete}, []CommonEventSourceConditionType{EventSourceFailed}, 1}, - {"Invalid", []CommonEventSourceConditionType{EventSourceComplete}, []CommonEventSourceConditionType{""}, 1}, - } - - // EventSource - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "EventSource", tc.name) - t.Run(testName, func(t *testing.T) { - src := EventSource{} - for _, t := range tc.set { - c := &CommonEventSourceCondition{ - Type: t, - } - src.Status.SetCondition(c) - } - for _, t := range tc.remove { - src.Status.RemoveCondition(t) - } - - got := len(src.Status.Conditions) - if tc.want != got { - t.Fatalf("Failed to return expecected number of conditions. \nwant:\t%#v\ngot:\t%#v", tc.want, got) - } - }) - } - - // ClusterEventSource - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "ClusterEventSource", tc.name) - t.Run(testName, func(t *testing.T) { - src := ClusterEventSource{} - for _, t := range tc.set { - c := &CommonEventSourceCondition{ - Type: t, - } - src.Status.SetCondition(c) - } - for _, t := range tc.remove { - src.Status.RemoveCondition(t) - } - - got := len(src.Status.Conditions) - if tc.want != got { - t.Fatalf("Failed to return expecected number of conditions. \nwant:\t%#v\ngot:\t%#v", tc.want, got) - } - }) - } -} diff --git a/pkg/apis/feeds/v1alpha1/common_event_source_types.go b/pkg/apis/feeds/v1alpha1/common_event_source_types.go deleted file mode 100644 index a3a32b378d1..00000000000 --- a/pkg/apis/feeds/v1alpha1/common_event_source_types.go +++ /dev/null @@ -1,112 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/pkg/apis" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// CommonEventSourceSpec describes the type and source of an event, a container image -// to run for feed lifecycle operations, and configuration options common for -// ClusterEventSource and EventSource. -type CommonEventSourceSpec struct { - // TODO: Generation does not work correctly with CRD. They are scrubbed - // by the APIserver (https://github.com/kubernetes/kubernetes/issues/58778) - // So, we add Generation here. Once that gets fixed, remove this and use - // ObjectMeta.Generation instead. - // +optional - Generation int64 `json:"generation,omitempty"` - - // Source is the name of the source that produces the events. - Source string `json:"source,omitempty"` - - // Image is the container image to run for feed lifecycle operations. - // - // TODO: make this a container - // TODO: specify exactly when containers are run - Image string `json:"image,omitempty"` - - // Parameters are configuration options for a particular EventSource - // TODO: Consider instead using ConfigMaps and mount them instead - // on the event sources containers. - Parameters *runtime.RawExtension `json:"parameters,omitempty"` -} - -// Check that CommonEventSourceSpec can be validated and can be defaulted -var _ apis.Validatable = (*CommonEventSourceSpec)(nil) -var _ apis.Defaultable = (*CommonEventSourceSpec)(nil) - -// EventSourceStatus is the status for a EventSource resource -type CommonEventSourceStatus struct { - Conditions []CommonEventSourceCondition `json:"conditions,omitempty"` -} - -type CommonEventSourceConditionType string - -const ( - // EventSourceComplete specifies that the EventSource has completed successfully. - EventSourceComplete CommonEventSourceConditionType = "Complete" - // EventSourceFailed specifies that the EventSource has failed. - EventSourceFailed CommonEventSourceConditionType = "Failed" - // EventSourceInvalid specifies that the given EventSource specification is invalid. - EventSourceInvalid CommonEventSourceConditionType = "Invalid" -) - -// CommonEventSourceCondition defines a readiness condition for a EventSource. -// See: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#typical-status-properties -type CommonEventSourceCondition struct { - Type CommonEventSourceConditionType `json:"type"` - - Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` - - // +optional - Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"` - // +optional - Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"` -} - -func (ess *CommonEventSourceStatus) SetCondition(new *CommonEventSourceCondition) { - if new == nil || new.Type == "" { - return - } - - t := new.Type - var conditions []CommonEventSourceCondition - for _, cond := range ess.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - conditions = append(conditions, *new) - ess.Conditions = conditions -} - -func (ess *CommonEventSourceStatus) RemoveCondition(t CommonEventSourceConditionType) { - if t == "" { - return - } - - var conditions []CommonEventSourceCondition - for _, cond := range ess.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - ess.Conditions = conditions -} diff --git a/pkg/apis/feeds/v1alpha1/common_event_source_validation.go b/pkg/apis/feeds/v1alpha1/common_event_source_validation.go deleted file mode 100644 index 8499e9f879c..00000000000 --- a/pkg/apis/feeds/v1alpha1/common_event_source_validation.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/pkg/apis" -) - -func (cess *CommonEventSourceSpec) Validate() *apis.FieldError { - // nothing to validate - return nil -} diff --git a/pkg/apis/feeds/v1alpha1/common_event_type_defaults.go b/pkg/apis/feeds/v1alpha1/common_event_type_defaults.go deleted file mode 100644 index 5e42df2ce04..00000000000 --- a/pkg/apis/feeds/v1alpha1/common_event_type_defaults.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -func (cets *CommonEventTypeSpec) SetDefaults() { - // TODO anything? -} diff --git a/pkg/apis/feeds/v1alpha1/common_event_type_test.go b/pkg/apis/feeds/v1alpha1/common_event_type_test.go deleted file mode 100644 index 9d57924b0c6..00000000000 --- a/pkg/apis/feeds/v1alpha1/common_event_type_test.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "fmt" - "testing" -) - -func TestCommonEventTypeCondition_SetCondition(t *testing.T) { - testcases := []struct { - name string - types []CommonEventTypeConditionType - want int - }{ - {"Simple", []CommonEventTypeConditionType{EventTypeComplete}, 1}, - {"Two", []CommonEventTypeConditionType{EventTypeComplete, EventTypeFailed}, 2}, - {"Override", []CommonEventTypeConditionType{EventTypeComplete, EventTypeComplete}, 1}, - {"Invalid", []CommonEventTypeConditionType{""}, 0}, - } - - // EventType - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "EventType", tc.name) - t.Run(testName, func(t *testing.T) { - src := EventType{} - for _, t := range tc.types { - c := &CommonEventTypeCondition{ - Type: t, - } - src.Status.SetCondition(c) - } - got := len(src.Status.Conditions) - if tc.want != got { - t.Fatalf("Failed to return expecected number of conditions. \nwant:\t%#v\ngot:\t%#v", tc.want, got) - } - }) - } - // ClusterEventType - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "ClusterEventType", tc.name) - t.Run(testName, func(t *testing.T) { - src := ClusterEventType{} - for _, t := range tc.types { - c := &CommonEventTypeCondition{ - Type: t, - } - src.Status.SetCondition(c) - } - got := len(src.Status.Conditions) - if tc.want != got { - t.Fatalf("Failed to return expecected number of conditions. \nwant:\t%#v\ngot:\t%#v", tc.want, got) - } - }) - } -} - -func TestCommonEventTypeCondition_RemoveCondition(t *testing.T) { - testcases := []struct { - name string - set []CommonEventTypeConditionType - remove []CommonEventTypeConditionType - want int - }{ - {"Simple", []CommonEventTypeConditionType{EventTypeComplete}, []CommonEventTypeConditionType{EventTypeComplete}, 0}, - {"One", []CommonEventTypeConditionType{EventTypeComplete, EventTypeFailed}, []CommonEventTypeConditionType{EventTypeComplete}, 1}, - {"Missing", []CommonEventTypeConditionType{EventTypeComplete}, []CommonEventTypeConditionType{EventTypeFailed}, 1}, - {"Invalid", []CommonEventTypeConditionType{EventTypeComplete}, []CommonEventTypeConditionType{""}, 1}, - } - - // EventType - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "EventType", tc.name) - t.Run(testName, func(t *testing.T) { - src := EventType{} - for _, t := range tc.set { - c := &CommonEventTypeCondition{ - Type: t, - } - src.Status.SetCondition(c) - } - for _, t := range tc.remove { - src.Status.RemoveCondition(t) - } - - got := len(src.Status.Conditions) - if tc.want != got { - t.Fatalf("Failed to return expecected number of conditions. \nwant:\t%#v\ngot:\t%#v", tc.want, got) - } - }) - } - - // ClusterEventType - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "ClusterEventType", tc.name) - t.Run(testName, func(t *testing.T) { - src := ClusterEventType{} - for _, t := range tc.set { - c := &CommonEventTypeCondition{ - Type: t, - } - src.Status.SetCondition(c) - } - for _, t := range tc.remove { - src.Status.RemoveCondition(t) - } - - got := len(src.Status.Conditions) - if tc.want != got { - t.Fatalf("Failed to return expecected number of conditions. \nwant:\t%#v\ngot:\t%#v", tc.want, got) - } - }) - } -} diff --git a/pkg/apis/feeds/v1alpha1/common_event_type_types.go b/pkg/apis/feeds/v1alpha1/common_event_type_types.go deleted file mode 100644 index c4c7b7841c8..00000000000 --- a/pkg/apis/feeds/v1alpha1/common_event_type_types.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/pkg/apis" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// CommonEventTypeSpec specifies information about the [Cluster]EventType, -// including a schema for the event and information about the parameters -// needed to create a Feed to the event. -type CommonEventTypeSpec struct { - // TODO: Generation does not work correctly with CRD. They are scrubbed - // by the APIserver (https://github.com/kubernetes/kubernetes/issues/58778) - // So, we add Generation here. Once that gets fixed, remove this and use - // ObjectMeta.Generation instead. - // +optional - Generation int64 `json:"generation,omitempty"` - - // Description is a human-readable description of the EventType. - Description string `json:"description,omitempty"` - // SubscribeSchema describing how to subscribe to this. This basically - // defines what is required in the Feed.Parameters so that the developer - // can see the required parameters. - SubscribeSchema *runtime.RawExtension `json:"subscribeSchema,omitempty"` - // Describe the schema for the events emitted by this EventType. - EventSchema *runtime.RawExtension `json:"eventSchema,omitempty"` -} - -// Check that CommonEventTypeSpec can be validated and can be defaulted -var _ apis.Validatable = (*CommonEventTypeSpec)(nil) -var _ apis.Defaultable = (*CommonEventTypeSpec)(nil) - -// CommonEventTypeStatus is the status for a EventType resource -type CommonEventTypeStatus struct { - Conditions []CommonEventTypeCondition `json:"conditions,omitempty"` -} - -type CommonEventTypeConditionType string - -const ( - // EventTypeComplete specifies that the EventType has completed successfully. - EventTypeComplete CommonEventTypeConditionType = "Complete" - // EventTypeFailed specifies that the EventType has failed. - EventTypeFailed CommonEventTypeConditionType = "Failed" - // EventTypeInvalid specifies that the given EventType specification is invalid. - EventTypeInvalid CommonEventTypeConditionType = "Invalid" - // EventTypeInUse specifies that the given EventType, which is marked for deletion, still has - // Feeds that depend on it, so cannot yet be deleted. - EventTypeInUse CommonEventTypeConditionType = "InUse" -) - -// EventTypeCondition defines a readiness condition for a EventType. -// See: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#typical-status-properties -type CommonEventTypeCondition struct { - Type CommonEventTypeConditionType `json:"type"` - - Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` - - // +optional - Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"` - // +optional - Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"` -} - -func (ets *CommonEventTypeStatus) SetCondition(new *CommonEventTypeCondition) { - if new == nil || new.Type == "" { - return - } - - t := new.Type - var conditions []CommonEventTypeCondition - for _, cond := range ets.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - conditions = append(conditions, *new) - ets.Conditions = conditions -} - -func (ets *CommonEventTypeStatus) RemoveCondition(t CommonEventTypeConditionType) { - if t == "" { - return - } - - var conditions []CommonEventTypeCondition - for _, cond := range ets.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - ets.Conditions = conditions -} diff --git a/pkg/apis/feeds/v1alpha1/common_event_type_validation.go b/pkg/apis/feeds/v1alpha1/common_event_type_validation.go deleted file mode 100644 index 9457e14a422..00000000000 --- a/pkg/apis/feeds/v1alpha1/common_event_type_validation.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/pkg/apis" -) - -func (cess *CommonEventTypeSpec) Validate() *apis.FieldError { - // nothing to validate. - return nil -} diff --git a/pkg/apis/feeds/v1alpha1/doc.go b/pkg/apis/feeds/v1alpha1/doc.go deleted file mode 100644 index 45e3270570a..00000000000 --- a/pkg/apis/feeds/v1alpha1/doc.go +++ /dev/null @@ -1,16 +0,0 @@ -/* -Copyright 2018 The Knative Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - http://www.apache.org/licenses/LICENSE-2.0 -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -// +k8s:deepcopy-gen=package -// Package v1alpha1 is the v1alpha1 version of the API. -// +groupName=feeds.knative.dev -package v1alpha1 diff --git a/pkg/apis/feeds/v1alpha1/event_source_defaults.go b/pkg/apis/feeds/v1alpha1/event_source_defaults.go deleted file mode 100644 index b49b8b0f0b0..00000000000 --- a/pkg/apis/feeds/v1alpha1/event_source_defaults.go +++ /dev/null @@ -1,25 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -func (es *EventSource) SetDefaults() { - es.Spec.SetDefaults() -} - -func (ess *EventSourceSpec) SetDefaults() { - ess.CommonEventSourceSpec.SetDefaults() -} diff --git a/pkg/apis/feeds/v1alpha1/event_source_types.go b/pkg/apis/feeds/v1alpha1/event_source_types.go deleted file mode 100644 index 84776c4c381..00000000000 --- a/pkg/apis/feeds/v1alpha1/event_source_types.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "encoding/json" - "github.com/knative/pkg/apis" - "github.com/knative/pkg/webhook" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// EventSource represents a software system which wishes to make changes in -// state discoverable via eventing, without prior knowledge of systems which -// might consume state changes. EventSources produce events that the Feed -// resource connects to consumers. -type EventSource struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec EventSourceSpec `json:"spec"` - Status EventSourceStatus `json:"status"` -} - -// Check that EventSource can be validated, can be defaulted, and has immutable fields. -var _ apis.Validatable = (*EventSource)(nil) -var _ apis.Defaultable = (*EventSource)(nil) -var _ runtime.Object = (*EventSource)(nil) -var _ webhook.GenericCRD = (*EventSource)(nil) - -// EventSourceSpec describes the type and source of an event, a container image -// to run for feed lifecycle operations, and configuration options for the -// EventSource. -type EventSourceSpec struct { - CommonEventSourceSpec `json:",inline"` -} - -// EventSourceStatus is the status for a EventSource resource -type EventSourceStatus struct { - CommonEventSourceStatus `json:",inline"` -} - -func (es *EventSource) GetSpecJSON() ([]byte, error) { - return json.Marshal(es.Spec) -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// EventSourceList is a list of EventSource resources -type EventSourceList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []EventSource `json:"items"` -} diff --git a/pkg/apis/feeds/v1alpha1/event_source_validation.go b/pkg/apis/feeds/v1alpha1/event_source_validation.go deleted file mode 100644 index 277babf6ea3..00000000000 --- a/pkg/apis/feeds/v1alpha1/event_source_validation.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/pkg/apis" -) - -func (es *EventSource) Validate() *apis.FieldError { - return es.Spec.Validate().ViaField("spec") -} - -func (ess *EventSourceSpec) Validate() *apis.FieldError { - return ess.CommonEventSourceSpec.Validate() -} diff --git a/pkg/apis/feeds/v1alpha1/event_type_defaults.go b/pkg/apis/feeds/v1alpha1/event_type_defaults.go deleted file mode 100644 index 20fa4adab8b..00000000000 --- a/pkg/apis/feeds/v1alpha1/event_type_defaults.go +++ /dev/null @@ -1,26 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -func (et *EventType) SetDefaults() { - et.Spec.SetDefaults() -} - -func (ets *EventTypeSpec) SetDefaults() { - ets.CommonEventTypeSpec.SetDefaults() - // TODO anything? -} diff --git a/pkg/apis/feeds/v1alpha1/event_type_types.go b/pkg/apis/feeds/v1alpha1/event_type_types.go deleted file mode 100644 index 425b155902e..00000000000 --- a/pkg/apis/feeds/v1alpha1/event_type_types.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "encoding/json" - - "github.com/knative/pkg/apis" - "github.com/knative/pkg/webhook" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" -) - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// EventType is a specification for a EventType resource -// EventSource can expose multiple event types. For example, github -// has PullRequest events as well as Issues and Comments, etc. -type EventType struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec EventTypeSpec `json:"spec"` - Status EventTypeStatus `json:"status"` -} - -// Check that EventType can be validated, can be defaulted, and has immutable fields. -var _ apis.Validatable = (*EventType)(nil) -var _ apis.Defaultable = (*EventType)(nil) -var _ apis.Immutable = (*EventType)(nil) -var _ runtime.Object = (*EventType)(nil) -var _ webhook.GenericCRD = (*EventType)(nil) - -// EventTypeSpec specifies information about the EventType, including a schema -// for the event and information about the parameters needed to create a Feed to -// the event. -type EventTypeSpec struct { - CommonEventTypeSpec `json:",inline"` - // EventSource is the name of the EventSource that produces this EventType. - EventSource string `json:"eventSource"` -} - -// EventTypeStatus is the status for a EventType resource -type EventTypeStatus struct { - CommonEventTypeStatus `json:",inline"` -} - -func (et *EventType) GetSpecJSON() ([]byte, error) { - return json.Marshal(et.Spec) -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// EventTypeList is a list of EventType resources -type EventTypeList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []EventType `json:"items"` -} diff --git a/pkg/apis/feeds/v1alpha1/event_type_validation.go b/pkg/apis/feeds/v1alpha1/event_type_validation.go deleted file mode 100644 index dd53e1b5dec..00000000000 --- a/pkg/apis/feeds/v1alpha1/event_type_validation.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/pkg/apis" - "k8s.io/apimachinery/pkg/util/validation" -) - -func (et *EventType) Validate() *apis.FieldError { - return et.Spec.Validate().ViaField("spec") -} - -func (ets *EventTypeSpec) Validate() *apis.FieldError { - if ets.EventSource == "" { - return apis.ErrMissingField("eventSource") - } - if errs := validation.IsQualifiedName(ets.EventSource); len(errs) > 0 { - return apis.ErrInvalidValue(ets.EventSource, "eventSource") - } - return ets.CommonEventTypeSpec.Validate() -} - -func (current *EventType) CheckImmutableFields(og apis.Immutable) *apis.FieldError { - original, ok := og.(*EventType) - if !ok { - return &apis.FieldError{Message: "The provided original was not a EventType"} - } - if original == nil { - return nil - } - - // EventSource for an EventType should not change. - if original.Spec.EventSource != current.Spec.EventSource { - return &apis.FieldError{ - Message: "Immutable fields changed", - Paths: []string{"spec.eventSource"}, - } - } - - return nil -} diff --git a/pkg/apis/feeds/v1alpha1/event_type_validation_test.go b/pkg/apis/feeds/v1alpha1/event_type_validation_test.go deleted file mode 100644 index ff794b920f1..00000000000 --- a/pkg/apis/feeds/v1alpha1/event_type_validation_test.go +++ /dev/null @@ -1,121 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/google/go-cmp/cmp" - "github.com/knative/pkg/apis" - "testing" -) - -func TestEventTypeSpecValidation(t *testing.T) { - tests := []struct { - name string - et *EventTypeSpec - want *apis.FieldError - }{{ - name: "valid", - et: &EventTypeSpec{ - EventSource: "foo", - }, - }, { - name: "invalid source", - et: &EventTypeSpec{ - EventSource: "f@o", - }, - want: apis.ErrInvalidValue("f@o", "eventSource"), - }, { - name: "empty", - et: &EventTypeSpec{}, - want: apis.ErrMissingField("eventSource"), - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.et.Validate() - if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("validateClusterEventType (-want, +got) = %v", diff) - } - }) - } -} - -func TestEventTypeImmutableFields(t *testing.T) { - tests := []struct { - name string - new apis.Immutable - old apis.Immutable - want *apis.FieldError - }{{ - name: "good (no change)", - new: &EventType{ - Spec: EventTypeSpec{ - EventSource: "foo", - }, - }, - old: &EventType{ - Spec: EventTypeSpec{ - EventSource: "foo", - }, - }, - want: nil, - }, { - name: "good (description change)", - new: &EventType{ - Spec: EventTypeSpec{ - EventSource: "foo", - CommonEventTypeSpec: CommonEventTypeSpec{ - Description: "Foo foo foo.", - }, - }, - }, - old: &EventType{ - Spec: EventTypeSpec{ - EventSource: "foo", - CommonEventTypeSpec: CommonEventTypeSpec{ - Description: "Bar bar bar.", - }, - }, - }, - want: nil, - }, { - name: "bad (source changes)", - new: &EventType{ - Spec: EventTypeSpec{ - EventSource: "foo", - }, - }, - old: &EventType{ - Spec: EventTypeSpec{ - EventSource: "bar", - }, - }, - want: &apis.FieldError{ - Message: "Immutable fields changed", - Paths: []string{"spec.eventSource"}, - }, - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.new.CheckImmutableFields(test.old) - if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("Validate (-want, +got) = %v", diff) - } - }) - } -} diff --git a/pkg/apis/feeds/v1alpha1/feed_defaults.go b/pkg/apis/feeds/v1alpha1/feed_defaults.go deleted file mode 100644 index 30f6d3c3b51..00000000000 --- a/pkg/apis/feeds/v1alpha1/feed_defaults.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -func (f *Feed) SetDefaults() { - f.Spec.SetDefaults() -} - -func (fs *FeedSpec) SetDefaults() { - if fs.ServiceAccountName == "" { - fs.ServiceAccountName = "default" - } -} diff --git a/pkg/apis/feeds/v1alpha1/feed_defaults_test.go b/pkg/apis/feeds/v1alpha1/feed_defaults_test.go deleted file mode 100644 index 9e1d0577428..00000000000 --- a/pkg/apis/feeds/v1alpha1/feed_defaults_test.go +++ /dev/null @@ -1,79 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/google/go-cmp/cmp" - "testing" -) - -func TestFeedDefaults(t *testing.T) { - tests := []struct { - name string - f *Feed - want *Feed - }{{ - name: "valid (no change)", - f: &Feed{ - Spec: FeedSpec{ - Trigger: EventTrigger{ - EventType: "foo", - Resource: "baz", - }, - ServiceAccountName: "bar", - }, - }, - want: &Feed{ - Spec: FeedSpec{ - Trigger: EventTrigger{ - EventType: "foo", - Resource: "baz", - }, - ServiceAccountName: "bar", - }, - }, - }, { - name: "valid (default service account)", - f: &Feed{ - Spec: FeedSpec{ - Trigger: EventTrigger{ - EventType: "foo", - Resource: "baz", - }, - }, - }, - want: &Feed{ - Spec: FeedSpec{ - Trigger: EventTrigger{ - EventType: "foo", - Resource: "baz", - }, - ServiceAccountName: "default", - }, - }, - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.f.DeepCopy() - got.SetDefaults() - if diff := cmp.Diff(test.want, got); diff != "" { - t.Errorf("defaultFeed (-want, +got) = %v", diff) - } - }) - } -} diff --git a/pkg/apis/feeds/v1alpha1/feed_types.go b/pkg/apis/feeds/v1alpha1/feed_types.go deleted file mode 100644 index 371ee15d377..00000000000 --- a/pkg/apis/feeds/v1alpha1/feed_types.go +++ /dev/null @@ -1,320 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "encoding/json" - - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/sets" - - "github.com/knative/pkg/apis" - "github.com/knative/pkg/webhook" - "k8s.io/apimachinery/pkg/runtime" -) - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Feed connects an event trigger with an action that processes events produced -// by the trigger. Feeds are building blocks used to implement Flows and are not -// expected to be used directly. -type Feed struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec FeedSpec `json:"spec"` - Status FeedStatus `json:"status"` -} - -var _ apis.Validatable = (*Feed)(nil) -var _ apis.Defaultable = (*Feed)(nil) -var _ runtime.Object = (*Feed)(nil) -var _ webhook.GenericCRD = (*Feed)(nil) - -// FeedSpec is the spec for a Feed resource. -type FeedSpec struct { - // TODO: Generation does not work correctly with CRD. They are scrubbed - // by the APIserver (https://github.com/kubernetes/kubernetes/issues/58778) - // So, we add Generation here. Once that gets fixed, remove this and use - // ObjectMeta.Generation instead. - // +optional - Generation int64 `json:"generation,omitempty"` - - // Action specifies the target handler for the events - Action FeedAction `json:"action"` - - // Trigger specifies the trigger producing events for this feed - Trigger EventTrigger `json:"trigger"` - - // Service Account to run feed container. If left out, uses "default" - ServiceAccountName string `json:"serviceAccountName,omitempty"` -} - -// FeedAction specifies the target handler - a Knative route or channel - for -// events produced by an EventTrigger. -type FeedAction struct { - // DNSName is the target for this feed. - DNSName string `json:"dnsName,omitempty"` -} - -// EventTrigger specifies the intention that a particular event type and -// resource should be consumed. -type EventTrigger struct { - // EventType or ClusterEventType Required. The type of event to observe. - // For example: - // `google.storage.object.finalize` and - // `google.firebase.analytics.event.log`. - // - // Event type consists of three parts: - // 1. namespace: The domain name of the organization in reverse-domain - // notation (e.g. `acme.net` appears as `net.acme`) and any orginization - // specific subdivisions. If the organization's top-level domain is `com`, - // the top-level domain is omitted (e.g. `google.com` appears as - // `google`). For example, `google.storage` and - // `google.firebase.analytics`. - // 2. resource type: The type of resource on which event occurs. For - // example, the Google Cloud Storage API includes the types `object` - // and `bucket`. - // 3. action: The action that generates the event. For example, actions for - // a Google Cloud Storage Object include 'finalize' and 'delete'. - // These parts are lower case and joined by '.'. - EventType string `json:"eventType"` - - // EventType or ClusterEventType Required. ClusterEventType is the same as - // EventType but Cluster scoped. - ClusterEventType string `json:"clusterEventType"` - - // Required. The resource(s) from which to observe events, for example, - // `projects/_/buckets/myBucket/objects/{objectPath=**}`. - // - // Can be a specific resource or use wildcards to match a set of resources. - // Wildcards can either match a single segment in the resource name, - // using '*', or multiple segments, using '**'. For example, - // `projects/myProject/buckets/*/objects/**` would match all objects in all - // buckets in the 'myProject' project. - // - // The contents of wildcards can also be captured. This is done by assigning - // it to a variable name in braces. For example, - // `projects/myProject/buckets/{bucket_id=*}/objects/{object_path=**}`. - // Additionally, a single segment capture can omit `=*` and a multiple segment - // capture can specify additional structure. For example, the following - // all match the same buckets, but capture different data: - // `projects/myProject/buckets/*/objects/users/*/data/**` - // `projects/myProject/buckets/{bucket_id=*}/objects/users/{user_id}/data/{data_path=**}` - // `projects/myProject/buckets/{bucket_id}/objects/{object_path=users/*/data/**}` - // - // Not all syntactically correct values are accepted by all services. For - // example: - // - // 1. The authorization model must support it. Google Cloud Functions - // only allows EventTriggers to be deployed that observe resources in the - // same project as the `CloudFunction`. - // 2. The resource type must match the pattern expected for an - // `event_type`. For example, an `EventTrigger` that has an - // `event_type` of "google.pubsub.topic.publish" should have a resource - // that matches Google Cloud Pub/Sub topics. - // - // Additionally, some services may support short names when creating an - // `EventTrigger`. These will always be returned in the normalized "long" - // format. - // - // See each *service's* documentation for supported formats. - Resource string `json:"resource"` - - // The hostname of the service that should be observed. - // - // If no string is provided, the default service implementing the API will - // be used. For example, `storage.googleapis.com` is the default for all - // event types in the 'google.storage` namespace. - Service string `json:"service"` - - // Parameters is what's necessary to create the subscription. - // This is specific to each Source. Opaque to platform, only consumed - // by the actual trigger actuator. - // NOTE: experimental field. - Parameters *runtime.RawExtension `json:"parameters,omitempty"` - - // ParametersFrom are pointers to secrets that contain sensitive - // parameters. Opaque to platform, merged in with Parameters and consumed - // by the actual trigger actuator. - // NOTE: experimental field. All secrets in ParametersFrom will be - // resolved and given to event sources in the Parameters field. - ParametersFrom []ParametersFromSource `json:"parametersFrom,omitempty"` -} - -// ParametersFromSource represents the source of a set of Parameters -// TODO: consider making this into a new secret type. -type ParametersFromSource struct { - // The Secret key to select from. - // The value must be a JSON object. - //+optional - SecretKeyRef *SecretKeyReference `json:"secretKeyRef,omitempty"` -} - -// SecretKeyReference references a key of a Secret. -type SecretKeyReference struct { - // The name of the secret in the resource's namespace to select from. - Name string `json:"name"` - // The key of the secret to select from. Must be a valid secret key. - Key string `json:"key"` -} - -// FeedStatus is the status for a Feed resource -type FeedStatus struct { - Conditions []FeedCondition `json:"conditions,omitempty"` - - // FeedContext is what the Feed operation returns and holds enough information - // for the event source to stop the Feed. - // This is specific to each Feed. Opaque to platform, only consumed - // by the actual trigger actuator. - // NOTE: experimental field. - FeedContext *runtime.RawExtension `json:"feedContext,omitempty"` -} - -type FeedConditionType string - -const ( - // FeedConditionReady specifies that the feed has started successfully. - FeedConditionReady FeedConditionType = "Ready" - - // FeedConditionDependenciesSatisfied specifies that all the dependencies for - // feed have been satisfied - FeedConditionDependenciesSatisfied FeedConditionType = "DependenciesSatisfied" -) - -// FeedCondition defines a readiness condition for a Feed. -// See: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#typical-status-properties -type FeedCondition struct { - Type FeedConditionType `json:"type"` - - Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` - // +optional - Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"` - // +optional - Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"` -} - -func (fs *FeedStatus) GetCondition(t FeedConditionType) *FeedCondition { - for _, cond := range fs.Conditions { - if cond.Type == t { - return &cond - } - } - return nil -} - -func (fs *FeedStatus) SetCondition(new *FeedCondition) { - if new == nil || new.Type == "" { - return - } - - t := new.Type - var conditions []FeedCondition - for _, cond := range fs.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - conditions = append(conditions, *new) - fs.Conditions = conditions -} - -func (fs *FeedStatus) RemoveCondition(t FeedConditionType) { - var conditions []FeedCondition - for _, cond := range fs.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - fs.Conditions = conditions -} - -func (fs *FeedStatus) InitializeConditions() { - for _, cond := range []FeedConditionType{ - FeedConditionReady, - } { - if fc := fs.GetCondition(cond); fc == nil { - fs.SetCondition(&FeedCondition{ - Type: cond, - Status: corev1.ConditionUnknown, - }) - } - } -} - -// AddFinalizer adds the given value to the list of finalizers if it doesn't -// already exist. -func (f *Feed) AddFinalizer(value string) { - finalizers := sets.NewString(f.GetFinalizers()...) - finalizers.Insert(value) - f.SetFinalizers(finalizers.List()) -} - -// RemoveFinalizer removes the given value from the list of finalizers if it -// exists. -func (f *Feed) RemoveFinalizer(value string) { - finalizers := sets.NewString(f.GetFinalizers()...) - finalizers.Delete(value) - if finalizers.Len() == 0 { - // if no finalizers, set to nil list, not an empty slice. - f.SetFinalizers([]string(nil)) - } else { - f.SetFinalizers(finalizers.List()) - } -} - -// HasFinalizer returns true if a finalizer exists, or false otherwise. -func (f *Feed) HasFinalizer(value string) bool { - for _, f := range f.GetFinalizers() { - if f == value { - return true - } - } - return false -} - -// SetOwnerReference adds the given owner reference to the list of owner -// references, replacing the corresponding owner reference if it exists. -func (f *Feed) SetOwnerReference(or *metav1.OwnerReference) { - var refs []metav1.OwnerReference - - for _, ref := range f.GetOwnerReferences() { - if !(ref.APIVersion == or.APIVersion && - ref.Kind == or.Kind && - ref.Name == or.Name) { - refs = append(refs, ref) - } - } - refs = append(refs, *or) - f.SetOwnerReferences(refs) -} - -func (f *Feed) GetSpecJSON() ([]byte, error) { - return json.Marshal(f.Spec) -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// FeedList is a list of Feed resources -type FeedList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []Feed `json:"items"` -} diff --git a/pkg/apis/feeds/v1alpha1/feed_types_test.go b/pkg/apis/feeds/v1alpha1/feed_types_test.go deleted file mode 100644 index 37068267b21..00000000000 --- a/pkg/apis/feeds/v1alpha1/feed_types_test.go +++ /dev/null @@ -1,112 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "fmt" - "testing" -) - -func TestFeedCondition_GetConditionNotFound(t *testing.T) { - feed := Feed{} - if feed.Status.GetCondition(FeedConditionReady) != nil { - t.Fatalf("Got a non-nil for non-existent conditiontype") - } -} - -func TestFeedCondition_GetCondition(t *testing.T) { - testcases := []struct { - name string - types []FeedConditionType - get FeedConditionType - want FeedConditionType - }{ - {"FeedConditionReady", []FeedConditionType{FeedConditionReady}, FeedConditionReady, FeedConditionReady}, - } - - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "Feed", tc.name) - t.Run(testName, func(t *testing.T) { - feed := Feed{} - for _, t := range tc.types { - c := &FeedCondition{Type: t} - feed.Status.SetCondition(c) - } - - if want, got := tc.want, feed.Status.GetCondition(tc.get).Type; want != got { - t.Fatalf("Failed to get expected condition. \nwant:\t%#v\ngot:\t%#v", want, got) - } - }) - } -} - -func TestFeedCondition_SetCondition(t *testing.T) { - testcases := []struct { - name string - types []FeedConditionType - want int - }{ - {"One", []FeedConditionType{FeedConditionReady}, 1}, - {"Replace", []FeedConditionType{FeedConditionReady, FeedConditionReady}, 1}, - {"Invalid", []FeedConditionType{""}, 0}, - } - - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "Feed", tc.name) - t.Run(testName, func(t *testing.T) { - feed := Feed{} - for _, t := range tc.types { - c := &FeedCondition{Type: t} - feed.Status.SetCondition(c) - } - if want, got := tc.want, len(feed.Status.Conditions); want != got { - t.Fatalf("Failed to return expected number of conditions. \nwant:\t%#v\ngot:\t%#v", want, got) - } - }) - } -} - -func TestFeedCondition_RemoveCondition(t *testing.T) { - testcases := []struct { - name string - set []FeedConditionType - remove []FeedConditionType - want int - }{ - {"RemoveOne", []FeedConditionType{FeedConditionReady}, []FeedConditionType{FeedConditionReady}, 0}, - {"RemoveNonExistent", []FeedConditionType{FeedConditionReady}, []FeedConditionType{"notthere"}, 1}, - {"Invalid", []FeedConditionType{FeedConditionReady}, []FeedConditionType{""}, 1}, - } - - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "Feed", tc.name) - t.Run(testName, func(t *testing.T) { - feed := Feed{} - for _, t := range tc.set { - c := &FeedCondition{Type: t} - feed.Status.SetCondition(c) - } - for _, t := range tc.remove { - feed.Status.RemoveCondition(t) - } - - if want, got := tc.want, len(feed.Status.Conditions); want != got { - t.Fatalf("Failed to return expected number of conditions. \nwant:\t%#v\ngot:\t%#v", want, got) - } - }) - } -} diff --git a/pkg/apis/feeds/v1alpha1/feed_validation.go b/pkg/apis/feeds/v1alpha1/feed_validation.go deleted file mode 100644 index c8d2ff879f4..00000000000 --- a/pkg/apis/feeds/v1alpha1/feed_validation.go +++ /dev/null @@ -1,72 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/pkg/apis" - - "k8s.io/apimachinery/pkg/util/validation" - "strings" -) - -func (f *Feed) Validate() *apis.FieldError { - return f.Spec.Validate().ViaField("spec") -} - -func (fs *FeedSpec) Validate() *apis.FieldError { - if err := fs.Trigger.Validate(); err != nil { - return err.ViaField("trigger") - } - if err := fs.Action.Validate(); err != nil { - return err.ViaField("action") - } - return nil -} - -func (et *EventTrigger) Validate() *apis.FieldError { - switch { - case len(et.EventType) != 0 && len(et.ClusterEventType) != 0: - return apis.ErrMultipleOneOf("eventType", "clusterEventType") - case len(et.EventType) != 0: - if errs := validation.IsQualifiedName(et.EventType); len(errs) > 0 { - return apis.ErrInvalidKeyName(et.EventType, "eventType", errs...) - } - case len(et.ClusterEventType) != 0: - if errs := validation.IsQualifiedName(et.ClusterEventType); len(errs) > 0 { - return apis.ErrInvalidKeyName(et.ClusterEventType, "clusterEventType", errs...) - } - default: - return apis.ErrMissingOneOf("eventType", "clusterEventType") - } - - if et.Resource == "" { - return apis.ErrMissingField("resource") - } - - return nil -} - -func (fa *FeedAction) Validate() *apis.FieldError { - if len(fa.DNSName) != 0 { - if errs := validation.IsDNS1123Subdomain(fa.DNSName); len(errs) > 0 { - err := apis.ErrInvalidValue(fa.DNSName, "dnsName") - err.Details = strings.Join(errs, ", ") - return err - } - } - return nil -} diff --git a/pkg/apis/feeds/v1alpha1/feed_validation_test.go b/pkg/apis/feeds/v1alpha1/feed_validation_test.go deleted file mode 100644 index c52e06ecae5..00000000000 --- a/pkg/apis/feeds/v1alpha1/feed_validation_test.go +++ /dev/null @@ -1,118 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/google/go-cmp/cmp" - "github.com/knative/pkg/apis" - "testing" -) - -func TestFeedSpecValidation(t *testing.T) { - tests := []struct { - name string - f *FeedSpec - want *apis.FieldError - }{{ - name: "valid namespaced", - f: &FeedSpec{ - Trigger: EventTrigger{ - EventType: "foo", - Resource: "baz", - }, - }, - }, { - name: "valid cluster", - f: &FeedSpec{ - Trigger: EventTrigger{ - ClusterEventType: "foo", - Resource: "baz", - }, - }, - }, { - name: "empty", - f: &FeedSpec{}, - want: apis.ErrMissingOneOf("eventType", "clusterEventType").ViaField("trigger"), - }, { - name: "empty trigger", - f: &FeedSpec{Trigger: EventTrigger{}}, - want: apis.ErrMissingOneOf("eventType", "clusterEventType").ViaField("trigger"), - }, { - name: "mutually exclusive missing", - f: &FeedSpec{ - ServiceAccountName: "Sue", - Trigger: EventTrigger{ - Resource: "baz", - }, - }, - want: apis.ErrMissingOneOf("eventType", "clusterEventType").ViaField("trigger"), - }, { - name: "mutually exclusive both", - f: &FeedSpec{ - Trigger: EventTrigger{ - EventType: "foo", - ClusterEventType: "bar", - Resource: "baz", - }, - }, - want: apis.ErrMultipleOneOf("eventType", "clusterEventType").ViaField("trigger"), - }, { - name: "valid dns name", - f: &FeedSpec{ - Trigger: EventTrigger{ - EventType: "foo", - Resource: "baz", - }, - Action: FeedAction{ - DNSName: "valid.com", - }, - }, - }, { - name: "missing trigger.resource", - f: &FeedSpec{ - Trigger: EventTrigger{ - EventType: "foo", - }, - }, - want: apis.ErrMissingField("resource").ViaField("trigger"), - }, { - name: "invalid dns name", - f: &FeedSpec{ - Trigger: EventTrigger{ - EventType: "foo", - Resource: "baz", - }, - Action: FeedAction{ - DNSName: "inv@lid.com", - }, - }, - want: &apis.FieldError{ - Message: `invalid value "inv@lid.com"`, - Paths: []string{"action.dnsName"}, - Details: "a DNS-1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character (e.g. 'example.com', regex used for validation is '[a-z0-9]([-a-z0-9]*[a-z0-9])?(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*')", - }, - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.f.Validate() - if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("validateFeed (-want, +got) = %v", diff) - } - }) - } -} diff --git a/pkg/apis/feeds/v1alpha1/register.go b/pkg/apis/feeds/v1alpha1/register.go deleted file mode 100644 index c5a15aa2f09..00000000000 --- a/pkg/apis/feeds/v1alpha1/register.go +++ /dev/null @@ -1,61 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/eventing/pkg/apis/feeds" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -// SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: feeds.GroupName, Version: "v1alpha1"} - -// Kind takes an unqualified kind and returns back a Group qualified GroupKind -func Kind(kind string) schema.GroupKind { - return SchemeGroupVersion.WithKind(kind).GroupKind() -} - -// Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -var ( - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - AddToScheme = SchemeBuilder.AddToScheme -) - -// Adds the list of known types to Scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &ClusterEventSource{}, - &ClusterEventSourceList{}, - &ClusterEventType{}, - &ClusterEventTypeList{}, - &Feed{}, - &FeedList{}, - &EventSource{}, - &EventSourceList{}, - &EventType{}, - &EventTypeList{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} diff --git a/pkg/apis/feeds/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/feeds/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index eefd57179a4..00000000000 --- a/pkg/apis/feeds/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,752 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterEventSource) DeepCopyInto(out *ClusterEventSource) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterEventSource. -func (in *ClusterEventSource) DeepCopy() *ClusterEventSource { - if in == nil { - return nil - } - out := new(ClusterEventSource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ClusterEventSource) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterEventSourceList) DeepCopyInto(out *ClusterEventSourceList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]ClusterEventSource, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterEventSourceList. -func (in *ClusterEventSourceList) DeepCopy() *ClusterEventSourceList { - if in == nil { - return nil - } - out := new(ClusterEventSourceList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ClusterEventSourceList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterEventSourceSpec) DeepCopyInto(out *ClusterEventSourceSpec) { - *out = *in - in.CommonEventSourceSpec.DeepCopyInto(&out.CommonEventSourceSpec) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterEventSourceSpec. -func (in *ClusterEventSourceSpec) DeepCopy() *ClusterEventSourceSpec { - if in == nil { - return nil - } - out := new(ClusterEventSourceSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterEventSourceStatus) DeepCopyInto(out *ClusterEventSourceStatus) { - *out = *in - in.CommonEventSourceStatus.DeepCopyInto(&out.CommonEventSourceStatus) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterEventSourceStatus. -func (in *ClusterEventSourceStatus) DeepCopy() *ClusterEventSourceStatus { - if in == nil { - return nil - } - out := new(ClusterEventSourceStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterEventType) DeepCopyInto(out *ClusterEventType) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterEventType. -func (in *ClusterEventType) DeepCopy() *ClusterEventType { - if in == nil { - return nil - } - out := new(ClusterEventType) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ClusterEventType) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterEventTypeList) DeepCopyInto(out *ClusterEventTypeList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]ClusterEventType, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterEventTypeList. -func (in *ClusterEventTypeList) DeepCopy() *ClusterEventTypeList { - if in == nil { - return nil - } - out := new(ClusterEventTypeList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *ClusterEventTypeList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterEventTypeSpec) DeepCopyInto(out *ClusterEventTypeSpec) { - *out = *in - in.CommonEventTypeSpec.DeepCopyInto(&out.CommonEventTypeSpec) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterEventTypeSpec. -func (in *ClusterEventTypeSpec) DeepCopy() *ClusterEventTypeSpec { - if in == nil { - return nil - } - out := new(ClusterEventTypeSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ClusterEventTypeStatus) DeepCopyInto(out *ClusterEventTypeStatus) { - *out = *in - in.CommonEventTypeStatus.DeepCopyInto(&out.CommonEventTypeStatus) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterEventTypeStatus. -func (in *ClusterEventTypeStatus) DeepCopy() *ClusterEventTypeStatus { - if in == nil { - return nil - } - out := new(ClusterEventTypeStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CommonEventSourceCondition) DeepCopyInto(out *CommonEventSourceCondition) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonEventSourceCondition. -func (in *CommonEventSourceCondition) DeepCopy() *CommonEventSourceCondition { - if in == nil { - return nil - } - out := new(CommonEventSourceCondition) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CommonEventSourceSpec) DeepCopyInto(out *CommonEventSourceSpec) { - *out = *in - if in.Parameters != nil { - in, out := &in.Parameters, &out.Parameters - if *in == nil { - *out = nil - } else { - *out = new(runtime.RawExtension) - (*in).DeepCopyInto(*out) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonEventSourceSpec. -func (in *CommonEventSourceSpec) DeepCopy() *CommonEventSourceSpec { - if in == nil { - return nil - } - out := new(CommonEventSourceSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CommonEventSourceStatus) DeepCopyInto(out *CommonEventSourceStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]CommonEventSourceCondition, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonEventSourceStatus. -func (in *CommonEventSourceStatus) DeepCopy() *CommonEventSourceStatus { - if in == nil { - return nil - } - out := new(CommonEventSourceStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CommonEventTypeCondition) DeepCopyInto(out *CommonEventTypeCondition) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonEventTypeCondition. -func (in *CommonEventTypeCondition) DeepCopy() *CommonEventTypeCondition { - if in == nil { - return nil - } - out := new(CommonEventTypeCondition) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CommonEventTypeSpec) DeepCopyInto(out *CommonEventTypeSpec) { - *out = *in - if in.SubscribeSchema != nil { - in, out := &in.SubscribeSchema, &out.SubscribeSchema - if *in == nil { - *out = nil - } else { - *out = new(runtime.RawExtension) - (*in).DeepCopyInto(*out) - } - } - if in.EventSchema != nil { - in, out := &in.EventSchema, &out.EventSchema - if *in == nil { - *out = nil - } else { - *out = new(runtime.RawExtension) - (*in).DeepCopyInto(*out) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonEventTypeSpec. -func (in *CommonEventTypeSpec) DeepCopy() *CommonEventTypeSpec { - if in == nil { - return nil - } - out := new(CommonEventTypeSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CommonEventTypeStatus) DeepCopyInto(out *CommonEventTypeStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]CommonEventTypeCondition, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CommonEventTypeStatus. -func (in *CommonEventTypeStatus) DeepCopy() *CommonEventTypeStatus { - if in == nil { - return nil - } - out := new(CommonEventTypeStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EventSource) DeepCopyInto(out *EventSource) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventSource. -func (in *EventSource) DeepCopy() *EventSource { - if in == nil { - return nil - } - out := new(EventSource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *EventSource) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EventSourceList) DeepCopyInto(out *EventSourceList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]EventSource, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventSourceList. -func (in *EventSourceList) DeepCopy() *EventSourceList { - if in == nil { - return nil - } - out := new(EventSourceList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *EventSourceList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EventSourceSpec) DeepCopyInto(out *EventSourceSpec) { - *out = *in - in.CommonEventSourceSpec.DeepCopyInto(&out.CommonEventSourceSpec) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventSourceSpec. -func (in *EventSourceSpec) DeepCopy() *EventSourceSpec { - if in == nil { - return nil - } - out := new(EventSourceSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EventSourceStatus) DeepCopyInto(out *EventSourceStatus) { - *out = *in - in.CommonEventSourceStatus.DeepCopyInto(&out.CommonEventSourceStatus) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventSourceStatus. -func (in *EventSourceStatus) DeepCopy() *EventSourceStatus { - if in == nil { - return nil - } - out := new(EventSourceStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EventTrigger) DeepCopyInto(out *EventTrigger) { - *out = *in - if in.Parameters != nil { - in, out := &in.Parameters, &out.Parameters - if *in == nil { - *out = nil - } else { - *out = new(runtime.RawExtension) - (*in).DeepCopyInto(*out) - } - } - if in.ParametersFrom != nil { - in, out := &in.ParametersFrom, &out.ParametersFrom - *out = make([]ParametersFromSource, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventTrigger. -func (in *EventTrigger) DeepCopy() *EventTrigger { - if in == nil { - return nil - } - out := new(EventTrigger) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EventType) DeepCopyInto(out *EventType) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventType. -func (in *EventType) DeepCopy() *EventType { - if in == nil { - return nil - } - out := new(EventType) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *EventType) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EventTypeList) DeepCopyInto(out *EventTypeList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]EventType, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventTypeList. -func (in *EventTypeList) DeepCopy() *EventTypeList { - if in == nil { - return nil - } - out := new(EventTypeList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *EventTypeList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EventTypeSpec) DeepCopyInto(out *EventTypeSpec) { - *out = *in - in.CommonEventTypeSpec.DeepCopyInto(&out.CommonEventTypeSpec) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventTypeSpec. -func (in *EventTypeSpec) DeepCopy() *EventTypeSpec { - if in == nil { - return nil - } - out := new(EventTypeSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EventTypeStatus) DeepCopyInto(out *EventTypeStatus) { - *out = *in - in.CommonEventTypeStatus.DeepCopyInto(&out.CommonEventTypeStatus) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventTypeStatus. -func (in *EventTypeStatus) DeepCopy() *EventTypeStatus { - if in == nil { - return nil - } - out := new(EventTypeStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Feed) DeepCopyInto(out *Feed) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Feed. -func (in *Feed) DeepCopy() *Feed { - if in == nil { - return nil - } - out := new(Feed) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Feed) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FeedAction) DeepCopyInto(out *FeedAction) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeedAction. -func (in *FeedAction) DeepCopy() *FeedAction { - if in == nil { - return nil - } - out := new(FeedAction) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FeedCondition) DeepCopyInto(out *FeedCondition) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeedCondition. -func (in *FeedCondition) DeepCopy() *FeedCondition { - if in == nil { - return nil - } - out := new(FeedCondition) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FeedList) DeepCopyInto(out *FeedList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Feed, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeedList. -func (in *FeedList) DeepCopy() *FeedList { - if in == nil { - return nil - } - out := new(FeedList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *FeedList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FeedSpec) DeepCopyInto(out *FeedSpec) { - *out = *in - out.Action = in.Action - in.Trigger.DeepCopyInto(&out.Trigger) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeedSpec. -func (in *FeedSpec) DeepCopy() *FeedSpec { - if in == nil { - return nil - } - out := new(FeedSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FeedStatus) DeepCopyInto(out *FeedStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]FeedCondition, len(*in)) - copy(*out, *in) - } - if in.FeedContext != nil { - in, out := &in.FeedContext, &out.FeedContext - if *in == nil { - *out = nil - } else { - *out = new(runtime.RawExtension) - (*in).DeepCopyInto(*out) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FeedStatus. -func (in *FeedStatus) DeepCopy() *FeedStatus { - if in == nil { - return nil - } - out := new(FeedStatus) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ParametersFromSource) DeepCopyInto(out *ParametersFromSource) { - *out = *in - if in.SecretKeyRef != nil { - in, out := &in.SecretKeyRef, &out.SecretKeyRef - if *in == nil { - *out = nil - } else { - *out = new(SecretKeyReference) - **out = **in - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ParametersFromSource. -func (in *ParametersFromSource) DeepCopy() *ParametersFromSource { - if in == nil { - return nil - } - out := new(ParametersFromSource) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *SecretKeyReference) DeepCopyInto(out *SecretKeyReference) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SecretKeyReference. -func (in *SecretKeyReference) DeepCopy() *SecretKeyReference { - if in == nil { - return nil - } - out := new(SecretKeyReference) - in.DeepCopyInto(out) - return out -} diff --git a/pkg/apis/flows/logkey/constants.go b/pkg/apis/flows/logkey/constants.go deleted file mode 100644 index 1136af2097b..00000000000 --- a/pkg/apis/flows/logkey/constants.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package logkey - -const ( - kNative = "knative.dev/" - - // Flow is the key used for flow name in structured logs - Flow = kNative + "flow" -) diff --git a/pkg/apis/flows/register.go b/pkg/apis/flows/register.go deleted file mode 100644 index bf9e7baf01c..00000000000 --- a/pkg/apis/flows/register.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package flows - -const ( - GroupName = "flows.knative.dev" -) diff --git a/pkg/apis/flows/v1alpha1/doc.go b/pkg/apis/flows/v1alpha1/doc.go deleted file mode 100644 index 21b483017e4..00000000000 --- a/pkg/apis/flows/v1alpha1/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2018 The Knative Authors. - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// +k8s:deepcopy-gen=package -// Package v1alpha1 is the v1alpha1 version of the API. -// +groupName=flows.knative.dev -package v1alpha1 diff --git a/pkg/apis/flows/v1alpha1/flow_defaults.go b/pkg/apis/flows/v1alpha1/flow_defaults.go deleted file mode 100644 index d02436f284d..00000000000 --- a/pkg/apis/flows/v1alpha1/flow_defaults.go +++ /dev/null @@ -1,34 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -func (f *Flow) SetDefaults() { - f.Spec.SetDefaults() -} - -func (fs *FlowSpec) SetDefaults() { - fs.Action.SetDefaults() - fs.Trigger.SetDefaults() -} - -func (fa *FlowAction) SetDefaults() { - // nothing to do -} - -func (et *EventTrigger) SetDefaults() { - // // nothing to do -} diff --git a/pkg/apis/flows/v1alpha1/flow_types.go b/pkg/apis/flows/v1alpha1/flow_types.go deleted file mode 100644 index bbb6882ac18..00000000000 --- a/pkg/apis/flows/v1alpha1/flow_types.go +++ /dev/null @@ -1,410 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "encoding/json" - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "github.com/knative/pkg/apis" - "github.com/knative/pkg/webhook" - "k8s.io/apimachinery/pkg/runtime" -) - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Flow connects an event source with an action that processes events produced -// by the event source. The flow controller handles creating the lower-level -// eventing primitives (Feeds, Channels, Subscriptions) used to implement flows. -type Flow struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec FlowSpec `json:"spec"` - Status FlowStatus `json:"status"` -} - -// Check that Flow can be validated and can be defaulted. -var _ apis.Validatable = (*Flow)(nil) -var _ apis.Defaultable = (*Flow)(nil) -var _ runtime.Object = (*Flow)(nil) -var _ webhook.GenericCRD = (*Flow)(nil) - -// FlowSpec is the spec for a Flow resource. -type FlowSpec struct { - // TODO: Generation does not work correctly with CRD. They are scrubbed - // by the APIserver (https://github.com/kubernetes/kubernetes/issues/58778) - // So, we add Generation here. Once that gets fixed, remove this and use - // ObjectMeta.Generation instead. - // +optional - Generation int64 `json:"generation,omitempty"` - - // Action specifies the target handler for the events - Action FlowAction `json:"action"` - - // Trigger specifies the trigger we're creating a Flow to - Trigger EventTrigger `json:"trigger"` - - // Service Account to use when creating the underlying Feed. - // If left out, uses "default" - ServiceAccountName string `json:"serviceAccountName,omitempty"` -} - -// FlowAction specifies the reference to an object that's expected to -// provide the resolved target of the action. Currently we inspect -// the objects Status and see if there's a predefined Status field -// that we will then use to give to Feed object as the target. Currently -// must resolve to a k8s service or Istio virtual service. Note that by -// in the future we should try to utilize subresources (/resolve ?) to -// utilize this, but CRDs do not support subresources yet, so we need -// to rely on a specified Status field today. By relying on this behaviour -// we can utilize a dynamic client instead of having to understand all -// kinds of different types of objects. As long as they adhere to this -// particular contract, they can be used as a Target. -// To ensure that we can support external targets and for ease of use -// we also allow for an URI to be specified. -type FlowAction struct { - // Only one of these can be specified - - // Reference to an object that will be used to find the target - // endpoint. - // For example, this could be a reference to a Route resource - // or a Configuration resource. - // TODO: Specify the required fields the target object must - // have in the status. - // You can specify only the following fields of the ObjectReference: - // - Kind - // - APIVersion - // - Name - // +optional - Target *corev1.ObjectReference `json:"target,omitempty"` - - // Reference to a 'known' endpoint where no resolving be done. - // http://k8s-service for example - // http://myexternalhandler.example.com/foo/bar - // +optional - TargetURI *string `json:"targetURI,omitempty"` -} - -// EventTrigger specifies the intention that a particular event type and -// resource should be consumed. -type EventTrigger struct { - // Required. The type of event to observe. For example: - // `google.storage.object.finalize` and - // `google.firebase.analytics.event.log`. - // - // Event type consists of three parts: - // 1. namespace: The domain name of the organization in reverse-domain - // notation (e.g. `acme.net` appears as `net.acme`) and any orginization - // specific subdivisions. If the organization's top-level domain is `com`, - // the top-level domain is omitted (e.g. `google.com` appears as - // `google`). For example, `google.storage` and - // `google.firebase.analytics`. - // 2. resource type: The type of resource on which event occurs. For - // example, the Google Cloud Storage API includes the types `object` - // and `bucket`. - // 3. action: The action that generates the event. For example, actions for - // a Google Cloud Storage Object include 'finalize' and 'delete'. - // These parts are lower case and joined by '.'. - EventType string `json:"eventType"` - - // Required. The resource(s) from which to observe events, for example, - // `projects/_/buckets/myBucket/objects/{objectPath=**}`. - // - // Can be a specific resource or use wildcards to match a set of resources. - // Wildcards can either match a single segment in the resource name, - // using '*', or multiple segments, using '**'. For example, - // `projects/myProject/buckets/*/objects/**` would match all objects in all - // buckets in the 'myProject' project. - // - // The contents of wildcards can also be captured. This is done by assigning - // it to a variable name in braces. For example, - // `projects/myProject/buckets/{bucket_id=*}/objects/{object_path=**}`. - // Additionally, a single segment capture can omit `=*` and a multiple segment - // capture can specify additional structure. For example, the following - // all match the same buckets, but capture different data: - // `projects/myProject/buckets/*/objects/users/*/data/**` - // `projects/myProject/buckets/{bucket_id=*}/objects/users/{user_id}/data/{data_path=**}` - // `projects/myProject/buckets/{bucket_id}/objects/{object_path=users/*/data/**}` - // - // Not all syntactically correct values are accepted by all services. For - // example: - // - // 1. The authorization model must support it. Google Cloud Functions - // only allows EventTriggers to be deployed that observe resources in the - // same project as the `CloudFunction`. - // 2. The resource type must match the pattern expected for an - // `event_type`. For example, an `EventTrigger` that has an - // `event_type` of "google.pubsub.topic.publish" should have a resource - // that matches Google Cloud Pub/Sub topics. - // - // Additionally, some services may support short names when creating an - // `EventTrigger`. These will always be returned in the normalized "long" - // format. - // - // See each *service's* documentation for supported formats. - Resource string `json:"resource"` - - // The hostname of the service that should be observed. - // - // If no string is provided, the default service implementing the API will - // be used. For example, `storage.googleapis.com` is the default for all - // event types in the 'google.storage` namespace. - Service string `json:"service"` - - // Parameters is what's necessary to create the subscription. - // This is specific to each Source. Opaque to platform, only consumed - // by the actual trigger actuator. - // NOTE: experimental field. - Parameters *runtime.RawExtension `json:"parameters,omitempty"` - - // ParametersFrom are pointers to secrets that contain sensitive - // parameters. Opaque to platform, merged in with Parameters and consumed - // by the actual trigger actuator. - // NOTE: experimental field. All secrets in ParametersFrom will be - // resolved and given to event sources in the Parameters field. - ParametersFrom []feedsv1alpha1.ParametersFromSource `json:"parametersFrom,omitempty"` -} - -// FlowStatus is the status for a Flow resource -type FlowStatus struct { - Conditions []FlowCondition `json:"conditions,omitempty"` - - // FlowContext is what the Flow operation returns and holds enough information - // to perform cleanup once a Flow is deleted. - // NOTE: experimental field. - FlowContext *runtime.RawExtension `json:"flowContext,omitempty"` - - // ChannelTarget is the name of the target channel - ChannelTarget string `json:"channelTarget,omitempty"` - - // ObservedGeneration is the 'Generation' of the Flow that - // was last reconciled by the controller. - // +optional - ObservedGeneration int64 `json:"observedGeneration,omitempty"` -} - -type FlowConditionType string - -const ( - // FlowConditionReady specifies that the Flow has been configured successfully. - FlowConditionReady FlowConditionType = "Ready" - - // FlowConditionFeedReady specifies that the Feed has been configured successfully. - FlowConditionFeedReady FlowConditionType = "FeedReady" - - // FlowConditionChannelReady specifies that the Channel has been configured successfully. - FlowConditionChannelReady FlowConditionType = "ChannelReady" - - // FlowConditionSubscriptionReady specifies that the Subscription has been configured successfully. - FlowConditionSubscriptionReady FlowConditionType = "SubscriptionReady" - - // FlowConditionActionTargetResolved specifies that the Action Target has been resolved - FlowConditionActionTargetResolved FlowConditionType = "ActionTargetResolved" -) - -// FlowCondition defines a readiness condition for a Flow. -// See: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#typical-status-properties -type FlowCondition struct { - Type FlowConditionType `json:"type"` - - Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` - // +optional - Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"` - // +optional - Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"` -} - -func (fs *FlowStatus) IsReady() bool { - if c := fs.GetCondition(FlowConditionReady); c != nil { - return c.Status == corev1.ConditionTrue - } - return false -} - -func (fs *FlowStatus) GetCondition(t FlowConditionType) *FlowCondition { - for _, cond := range fs.Conditions { - if cond.Type == t { - return &cond - } - } - return nil -} - -func (fs *FlowStatus) setCondition(new *FlowCondition) { - if new == nil || new.Type == "" { - return - } - - t := new.Type - var conditions []FlowCondition - for _, cond := range fs.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - conditions = append(conditions, *new) - fs.Conditions = conditions -} - -func (fs *FlowStatus) removeCondition(t FlowConditionType) { - var conditions []FlowCondition - for _, cond := range fs.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - fs.Conditions = conditions -} - -func (fs *FlowStatus) PropagateActionTargetResolved(status corev1.ConditionStatus, reason string, message string) { - fs.setCondition(&FlowCondition{ - Type: FlowConditionActionTargetResolved, - Status: status, - Reason: reason, - Message: message, - }) - fs.checkAndMarkReady() -} - -func (fs *FlowStatus) InitializeConditions() { - for _, cond := range []FlowConditionType{ - FlowConditionReady, - } { - if fc := fs.GetCondition(cond); fc == nil { - fs.setCondition(&FlowCondition{ - Type: cond, - Status: corev1.ConditionUnknown, - }) - } - } -} - -func (fs *FlowStatus) PropagateChannelStatus(cs channelsv1alpha1.ChannelStatus) { - cc := cs.GetCondition(channelsv1alpha1.ChannelReady) - - if cc == nil { - return - } - - fst := []FlowConditionType{FlowConditionChannelReady} - // If the underlying channel reported not ready, then bubble it up. - if cc.Status != corev1.ConditionTrue { - fst = append(fst, FlowConditionReady) - } - - for _, cond := range fst { - fs.setCondition(&FlowCondition{ - Type: cond, - Status: cc.Status, - Reason: cc.Reason, - Message: cc.Message, - }) - } - - fs.ChannelTarget = cs.DomainInternal - - fs.checkAndMarkReady() -} - -func (fs *FlowStatus) PropagateSubscriptionStatus(ss channelsv1alpha1.SubscriptionStatus) { - sc := ss.GetCondition(channelsv1alpha1.SubscriptionDispatching) - - if sc == nil { - return - } - - fst := []FlowConditionType{FlowConditionSubscriptionReady} - // If the underlying subscription reported not ready, then bubble it up. - if sc.Status != corev1.ConditionTrue { - fst = append(fst, FlowConditionReady) - } - - for _, cond := range fst { - fs.setCondition(&FlowCondition{ - Type: cond, - Status: sc.Status, - Reason: sc.Reason, - Message: sc.Message, - }) - } - - fs.checkAndMarkReady() -} - -func (fs *FlowStatus) PropagateFeedStatus(s feedsv1alpha1.FeedStatus) { - // Check to see if Feed is ready - fc := s.GetCondition(feedsv1alpha1.FeedConditionReady) - - if fc == nil { - return - } - fst := []FlowConditionType{FlowConditionFeedReady} - // If the underlying Feed reported not ready, then bubble it up. - if fc.Status != corev1.ConditionTrue { - fst = append(fst, FlowConditionReady) - } - for _, cond := range fst { - fs.setCondition(&FlowCondition{ - Type: cond, - Status: fc.Status, - Reason: fc.Reason, - Message: fc.Message, - }) - } - fs.checkAndMarkReady() -} - -func (fs *FlowStatus) checkAndMarkReady() { - for _, cond := range []FlowConditionType{ - FlowConditionFeedReady, - FlowConditionChannelReady, - FlowConditionSubscriptionReady, - FlowConditionActionTargetResolved, - } { - c := fs.GetCondition(cond) - if c == nil || c.Status != corev1.ConditionTrue { - return - } - } - fs.markReady() -} - -func (fs *FlowStatus) markReady() { - fs.setCondition(&FlowCondition{ - Type: FlowConditionReady, - Status: corev1.ConditionTrue, - }) -} - -func (f *Flow) GetSpecJSON() ([]byte, error) { - return json.Marshal(f.Spec) -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// FlowList is a list of Flow resources -type FlowList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []Flow `json:"items"` -} diff --git a/pkg/apis/flows/v1alpha1/flow_types_test.go b/pkg/apis/flows/v1alpha1/flow_types_test.go deleted file mode 100644 index 9dfc652aff5..00000000000 --- a/pkg/apis/flows/v1alpha1/flow_types_test.go +++ /dev/null @@ -1,356 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "fmt" - "testing" - - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - corev1 "k8s.io/api/core/v1" -) - -const ( - actionTargetResolveFailMessage = "action target failed to resolve" - actionTargetResolveSuccessMessage = "action target resolved" - - actionTargetResolveFailReason = "ActionTargetResolveFailed" - actionTargetResolveSuccessReason = "ActionTargetResolveSucceeded" -) - -func TestFlowCondition_GetConditionNotFound(t *testing.T) { - flow := Flow{} - flow.Status.setCondition(&FlowCondition{Type: FlowConditionReady}) - if flow.Status.GetCondition(FlowConditionFeedReady) != nil { - t.Fatalf("Got a non-nil for non-existent conditiontype") - } - - flow2 := Flow{} - if flow2.Status.GetCondition(FlowConditionFeedReady) != nil { - t.Fatalf("Got a non-nil for non-existent conditiontype") - } -} - -func TestFlowCondition_GetCondition(t *testing.T) { - testcases := []struct { - name string - types []FlowConditionType - get FlowConditionType - want FlowConditionType - }{ - {"FlowConditionReady", []FlowConditionType{FlowConditionReady}, FlowConditionReady, FlowConditionReady}, - {"FlowConditionFeedReady", []FlowConditionType{FlowConditionFeedReady, FlowConditionReady}, FlowConditionFeedReady, FlowConditionFeedReady}, - {"FlowConditionChannelReady", []FlowConditionType{FlowConditionChannelReady, FlowConditionReady}, FlowConditionChannelReady, FlowConditionChannelReady}, - {"FlowConditionSubscriptionReady", []FlowConditionType{FlowConditionSubscriptionReady}, FlowConditionSubscriptionReady, FlowConditionSubscriptionReady}, - } - - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "Flow", tc.name) - t.Run(testName, func(t *testing.T) { - flow := Flow{} - for _, t := range tc.types { - c := &FlowCondition{Type: t} - flow.Status.setCondition(c) - } - - if want, got := tc.want, flow.Status.GetCondition(tc.get).Type; want != got { - t.Fatalf("Failed to get expected condition. \nwant:\t%#v\ngot:\t%#v", want, got) - } - }) - } -} - -func TestFlowCondition_setCondition(t *testing.T) { - testcases := []struct { - name string - types []FlowConditionType - want int - }{ - {"One", []FlowConditionType{FlowConditionReady}, 1}, - {"Two", []FlowConditionType{FlowConditionReady, FlowConditionFeedReady}, 2}, - {"Replace", []FlowConditionType{FlowConditionReady, FlowConditionReady}, 1}, - {"Invalid", []FlowConditionType{""}, 0}, - } - - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "Flow", tc.name) - t.Run(testName, func(t *testing.T) { - flow := Flow{} - for _, t := range tc.types { - c := &FlowCondition{Type: t} - flow.Status.setCondition(c) - } - if want, got := tc.want, len(flow.Status.Conditions); want != got { - t.Fatalf("Failed to return expected number of conditions. \nwant:\t%#v\ngot:\t%#v", want, got) - } - }) - } -} - -func TestFlowCondition_RemoveCondition(t *testing.T) { - testcases := []struct { - name string - set []FlowConditionType - remove []FlowConditionType - want int - }{ - {"RemoveOnlyOne", []FlowConditionType{FlowConditionReady}, []FlowConditionType{FlowConditionReady}, 0}, - {"RemoveOne", []FlowConditionType{FlowConditionReady, FlowConditionChannelReady}, []FlowConditionType{FlowConditionReady}, 1}, - {"RemoveNonExistent", []FlowConditionType{FlowConditionReady}, []FlowConditionType{FlowConditionSubscriptionReady}, 1}, - {"Invalid", []FlowConditionType{FlowConditionReady}, []FlowConditionType{""}, 1}, - } - - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "Flow", tc.name) - t.Run(testName, func(t *testing.T) { - flow := Flow{} - for _, t := range tc.set { - c := &FlowCondition{Type: t} - flow.Status.setCondition(c) - } - for _, t := range tc.remove { - flow.Status.removeCondition(t) - } - - if want, got := tc.want, len(flow.Status.Conditions); want != got { - t.Fatalf("Failed to return expected number of conditions. \nwant:\t%#v\ngot:\t%#v", want, got) - } - }) - } -} - -func TestFlowCondition_IsReady(t *testing.T) { - testcases := []struct { - name string - set []FlowCondition - want bool - }{ - {"FlowConditionFeedReady", []FlowCondition{{ - Type: FlowConditionFeedReady, - Status: corev1.ConditionTrue, - }}, - false}, - {"FlowConditionFeedAndChannelReady", []FlowCondition{{ - Type: FlowConditionFeedReady, - Status: corev1.ConditionTrue, - }, { - Type: FlowConditionChannelReady, - Status: corev1.ConditionTrue, - }}, - false}, - {"FlowConditionFeedReadyChannelNotReady", []FlowCondition{{ - Type: FlowConditionFeedReady, - Status: corev1.ConditionTrue, - }, { - Type: FlowConditionChannelReady, - Status: corev1.ConditionFalse, - }}, - false}, - {"FlowConditionFeedNotReadyChannelReady", []FlowCondition{{ - Type: FlowConditionFeedReady, - Status: corev1.ConditionFalse, - }, { - Type: FlowConditionChannelReady, - Status: corev1.ConditionTrue, - }}, - false}, - {"FlowConditionFeedReadyChannelReadySubscriptionDispatching", []FlowCondition{{ - Type: FlowConditionFeedReady, - Status: corev1.ConditionTrue, - }, { - Type: FlowConditionChannelReady, - Status: corev1.ConditionTrue, - }, { - Type: FlowConditionSubscriptionReady, - Status: corev1.ConditionTrue, - }}, - false}, - {"FlowConditionFeedReadyChannelReadySubscriptionDispatchingActionTargetResolved", []FlowCondition{{ - Type: FlowConditionFeedReady, - Status: corev1.ConditionTrue, - }, { - Type: FlowConditionChannelReady, - Status: corev1.ConditionTrue, - }, { - Type: FlowConditionSubscriptionReady, - Status: corev1.ConditionTrue, - }, { - Type: FlowConditionActionTargetResolved, - Status: corev1.ConditionTrue, - }}, - true}, - } - - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "Flow", tc.name) - t.Run(testName, func(t *testing.T) { - flow := Flow{} - for _, c := range tc.set { - flow.Status.setCondition(&c) - } - flow.Status.checkAndMarkReady() - if want, got := tc.want, flow.Status.IsReady(); want != got { - t.Fatalf("Failed IsReady check : \nwant:\t%#v\ngot:\t%#v", want, got) - } - }) - } -} - -func TestFlowCondition_PropagateStatus(t *testing.T) { - // These just get set by the - - testcases := []struct { - name string - feedStatuses []feedsv1alpha1.FeedStatus - channelStatuses []channelsv1alpha1.ChannelStatus - subscriptionStatuses []channelsv1alpha1.SubscriptionStatus - actionTargetResolveStatus corev1.ConditionStatus - want bool - }{ - {"NothingReady", - []feedsv1alpha1.FeedStatus{{}}, - []channelsv1alpha1.ChannelStatus{{}}, - []channelsv1alpha1.SubscriptionStatus{{}}, - corev1.ConditionFalse, - false}, - {"FeedReady", - []feedsv1alpha1.FeedStatus{{ - Conditions: []feedsv1alpha1.FeedCondition{{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionTrue, - }}, - }}, - []channelsv1alpha1.ChannelStatus{}, - []channelsv1alpha1.SubscriptionStatus{}, - corev1.ConditionFalse, - false}, - {"ChannelReady", - []feedsv1alpha1.FeedStatus{}, - []channelsv1alpha1.ChannelStatus{{ - Conditions: []channelsv1alpha1.ChannelCondition{{ - Type: channelsv1alpha1.ChannelReady, - Status: corev1.ConditionTrue, - }}, - DomainInternal: "foobar-channel.default.svc.cluster.local", - }}, - []channelsv1alpha1.SubscriptionStatus{}, - corev1.ConditionFalse, - false}, - {"SubscriptionReady", - []feedsv1alpha1.FeedStatus{}, - []channelsv1alpha1.ChannelStatus{}, - []channelsv1alpha1.SubscriptionStatus{{ - Conditions: []channelsv1alpha1.SubscriptionCondition{{ - Type: channelsv1alpha1.SubscriptionDispatching, - Status: corev1.ConditionTrue, - }}, - }}, - corev1.ConditionFalse, - false}, - {"ActionTargetResolved", - []feedsv1alpha1.FeedStatus{}, - []channelsv1alpha1.ChannelStatus{}, - []channelsv1alpha1.SubscriptionStatus{}, - corev1.ConditionTrue, - false}, - {"AllNotReady", - []feedsv1alpha1.FeedStatus{{ - Conditions: []feedsv1alpha1.FeedCondition{{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionFalse, - }}, - }}, - []channelsv1alpha1.ChannelStatus{{ - Conditions: []channelsv1alpha1.ChannelCondition{{ - Type: channelsv1alpha1.ChannelReady, - Status: corev1.ConditionFalse, - }}, - DomainInternal: "foobar-channel.default.svc.cluster.local", - }}, - []channelsv1alpha1.SubscriptionStatus{{ - Conditions: []channelsv1alpha1.SubscriptionCondition{{ - Type: channelsv1alpha1.SubscriptionDispatching, - Status: corev1.ConditionFalse, - }}, - }}, - corev1.ConditionFalse, - false}, - {"AllReady", - []feedsv1alpha1.FeedStatus{{ - Conditions: []feedsv1alpha1.FeedCondition{{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionTrue, - }}, - }}, - []channelsv1alpha1.ChannelStatus{{ - Conditions: []channelsv1alpha1.ChannelCondition{{ - Type: channelsv1alpha1.ChannelReady, - Status: corev1.ConditionTrue, - }}, - DomainInternal: "foobar-channel.default.svc.cluster.local", - }}, - []channelsv1alpha1.SubscriptionStatus{{ - Conditions: []channelsv1alpha1.SubscriptionCondition{{ - Type: channelsv1alpha1.SubscriptionDispatching, - Status: corev1.ConditionTrue, - }}, - }}, - corev1.ConditionTrue, - true}, - } - for _, tc := range testcases { - testName := fmt.Sprintf("%s - %s", "Flow", tc.name) - t.Run(testName, func(t *testing.T) { - flow := Flow{} - for _, fs := range tc.feedStatuses { - flow.Status.PropagateFeedStatus(fs) - } - for _, cs := range tc.channelStatuses { - flow.Status.PropagateChannelStatus(cs) - } - for _, ss := range tc.subscriptionStatuses { - flow.Status.PropagateSubscriptionStatus(ss) - } - if tc.actionTargetResolveStatus == corev1.ConditionTrue { - flow.Status.PropagateActionTargetResolved(corev1.ConditionTrue, actionTargetResolveSuccessReason, actionTargetResolveSuccessMessage) - } else if tc.actionTargetResolveStatus == corev1.ConditionFalse { - flow.Status.PropagateActionTargetResolved(corev1.ConditionFalse, actionTargetResolveFailReason, actionTargetResolveFailMessage) - } else { - flow.Status.PropagateActionTargetResolved(corev1.ConditionUnknown, "Unknown", "Unknown") - } - if want, got := tc.want, flow.Status.IsReady(); want != got { - t.Fatalf("Failed IsReady check : \nwant:\t%#v\ngot:\t%#v", want, got) - } - }) - } -} - -func TestFlowCondition_InitializeConditions(t *testing.T) { - flow := Flow{} - flow.Status.InitializeConditions() - cond := flow.Status.GetCondition(FlowConditionReady) - if cond == nil { - t.Fatalf("InitializeConditions didn't set FlowConditionReady") - } - if want, got := cond.Status, corev1.ConditionUnknown; want != got { - t.Fatalf("Status was not set correctly : \nwant:\t%#v\ngot:\t%#v", want, got) - } - if flow.Status.IsReady() { - t.Fatalf("InitializeConditions marked the flow Ready") - } -} diff --git a/pkg/apis/flows/v1alpha1/flow_validation.go b/pkg/apis/flows/v1alpha1/flow_validation.go deleted file mode 100644 index e11025aa5e4..00000000000 --- a/pkg/apis/flows/v1alpha1/flow_validation.go +++ /dev/null @@ -1,81 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/pkg/apis" - "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/util/validation" - "net/url" -) - -// Validate validates the Flow resource. -func (f *Flow) Validate() *apis.FieldError { - return f.Spec.Validate().ViaField("spec") -} - -// Validate validates the flow spec for a valid action and trigger. -func (fs *FlowSpec) Validate() *apis.FieldError { - if equality.Semantic.DeepEqual(fs, &FlowSpec{}) { - return apis.ErrMissingField("trigger", "action") - } - - if err := fs.Trigger.Validate(); err != nil { - return err.ViaField("trigger") - } - if err := fs.Action.Validate(); err != nil { - return err.ViaField("action") - } - return nil -} - -// Validate validates event trigger has both eventType and resource set. -func (et *EventTrigger) Validate() *apis.FieldError { - - if et.EventType == "" { - return apis.ErrMissingField("eventType") - } - if errs := validation.IsQualifiedName(et.EventType); len(errs) > 0 { - return apis.ErrInvalidValue(et.EventType, "eventType") - } - - if et.Resource == "" { - return apis.ErrMissingField("resource") - } - - return nil -} - -// Validate validates flow action has a valid formed target or targetURI, but not both. -func (fa *FlowAction) Validate() *apis.FieldError { - switch { - case fa.Target != nil && fa.TargetURI != nil: - return apis.ErrMultipleOneOf("target", "targetURI") - case fa.Target != nil: - if errs := validation.IsQualifiedName(fa.Target.Name); len(errs) > 0 { - return apis.ErrInvalidValue(fa.Target.Name, "name").ViaField("target") - } - return nil - case fa.TargetURI != nil: - if _, err := url.ParseRequestURI(*fa.TargetURI); err != nil { - return apis.ErrInvalidValue(*fa.TargetURI, "targetURI") - } - return nil - default: - return apis.ErrMissingOneOf("target", "targetURI") - } -} diff --git a/pkg/apis/flows/v1alpha1/flow_validation_test.go b/pkg/apis/flows/v1alpha1/flow_validation_test.go deleted file mode 100644 index aa3e4bfa258..00000000000 --- a/pkg/apis/flows/v1alpha1/flow_validation_test.go +++ /dev/null @@ -1,183 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/google/go-cmp/cmp" - "github.com/knative/pkg/apis" - "k8s.io/api/core/v1" - "testing" -) - -func TestFlowSpecValidation(t *testing.T) { - tests := []struct { - name string - f *FlowSpec - want *apis.FieldError - }{{ - name: "valid with URI", - f: &FlowSpec{ - Action: FlowAction{ - TargetURI: stringPtr("http://example.com/action"), - }, - Trigger: EventTrigger{ - EventType: "foo", - Resource: "bar", - }, - }, - }, { - name: "valid with Target", - f: &FlowSpec{ - Action: FlowAction{ - Target: &v1.ObjectReference{ - Name: "foo", - }, - }, - Trigger: EventTrigger{ - EventType: "foo", - Resource: "bar", - }, - }, - }, { - name: "invalid with URI", - f: &FlowSpec{ - Action: FlowAction{ - TargetURI: stringPtr("http//example.com/action"), - }, - Trigger: EventTrigger{ - EventType: "foo", - Resource: "bar", - }, - }, - want: &apis.FieldError{ - Message: `invalid value "http//example.com/action"`, - Paths: []string{"action.targetURI"}, - }, - }, { - name: "invalid with Target", - f: &FlowSpec{ - Action: FlowAction{ - Target: &v1.ObjectReference{ - Name: "f@o", - }, - }, - Trigger: EventTrigger{ - EventType: "foo", - Resource: "bar", - }, - }, - want: &apis.FieldError{ - Message: `invalid value "f@o"`, - Paths: []string{"action.target.name"}, - }, - }, { - name: "invalid with both target and targetURI", - f: &FlowSpec{ - Action: FlowAction{ - TargetURI: stringPtr("http://example.com/action"), - Target: &v1.ObjectReference{ - Name: "foo", - }, - }, - Trigger: EventTrigger{ - EventType: "foo", - Resource: "bar", - }, - }, - want: &apis.FieldError{ - Message: `expected exactly one, got both`, - Paths: []string{"action.target", "action.targetURI"}, - }, - }, { - name: "invalid, no target or targetURI", - f: &FlowSpec{ - Action: FlowAction{}, - Trigger: EventTrigger{ - EventType: "foo", - Resource: "bar", - }, - }, - want: &apis.FieldError{ - Message: `expected exactly one, got neither`, - Paths: []string{"action.target", "action.targetURI"}, - }, - }, { - name: "invalid, missing event type", - f: &FlowSpec{ - Action: FlowAction{ - TargetURI: stringPtr("http://example.com/action"), - }, - Trigger: EventTrigger{ - Resource: "bar", - }, - }, - want: &apis.FieldError{ - Message: "missing field(s)", - Paths: []string{"trigger.eventType"}, - }, - }, { - name: "invalid event type", - f: &FlowSpec{ - Action: FlowAction{ - TargetURI: stringPtr("http://example.com/action"), - }, - Trigger: EventTrigger{ - EventType: "f@o", - Resource: "bar", - }, - }, - want: &apis.FieldError{ - Message: `invalid value "f@o"`, - Paths: []string{"trigger.eventType"}, - }, - }, { - name: "invalid, missing resource", - f: &FlowSpec{ - Action: FlowAction{ - TargetURI: stringPtr("http://example.com/action"), - }, - Trigger: EventTrigger{ - EventType: "foo", - }, - }, - want: &apis.FieldError{ - Message: "missing field(s)", - Paths: []string{"trigger.resource"}, - }, - }, { - name: "empty", - f: &FlowSpec{}, - want: &apis.FieldError{ - Message: "missing field(s)", - Paths: []string{"trigger", "action"}, - }, - }} - - for _, test := range tests { - t.Run(test.name, func(t *testing.T) { - got := test.f.Validate() - if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("validateFlow (-want, +got) = %v", diff) - } - }) - } -} - -// stringPtr returns a pointer to the passed string. -func stringPtr(s string) *string { - return &s -} diff --git a/pkg/apis/flows/v1alpha1/register.go b/pkg/apis/flows/v1alpha1/register.go deleted file mode 100644 index 918c85b031c..00000000000 --- a/pkg/apis/flows/v1alpha1/register.go +++ /dev/null @@ -1,53 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "github.com/knative/eventing/pkg/apis/flows" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" -) - -// SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: flows.GroupName, Version: "v1alpha1"} - -// Kind takes an unqualified kind and returns back a Group qualified GroupKind -func Kind(kind string) schema.GroupKind { - return SchemeGroupVersion.WithKind(kind).GroupKind() -} - -// Resource takes an unqualified resource and returns a Group qualified GroupResource -func Resource(resource string) schema.GroupResource { - return SchemeGroupVersion.WithResource(resource).GroupResource() -} - -var ( - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - AddToScheme = SchemeBuilder.AddToScheme -) - -// Adds the list of known types to Scheme. -func addKnownTypes(scheme *runtime.Scheme) error { - scheme.AddKnownTypes(SchemeGroupVersion, - &Flow{}, - &FlowList{}, - ) - metav1.AddToGroupVersion(scheme, SchemeGroupVersion) - return nil -} diff --git a/pkg/apis/flows/v1alpha1/zz_generated.deepcopy.go b/pkg/apis/flows/v1alpha1/zz_generated.deepcopy.go deleted file mode 100644 index 03bae2eb6b5..00000000000 --- a/pkg/apis/flows/v1alpha1/zz_generated.deepcopy.go +++ /dev/null @@ -1,218 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by deepcopy-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - feeds_v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - v1 "k8s.io/api/core/v1" - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *EventTrigger) DeepCopyInto(out *EventTrigger) { - *out = *in - if in.Parameters != nil { - in, out := &in.Parameters, &out.Parameters - if *in == nil { - *out = nil - } else { - *out = new(runtime.RawExtension) - (*in).DeepCopyInto(*out) - } - } - if in.ParametersFrom != nil { - in, out := &in.ParametersFrom, &out.ParametersFrom - *out = make([]feeds_v1alpha1.ParametersFromSource, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EventTrigger. -func (in *EventTrigger) DeepCopy() *EventTrigger { - if in == nil { - return nil - } - out := new(EventTrigger) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Flow) DeepCopyInto(out *Flow) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - in.Status.DeepCopyInto(&out.Status) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Flow. -func (in *Flow) DeepCopy() *Flow { - if in == nil { - return nil - } - out := new(Flow) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Flow) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FlowAction) DeepCopyInto(out *FlowAction) { - *out = *in - if in.Target != nil { - in, out := &in.Target, &out.Target - if *in == nil { - *out = nil - } else { - *out = new(v1.ObjectReference) - **out = **in - } - } - if in.TargetURI != nil { - in, out := &in.TargetURI, &out.TargetURI - if *in == nil { - *out = nil - } else { - *out = new(string) - **out = **in - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FlowAction. -func (in *FlowAction) DeepCopy() *FlowAction { - if in == nil { - return nil - } - out := new(FlowAction) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FlowCondition) DeepCopyInto(out *FlowCondition) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FlowCondition. -func (in *FlowCondition) DeepCopy() *FlowCondition { - if in == nil { - return nil - } - out := new(FlowCondition) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FlowList) DeepCopyInto(out *FlowList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Flow, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FlowList. -func (in *FlowList) DeepCopy() *FlowList { - if in == nil { - return nil - } - out := new(FlowList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *FlowList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } - return nil -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FlowSpec) DeepCopyInto(out *FlowSpec) { - *out = *in - in.Action.DeepCopyInto(&out.Action) - in.Trigger.DeepCopyInto(&out.Trigger) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FlowSpec. -func (in *FlowSpec) DeepCopy() *FlowSpec { - if in == nil { - return nil - } - out := new(FlowSpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *FlowStatus) DeepCopyInto(out *FlowStatus) { - *out = *in - if in.Conditions != nil { - in, out := &in.Conditions, &out.Conditions - *out = make([]FlowCondition, len(*in)) - copy(*out, *in) - } - if in.FlowContext != nil { - in, out := &in.FlowContext, &out.FlowContext - if *in == nil { - *out = nil - } else { - *out = new(runtime.RawExtension) - (*in).DeepCopyInto(*out) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FlowStatus. -func (in *FlowStatus) DeepCopy() *FlowStatus { - if in == nil { - return nil - } - out := new(FlowStatus) - in.DeepCopyInto(out) - return out -} diff --git a/pkg/buses/bus.go b/pkg/buses/bus.go deleted file mode 100644 index 0ee44279a26..00000000000 --- a/pkg/buses/bus.go +++ /dev/null @@ -1,198 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package buses - -import ( - "fmt" - - "go.uber.org/zap" - - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" -) - -type bus struct { - ref BusReference - handlerFuncs EventHandlerFuncs - - reconciler *Reconciler - cache *Cache - dispatcher *MessageDispatcher - receiver *MessageReceiver - - logger *zap.SugaredLogger -} - -// BusOpts holds configuration options for new buses. These options are not -// required for proper operation of the bus, but are useful to override the -// default behavior and for testing. -type BusOpts struct { - // Logger to use for the bus and created components - Logger *zap.SugaredLogger - - // MasterURL is the address of the Kubernetes API server. Overrides any - // value in kubeconfig. Only required if out-of-cluster. - MasterURL string - // KubeConfig is the path to a kubeconfig. Only required if out-of-cluster. - KubeConfig string - - // Cache to use for this bus. A cache will be created for the bus if not - // specified. - Cache *Cache - // Reconciler to use for this bus. A reconciler wil be created for the bus - // if not specified. - Reconciler *Reconciler - // MessageDispatcher to use for this bus. The message dispatcher is used to - // send messages from the bus to a subscriber. A message dispatcher will be - // created for the bus if needed and not specified. - MessageDispatcher *MessageDispatcher - // MessageReceiver to use for this bus. The message receiver is used to - // receive message sent to a channel. A message receiver will be created - // for the bus if needed and not specified. - MessageReceiver *MessageReceiver -} - -// BusProvisioner provisions channels and subscriptions for a bus on backing -// infrastructure. -type BusProvisioner interface { - Run(threadiness int, stopCh <-chan struct{}) -} - -// NewBusProvisioner creates a new provisioner for a specific bus. -// EventHandlerFuncs are used to be notified when a channel or subscription is -// created, updated or removed. -func NewBusProvisioner(ref BusReference, handlerFuncs EventHandlerFuncs, opts *BusOpts) BusProvisioner { - if opts == nil { - opts = &BusOpts{} - } - if opts.Logger == nil { - opts.Logger = zap.NewNop().Sugar() - } - if opts.Cache == nil { - opts.Cache = NewCache() - } - if handlerFuncs.logger == nil { - handlerFuncs.logger = opts.Logger.Named(handlerLoggingComponent) - } - if opts.Reconciler == nil { - opts.Reconciler = NewReconciler( - ref, Provisioner, opts.MasterURL, opts.KubeConfig, opts.Cache, - handlerFuncs, opts.Logger.Named(reconcilerLoggingComponent), - ) - } - - return &bus{ - ref: ref, - handlerFuncs: handlerFuncs, - - cache: opts.Cache, - reconciler: opts.Reconciler, - - logger: opts.Logger.Named(busLoggingComponent), - } -} - -// BusDispatcher dispatches messages from channels to subscribers via backing -// infrastructure. -type BusDispatcher interface { - Run(threadiness int, stopCh <-chan struct{}) - DispatchMessage(subscription SubscriptionReference, message *Message) error -} - -// NewBusDispatcher creates a new dispatcher for a specific bus. -// EventHandlerFuncs are used to be notified when a subscription is created, -// updated or removed, or a message is received. -func NewBusDispatcher(ref BusReference, handlerFuncs EventHandlerFuncs, opts *BusOpts) BusDispatcher { - var b *bus - - if opts == nil { - opts = &BusOpts{} - } - if opts.Logger == nil { - opts.Logger = zap.NewNop().Sugar() - } - if opts.Cache == nil { - opts.Cache = NewCache() - } - if handlerFuncs.logger == nil { - handlerFuncs.logger = opts.Logger.Named(handlerLoggingComponent) - } - if opts.Reconciler == nil { - opts.Reconciler = NewReconciler( - ref, Dispatcher, opts.MasterURL, opts.KubeConfig, opts.Cache, - handlerFuncs, opts.Logger.Named(reconcilerLoggingComponent), - ) - } - if opts.MessageDispatcher == nil { - opts.MessageDispatcher = NewMessageDispatcher(opts.Logger.Named(dispatcherLoggingComponent)) - } - if opts.MessageReceiver == nil { - opts.MessageReceiver = NewMessageReceiver(func(channel ChannelReference, message *Message) error { - return b.receiveMessage(channel, message) - }, opts.Logger.Named(receiverLoggingComponent)) - } - - b = &bus{ - ref: ref, - handlerFuncs: handlerFuncs, - - cache: opts.Cache, - reconciler: opts.Reconciler, - dispatcher: opts.MessageDispatcher, - receiver: opts.MessageReceiver, - - logger: opts.Logger, - } - - return b -} - -// Run starts the bus's processing. -func (b bus) Run(threadiness int, stopCh <-chan struct{}) { - go b.reconciler.Run(threadiness, stopCh) - b.reconciler.WaitForCacheSync(stopCh) - if b.receiver != nil { - go b.receiver.Run(stopCh) - } - - <-stopCh -} - -func (b *bus) receiveMessage(channel ChannelReference, message *Message) error { - _, err := b.cache.Channel(channel) - if err != nil { - return ErrUnknownChannel - } - return b.handlerFuncs.onReceiveMessage(channel, message) -} - -// DispatchMessage sends a message to a subscriber. This function is only -// avilable for bus dispatchers. -func (b *bus) DispatchMessage(ref SubscriptionReference, message *Message) error { - subscription, err := b.cache.Subscription(ref) - if err != nil { - return fmt.Errorf("unable to dispatch to unknown subscription %q", ref.String()) - } - return b.dispatchMessage(subscription, message) -} - -func (b *bus) dispatchMessage(subscription *channelsv1alpha1.Subscription, message *Message) error { - subscriber := subscription.Spec.Subscriber - defaults := DispatchDefaults{ - Namespace: subscription.Namespace, - } - return b.dispatcher.DispatchMessage(message, subscriber, subscription.Spec.ReplyTo, defaults) -} diff --git a/pkg/buses/cache.go b/pkg/buses/cache.go deleted file mode 100644 index 009d629fec2..00000000000 --- a/pkg/buses/cache.go +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package buses - -import ( - "fmt" - - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" -) - -// NewCache create a cache that is able to save and retrive Channels and -// Subscriptions by their reference. -func NewCache() *Cache { - return &Cache{ - channels: make(map[ChannelReference]*channelsv1alpha1.Channel), - subscriptions: make(map[SubscriptionReference]*channelsv1alpha1.Subscription), - } -} - -// Cache able to save and retrive Channels and Subscriptions by their -// reference. It is used by the reconciler to track which resources have been -// provisioned and comparing updated resources to the provisioned version. -type Cache struct { - channels map[ChannelReference]*channelsv1alpha1.Channel - subscriptions map[SubscriptionReference]*channelsv1alpha1.Subscription -} - -// Channel returns a cached channel for provided reference or an error if the -// channel is not in the cache. -func (c *Cache) Channel(ref ChannelReference) (*channelsv1alpha1.Channel, error) { - channel, ok := c.channels[ref] - if !ok { - return nil, fmt.Errorf("unknown channel %q", ref.String()) - } - return channel, nil -} - -// Subscription returns a cached subscription for provided reference or an -// error if the subscription is not in the cache. -func (c *Cache) Subscription(ref SubscriptionReference) (*channelsv1alpha1.Subscription, error) { - subscription, ok := c.subscriptions[ref] - if !ok { - return nil, fmt.Errorf("unknown subscription %q", ref.String()) - } - return subscription, nil -} - -// AddChannel adds, or updates, the provided channel to the cache for later -// retrieal by its reference. -func (c *Cache) AddChannel(channel *channelsv1alpha1.Channel) { - if channel == nil { - return - } - ref := NewChannelReference(channel) - c.channels[ref] = channel -} - -// RemoveChannel removes the provided channel from the cache. -func (c *Cache) RemoveChannel(channel *channelsv1alpha1.Channel) { - if channel == nil { - return - } - ref := NewChannelReference(channel) - delete(c.channels, ref) -} - -// AddSubscription adds, or updates, the provided subscription to the cache for -// later retrieal by its reference. -func (c *Cache) AddSubscription(subscription *channelsv1alpha1.Subscription) { - if subscription == nil { - return - } - ref := NewSubscriptionReference(subscription) - c.subscriptions[ref] = subscription -} - -// RemoveSubscription removes the provided subscription from the cache. -func (c *Cache) RemoveSubscription(subscription *channelsv1alpha1.Subscription) { - if subscription == nil { - return - } - ref := NewSubscriptionReference(subscription) - delete(c.subscriptions, ref) -} - -func (c *Cache) AllChannels() []*channelsv1alpha1.Channel { - chans := []*channelsv1alpha1.Channel{} - for _, channel := range c.channels { - chans = append(chans, channel) - } - return chans -} - -func (c *Cache) AllSubscriptions() []*channelsv1alpha1.Subscription { - subs := []*channelsv1alpha1.Subscription{} - for _, sub := range c.subscriptions { - subs = append(subs, sub) - } - return subs -} diff --git a/pkg/buses/cache_test.go b/pkg/buses/cache_test.go deleted file mode 100644 index 2a337c9adc6..00000000000 --- a/pkg/buses/cache_test.go +++ /dev/null @@ -1,201 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package buses_test - -import ( - "fmt" - "testing" - - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "github.com/knative/eventing/pkg/buses" - "k8s.io/apimachinery/pkg/api/equality" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - cacheDefaultNamespace = "default" - cacheTestChannel = "test-channel" - cacheTestSubscription = "test-subscription" -) - -func TestCacheErrsForUnknownChannel(t *testing.T) { - cache := buses.NewCache() - ref := buses.NewChannelReferenceFromNames(cacheTestChannel, cacheDefaultNamespace) - var expected *channelsv1alpha1.Channel - actual, err := cache.Channel(ref) - if err == nil { - t.Errorf("%s expected: %+v got: %+v", "Error", "", err) - } - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "Unexpected channel", nil, actual) - } -} - -func TestCacheRetrievesKnownChannel(t *testing.T) { - cache := buses.NewCache() - ref := buses.NewChannelReferenceFromNames(cacheTestChannel, cacheDefaultNamespace) - expected := makeChannel(ref) - cache.AddChannel(expected) - actual, err := cache.Channel(ref) - if err != nil { - t.Errorf("%s expected: %+v got: %+v", "Unexpected error", nil, err) - } - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "Channel", expected, actual) - } -} - -func TestCacheRemovesKnownChannel(t *testing.T) { - cache := buses.NewCache() - ref := buses.NewChannelReferenceFromNames(cacheTestChannel, cacheDefaultNamespace) - channel := makeChannel(ref) - cache.AddChannel(channel) - cache.RemoveChannel(channel) - var expected *channelsv1alpha1.Channel - actual, err := cache.Channel(ref) - if err == nil { - t.Errorf("%s expected: %+v got: %+v", "Unexpected error", nil, err) - } - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "Channel", expected, actual) - } -} - -func TestCacheNilChannel(t *testing.T) { - cache := buses.NewCache() - var channel *channelsv1alpha1.Channel - cache.AddChannel(channel) - cache.RemoveChannel(channel) -} - -func TestCacheErrsForUnknownSubscription(t *testing.T) { - cache := buses.NewCache() - ref := buses.NewSubscriptionReferenceFromNames(cacheTestSubscription, cacheDefaultNamespace) - var expected *channelsv1alpha1.Subscription - actual, err := cache.Subscription(ref) - if err == nil { - t.Errorf("%s expected: %+v got: %+v", "Error", "", err) - } - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "Unexpected subscription", nil, actual) - } -} - -func TestCacheRetrievesKnownSubscription(t *testing.T) { - cache := buses.NewCache() - ref := buses.NewSubscriptionReferenceFromNames(cacheTestSubscription, cacheDefaultNamespace) - expected := makeSubscription(ref) - cache.AddSubscription(expected) - actual, err := cache.Subscription(ref) - if err != nil { - t.Errorf("%s expected: %+v got: %+v", "Unexpected error", nil, err) - } - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "Subscription", expected, actual) - } -} - -func TestCacheRemovesKnownSubscription(t *testing.T) { - cache := buses.NewCache() - ref := buses.NewSubscriptionReferenceFromNames(cacheTestSubscription, cacheDefaultNamespace) - subscription := makeSubscription(ref) - cache.AddSubscription(subscription) - cache.RemoveSubscription(subscription) - var expected *channelsv1alpha1.Subscription - actual, err := cache.Subscription(ref) - if err == nil { - t.Errorf("%s expected: %+v got: %+v", "Unexpected error", nil, err) - } - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "Subscription", expected, actual) - } -} - -func TestCacheNilSubscription(t *testing.T) { - cache := buses.NewCache() - var subscription *channelsv1alpha1.Subscription - cache.AddSubscription(subscription) - cache.RemoveSubscription(subscription) -} - -func TestCacheAllChannels(t *testing.T) { - cases := []struct { - Channels []*channelsv1alpha1.Channel - }{ - {Channels: []*channelsv1alpha1.Channel{}}, - {Channels: []*channelsv1alpha1.Channel{ - makeChannel(buses.NewChannelReferenceFromNames(cacheTestChannel, cacheDefaultNamespace)), - }}, - } - - for _, tt := range cases { - t.Run(fmt.Sprintf("%v", tt.Channels), func(t *testing.T) { - cache := buses.NewCache() - - for _, channel := range tt.Channels { - cache.AddChannel(channel) - } - - if !equality.Semantic.DeepEqual(tt.Channels, cache.AllChannels()) { - t.Errorf("%v != %v", tt.Channels, cache.AllChannels()) - } - }) - } -} - -func TestCacheAllSubscriptions(t *testing.T) { - cases := []struct { - Subscriptions []*channelsv1alpha1.Subscription - }{ - {Subscriptions: []*channelsv1alpha1.Subscription{}}, - {Subscriptions: []*channelsv1alpha1.Subscription{ - makeSubscription(buses.NewSubscriptionReferenceFromNames(cacheTestSubscription, cacheDefaultNamespace)), - }}, - } - - for _, tt := range cases { - t.Run(fmt.Sprintf("%v", tt.Subscriptions), func(t *testing.T) { - cache := buses.NewCache() - - for _, sub := range tt.Subscriptions { - cache.AddSubscription(sub) - } - - if !equality.Semantic.DeepEqual(tt.Subscriptions, cache.AllSubscriptions()) { - t.Errorf("%v != %v", tt.Subscriptions, cache.AllSubscriptions()) - } - }) - } -} - -func makeChannel(ref buses.ChannelReference) *channelsv1alpha1.Channel { - return &channelsv1alpha1.Channel{ - ObjectMeta: metav1.ObjectMeta{ - Name: ref.Name, - Namespace: ref.Namespace, - }, - } -} - -func makeSubscription(ref buses.SubscriptionReference) *channelsv1alpha1.Subscription { - return &channelsv1alpha1.Subscription{ - ObjectMeta: metav1.ObjectMeta{ - Name: ref.Name, - Namespace: ref.Namespace, - }, - } -} diff --git a/pkg/buses/gcppubsub/bus.go b/pkg/buses/gcppubsub/bus.go deleted file mode 100644 index d8385b0e19d..00000000000 --- a/pkg/buses/gcppubsub/bus.go +++ /dev/null @@ -1,292 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package gcppubsub - -import ( - "context" - "fmt" - - "cloud.google.com/go/pubsub" - "github.com/knative/eventing/pkg/buses" - "go.uber.org/zap" -) - -// BusType is the type of the gcppubsub bus -const BusType = "gcppubsub" - -type CloudPubSubBus struct { - ref buses.BusReference - reconciler *buses.Reconciler - dispatcher buses.BusDispatcher - provisioner buses.BusProvisioner - - pubsubClient *pubsub.Client - receivers map[string]context.CancelFunc - - logger *zap.SugaredLogger -} - -func NewCloudPubSubBusDispatcher(ref buses.BusReference, projectID string, opts *buses.BusOpts) (*CloudPubSubBus, error) { - ctx := context.Background() - pubsubClient, err := pubsub.NewClient(ctx, projectID) - if err != nil { - return nil, err - } - - bus := &CloudPubSubBus{ - ref: ref, - pubsubClient: pubsubClient, - } - eventHandlers := buses.EventHandlerFuncs{ - SubscribeFunc: func(channel buses.ChannelReference, subscription buses.SubscriptionReference, parameters buses.ResolvedParameters) error { - return bus.startReceivingEvents(subscription, parameters) - }, - UnsubscribeFunc: func(channel buses.ChannelReference, subscription buses.SubscriptionReference) error { - bus.stopReceivingEvents(subscription) - return nil - }, - ReceiveMessageFunc: func(channel buses.ChannelReference, messgae *buses.Message) error { - return bus.sendEventToTopic(channel, messgae) - }, - } - bus.dispatcher = buses.NewBusDispatcher(ref, eventHandlers, opts) - bus.logger = opts.Logger - bus.reconciler = opts.Reconciler - bus.receivers = make(map[string]context.CancelFunc) - return bus, nil -} - -func NewCloudPubSubBusProvisioner(ref buses.BusReference, projectID string, opts *buses.BusOpts) (*CloudPubSubBus, error) { - ctx := context.Background() - pubsubClient, err := pubsub.NewClient(ctx, projectID) - if err != nil { - return nil, err - } - - bus := &CloudPubSubBus{ - ref: ref, - pubsubClient: pubsubClient, - } - eventHandlers := buses.EventHandlerFuncs{ - ProvisionFunc: func(channel buses.ChannelReference, parameters buses.ResolvedParameters) error { - return bus.createTopic(channel, parameters) - }, - UnprovisionFunc: func(channel buses.ChannelReference) error { - return bus.deleteTopic(channel) - }, - SubscribeFunc: func(channel buses.ChannelReference, subscription buses.SubscriptionReference, parameters buses.ResolvedParameters) error { - return bus.createOrUpdateSubscription(channel, subscription, parameters) - }, - UnsubscribeFunc: func(channel buses.ChannelReference, subscription buses.SubscriptionReference) error { - return bus.deleteSubscription(subscription) - }, - } - bus.provisioner = buses.NewBusProvisioner(ref, eventHandlers, opts) - bus.logger = opts.Logger - return bus, nil -} - -func (b *CloudPubSubBus) Run(threadness int, stopCh <-chan struct{}) { - if b.dispatcher != nil { - b.dispatcher.Run(threadness, stopCh) - } - if b.provisioner != nil { - b.provisioner.Run(threadness, stopCh) - } -} - -// startReceivingEvents will receive events from a Cloud Pub/Sub Subscription for a -// Knative Subscription. This method will not block, but will continue to -// receive events until either this method or StopReceivingEvents is called for -// the same Subscription. -func (b *CloudPubSubBus) startReceivingEvents(ref buses.SubscriptionReference, parameters buses.ResolvedParameters) error { - ctx := context.Background() - cctx, cancel := context.WithCancel(ctx) - - // cancel current subscription receiver, if any - b.stopReceivingEvents(ref) - - subscriptionID := b.subscriptionID(ref) - subscription := b.pubsubClient.Subscription(subscriptionID) - b.receivers[subscriptionID] = cancel - - // check if subscription exists before receiving - if exists, err := subscription.Exists(ctx); err != nil { - return err - } else if !exists { - return fmt.Errorf("cannot receive message for a non-existent subscription %s", subscriptionID) - } - - // subscription.Receive blocks, so run it in a goroutine - go func() { - b.logger.Infof("Start receiving events for subscription %q", subscriptionID) - err := subscription.Receive(cctx, func(ctx context.Context, pubsubMessage *pubsub.Message) { - message := &buses.Message{ - Headers: pubsubMessage.Attributes, - Payload: pubsubMessage.Data, - } - err := b.dispatcher.DispatchMessage(ref, message) - if err != nil { - b.logger.Warnf("Unable to dispatch event %q to %q", pubsubMessage.ID, ref.String()) - pubsubMessage.Nack() - } else { - b.logger.Infof("Dispatched event %q to %q", pubsubMessage.ID, ref.String()) - pubsubMessage.Ack() - } - }) - if err != nil { - b.logger.Errorf("Error receiving messesages for %q: %v", subscriptionID, err) - } - delete(b.receivers, subscriptionID) - b.reconciler.RequeueSubscription(ref) - }() - - return nil -} - -// stopReceivingEvents stops receiving events for a previous call to -// StartReceivingEvents. Calls for a Subscription that is not not actively receiving -// are ignored. -func (b *CloudPubSubBus) stopReceivingEvents(ref buses.SubscriptionReference) { - subscriptionID := b.subscriptionID(ref) - if cancel, ok := b.receivers[subscriptionID]; ok { - b.logger.Infof("Stop receiving events for subscription %q", subscriptionID) - cancel() - delete(b.receivers, subscriptionID) - } -} - -// sendEventToTopic sends a message to the Cloud Pub/Sub Topic backing the -// Channel. -func (b *CloudPubSubBus) sendEventToTopic(channel buses.ChannelReference, message *buses.Message) error { - ctx := context.Background() - - topicID := b.topicID(channel) - topic := b.pubsubClient.Topic(topicID) - - result := topic.Publish(ctx, &pubsub.Message{ - Data: message.Payload, - Attributes: message.Headers, - }) - id, err := result.Get(ctx) - if err != nil { - return err - } - - // TODO allow topics to be reused between publish events, call .Stop after an idle period - topic.Stop() - - b.logger.Infof("Published a message to %s; msg ID: %v", topicID, id) - return nil -} - -// createTopic creates a Topic in Cloud Pub/Sub for the Channel. -func (b *CloudPubSubBus) createTopic(channel buses.ChannelReference, parameters buses.ResolvedParameters) error { - ctx := context.Background() - - topicID := b.topicID(channel) - topic := b.pubsubClient.Topic(topicID) - - // check if topic exists before creating - if exists, err := topic.Exists(ctx); err != nil { - return err - } else if exists { - return nil - } - - b.logger.Infof("Create topic %q", topicID) - topic, err := b.pubsubClient.CreateTopic(ctx, topicID) - if err != nil { - return err - } - - return nil -} - -// deleteTopic deletes the Topic in Cloud Pub/Sub for the Channel. -func (b *CloudPubSubBus) deleteTopic(channel buses.ChannelReference) error { - ctx := context.Background() - - topicID := b.topicID(channel) - topic := b.pubsubClient.Topic(topicID) - - // check if topic exists before deleting - if exists, err := topic.Exists(ctx); err != nil { - return err - } else if !exists { - return nil - } - - b.logger.Infof("Delete topic %q", topicID) - return topic.Delete(ctx) -} - -// createOrUpdateSubscription creates a Subscription in Cloud Pub/Sub for the -// Knative Subscription, or idempotently updates a Subscription if it already -// exists. -func (b *CloudPubSubBus) createOrUpdateSubscription(channel buses.ChannelReference, ref buses.SubscriptionReference, parameters buses.ResolvedParameters) error { - ctx := context.Background() - - subscriptionID := b.subscriptionID(ref) - subscription := b.pubsubClient.Subscription(subscriptionID) - - // check if subscription exists before creating - if exists, err := subscription.Exists(ctx); err != nil { - return err - } else if exists { - // TODO once the bus has configurable params, update subscription configuration - // _, err := subscription.Update(b.ctx, pubsub.SubscriptionConfigToUpdate{}) - // return err - return nil - } - - // create subscription - topicID := b.topicID(channel) - topic := b.pubsubClient.Topic(topicID) - b.logger.Infof("Create subscription %q for topic %q", subscriptionID, topicID) - subscription, err := b.pubsubClient.CreateSubscription(ctx, subscriptionID, pubsub.SubscriptionConfig{ - Topic: topic, - }) - return err -} - -// deleteSubscription removes a Subscription from Cloud Pub/Sub for a Knative -// Subscription. -func (b *CloudPubSubBus) deleteSubscription(ref buses.SubscriptionReference) error { - ctx := context.Background() - - subscriptionID := b.subscriptionID(ref) - subscription := b.pubsubClient.Subscription(subscriptionID) - - // check if subscription exists before deleting - if exists, err := subscription.Exists(ctx); err != nil { - return err - } else if !exists { - return nil - } - - b.logger.Infof("Deleting subscription %q", subscriptionID) - return subscription.Delete(ctx) -} - -func (b *CloudPubSubBus) topicID(channel buses.ChannelReference) string { - return fmt.Sprintf("channel-%s-%s-%s", b.ref.Name, channel.Namespace, channel.Name) -} - -func (b *CloudPubSubBus) subscriptionID(subscription buses.SubscriptionReference) string { - return fmt.Sprintf("subscription-%s-%s-%s", b.ref.Name, subscription.Namespace, subscription.Name) -} diff --git a/pkg/buses/gcppubsub/bus_test.go b/pkg/buses/gcppubsub/bus_test.go deleted file mode 100644 index 326a3924cc8..00000000000 --- a/pkg/buses/gcppubsub/bus_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package gcppubsub - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/buses/gcppubsub/dispatcher/kodata/LICENSE b/pkg/buses/gcppubsub/dispatcher/kodata/LICENSE deleted file mode 120000 index 2a64f9d0fc6..00000000000 --- a/pkg/buses/gcppubsub/dispatcher/kodata/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../LICENSE \ No newline at end of file diff --git a/pkg/buses/gcppubsub/dispatcher/kodata/VENDOR-LICENSE b/pkg/buses/gcppubsub/dispatcher/kodata/VENDOR-LICENSE deleted file mode 120000 index 681789ed22f..00000000000 --- a/pkg/buses/gcppubsub/dispatcher/kodata/VENDOR-LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/pkg/buses/gcppubsub/dispatcher/main.go b/pkg/buses/gcppubsub/dispatcher/main.go deleted file mode 100644 index 8718b170e81..00000000000 --- a/pkg/buses/gcppubsub/dispatcher/main.go +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "flag" - "os" - - "github.com/knative/eventing/pkg/buses" - "github.com/knative/eventing/pkg/buses/gcppubsub" - "github.com/knative/pkg/signals" - "go.uber.org/zap" -) - -const ( - threadsPerReconciler = 1 -) - -func main() { - ref := buses.NewBusReferenceFromNames( - os.Getenv("BUS_NAME"), - os.Getenv("BUS_NAMESPACE"), - ) - - config := buses.NewLoggingConfig() - logger := buses.NewBusLoggerFromConfig(config) - defer logger.Sync() - logger = logger.With( - zap.String("channels.knative.dev/bus", ref.String()), - zap.String("channels.knative.dev/busType", gcppubsub.BusType), - zap.String("channels.knative.dev/busComponent", buses.Dispatcher), - ) - - projectID := os.Getenv("GOOGLE_CLOUD_PROJECT") - if projectID == "" { - logger.Fatalf("GOOGLE_CLOUD_PROJECT environment variable must be set") - } - - opts := &buses.BusOpts{ - Logger: logger, - } - - flag.StringVar(&opts.KubeConfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") - flag.StringVar(&opts.MasterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.") - flag.Parse() - - bus, err := gcppubsub.NewCloudPubSubBusDispatcher(ref, projectID, opts) - if err != nil { - logger.Fatalf("Error starting pub/sub bus dispatcher: %v", err) - } - - // set up signals so we handle the first shutdown signal gracefully - stopCh := signals.SetupSignalHandler() - bus.Run(threadsPerReconciler, stopCh) -} diff --git a/pkg/buses/gcppubsub/provisioner/kodata/LICENSE b/pkg/buses/gcppubsub/provisioner/kodata/LICENSE deleted file mode 120000 index 2a64f9d0fc6..00000000000 --- a/pkg/buses/gcppubsub/provisioner/kodata/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../LICENSE \ No newline at end of file diff --git a/pkg/buses/gcppubsub/provisioner/kodata/VENDOR-LICENSE b/pkg/buses/gcppubsub/provisioner/kodata/VENDOR-LICENSE deleted file mode 120000 index 681789ed22f..00000000000 --- a/pkg/buses/gcppubsub/provisioner/kodata/VENDOR-LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/pkg/buses/gcppubsub/provisioner/main.go b/pkg/buses/gcppubsub/provisioner/main.go deleted file mode 100644 index e2a4fd87384..00000000000 --- a/pkg/buses/gcppubsub/provisioner/main.go +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "flag" - "os" - - "github.com/knative/eventing/pkg/buses" - "github.com/knative/eventing/pkg/buses/gcppubsub" - "github.com/knative/pkg/signals" - "go.uber.org/zap" -) - -const ( - threadsPerReconciler = 1 -) - -func main() { - ref := buses.NewBusReferenceFromNames( - os.Getenv("BUS_NAME"), - os.Getenv("BUS_NAMESPACE"), - ) - - config := buses.NewLoggingConfig() - logger := buses.NewBusLoggerFromConfig(config) - defer logger.Sync() - logger = logger.With( - zap.String("channels.knative.dev/bus", ref.String()), - zap.String("channels.knative.dev/busType", gcppubsub.BusType), - zap.String("channels.knative.dev/busComponent", buses.Provisioner), - ) - - projectID := os.Getenv("GOOGLE_CLOUD_PROJECT") - if projectID == "" { - logger.Fatalf("GOOGLE_CLOUD_PROJECT environment variable must be set") - } - - opts := &buses.BusOpts{ - Logger: logger} - - flag.StringVar(&opts.KubeConfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") - flag.StringVar(&opts.MasterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.") - flag.Parse() - - bus, err := gcppubsub.NewCloudPubSubBusProvisioner(ref, projectID, opts) - if err != nil { - logger.Fatalf("Error starting pub/sub bus provisioner: %v", err) - } - - // set up signals so we handle the first shutdown signal gracefully - stopCh := signals.SetupSignalHandler() - bus.Run(threadsPerReconciler, stopCh) -} diff --git a/pkg/buses/handler_funcs.go b/pkg/buses/handler_funcs.go deleted file mode 100644 index 75e899ea0fd..00000000000 --- a/pkg/buses/handler_funcs.go +++ /dev/null @@ -1,258 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package buses - -import ( - "fmt" - - "go.uber.org/zap" - - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "github.com/knative/eventing/pkg/controller/util" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/equality" -) - -const ( - // SuccessSynced is used as part of the Event 'reason' when a resource is synced - successSynced = "Synced" - // ErrResourceSync is used as part of the Event 'reason' when a resource fails - // to sync. - errResourceSync = "ErrResourceSync" -) - -// ResolvedParameters is a map containing parameter names and the resolved -// values from attributes or parameter defaults. -type ResolvedParameters = map[string]string - -// EventHandlerFuncs is a set of handler functions that are called when a bus -// requires sync, channels are provisioned/unprovisioned, or a subscription is -// created or deleted, or if one of the relevant resources is changed. -type EventHandlerFuncs struct { - // BusFunc is invoked when the Bus requires sync. - BusFunc func(bus BusReference) error - - // ProvisionFunc is invoked when a new Channel should be provisioned or when - // the attributes change. - ProvisionFunc func(channel ChannelReference, parameters ResolvedParameters) error - - // UnprovisionFunc in invoked when a Channel should be deleted. - UnprovisionFunc func(channel ChannelReference) error - - // SubscribeFunc is invoked when a new Subscription should be set up or when - // the attributes change. - SubscribeFunc func(channel ChannelReference, subscription SubscriptionReference, parameters ResolvedParameters) error - - // UnsubscribeFunc is invoked when a Subscription should be deleted. - UnsubscribeFunc func(channel ChannelReference, subscription SubscriptionReference) error - - // ReceiveMessageFunc is invoked when a Message is received on a Channel - ReceiveMessageFunc func(channel ChannelReference, message *Message) error - - logger *zap.SugaredLogger -} - -func (h EventHandlerFuncs) onBus(bus channelsv1alpha1.GenericBus, reconciler *Reconciler) error { - if h.BusFunc == nil { - return nil - } - ref := NewBusReference(bus) - err := h.BusFunc(ref) - if err != nil { - reconciler.RecordBusEventf(corev1.EventTypeWarning, errResourceSync, "Error syncing Bus: %s", err) - } else { - reconciler.RecordBusEventf(corev1.EventTypeNormal, successSynced, "Bus synched successfully") - } - return err -} - -func (h EventHandlerFuncs) onProvision(channel *channelsv1alpha1.Channel, reconciler *Reconciler) error { - if h.ProvisionFunc == nil { - return nil - } - parameters, err := h.resolveChannelParameters(reconciler.bus.GetSpec(), channel.Spec) - if err != nil { - return err - } - channelCopy := channel.DeepCopy() - var cond *channelsv1alpha1.ChannelCondition - ref := NewChannelReference(channel) - err = h.ProvisionFunc(ref, parameters) - if err != nil { - reconciler.RecordChannelEventf(ref, corev1.EventTypeWarning, errResourceSync, "Error provisioning channel: %s", err) - cond = util.NewChannelCondition(channelsv1alpha1.ChannelProvisioned, corev1.ConditionFalse, errResourceSync, err.Error()) - } else { - reconciler.RecordChannelEventf(ref, corev1.EventTypeNormal, successSynced, "Channel provisioned successfully") - cond = util.NewChannelCondition(channelsv1alpha1.ChannelProvisioned, corev1.ConditionTrue, successSynced, "Channel provisioned successfully") - } - util.SetChannelCondition(&channelCopy.Status, *cond) - util.ConsolidateChannelCondition(&channelCopy.Status) - if !equality.Semantic.DeepEqual(channel.Status, channelCopy.Status) { - // status has changed, update channel - _, errS := reconciler.eventingClient.ChannelsV1alpha1().Channels(channel.Namespace).Update(channelCopy) - if errS != nil { - h.logger.Warnf("Could not update channel status: %v", errS) - if err != nil { - return fmt.Errorf("error provisioning channel (%v); error updating channel status (%v)", err, errS) - } - return errS - } - } - return err -} - -func (h EventHandlerFuncs) onUnprovision(channel *channelsv1alpha1.Channel, reconciler *Reconciler) error { - if h.UnprovisionFunc == nil { - return nil - } - ref := NewChannelReference(channel) - if err := h.UnprovisionFunc(ref); err != nil { - reconciler.RecordChannelEventf(ref, corev1.EventTypeWarning, errResourceSync, "Error unprovisioning channel: %s", err) - return err - } - reconciler.RecordChannelEventf(ref, corev1.EventTypeNormal, successSynced, "Channel unprovisioned successfully") - // skip updating status conditions since the channel was deleted - return nil -} - -func (h EventHandlerFuncs) onSubscribe(subscription *channelsv1alpha1.Subscription, reconciler *Reconciler) error { - if h.SubscribeFunc == nil { - return nil - } - parameters, err := h.resolveSubscriptionParameters(reconciler.bus.GetSpec(), subscription.Spec) - if err != nil { - return err - } - channel := NewChannelReferenceFromSubscription(subscription) - ref := NewSubscriptionReference(subscription) - subscriptionCopy := subscription.DeepCopy() - var cond *channelsv1alpha1.SubscriptionCondition - err = h.SubscribeFunc(channel, ref, parameters) - if err != nil { - reconciler.RecordSubscriptionEventf(ref, corev1.EventTypeWarning, errResourceSync, "Error subscribing: %s", err) - cond = util.NewSubscriptionCondition(channelsv1alpha1.SubscriptionDispatching, corev1.ConditionFalse, errResourceSync, err.Error()) - } else { - reconciler.RecordSubscriptionEventf(ref, corev1.EventTypeNormal, successSynced, "Subscribed successfully") - cond = util.NewSubscriptionCondition(channelsv1alpha1.SubscriptionDispatching, corev1.ConditionTrue, successSynced, "Subscription dispatcher successfully created") - } - util.SetSubscriptionCondition(&subscriptionCopy.Status, *cond) - if !equality.Semantic.DeepEqual(subscription.Status, subscriptionCopy.Status) { - // status has changed, update subscription - _, errS := reconciler.eventingClient.ChannelsV1alpha1().Subscriptions(subscription.Namespace).Update(subscriptionCopy) - if errS != nil { - h.logger.Warnf("Could not update subscription status: %v", errS) - if err != nil { - return fmt.Errorf("error subscribing (%v); error updating subscription status (%v)", err, errS) - } - return errS - } - } - return err -} - -func (h EventHandlerFuncs) onUnsubscribe(subscription *channelsv1alpha1.Subscription, reconciler *Reconciler) error { - if h.UnsubscribeFunc == nil { - return nil - } - channel := NewChannelReferenceFromSubscription(subscription) - ref := NewSubscriptionReference(subscription) - if err := h.UnsubscribeFunc(channel, ref); err != nil { - reconciler.RecordSubscriptionEventf(ref, corev1.EventTypeWarning, errResourceSync, "Error unsubscribing: %s", err) - return err - } - reconciler.RecordSubscriptionEventf(ref, corev1.EventTypeNormal, successSynced, "Unsubscribed successfully") - // skip updating status conditions since the subscription was deleted - return nil -} - -func (h EventHandlerFuncs) onReceiveMessage(channel ChannelReference, message *Message) error { - if h.ReceiveMessageFunc == nil { - // TODO use a static error - return fmt.Errorf("unable to dispatch message") - } - return h.ReceiveMessageFunc(channel, message) -} - -// resolveChannelParameters resolves the given Channel Parameters and the Bus' -// Channel Parameters, returning an ResolvedParameters or an Error. -func (h EventHandlerFuncs) resolveChannelParameters(bus *channelsv1alpha1.BusSpec, channel channelsv1alpha1.ChannelSpec) (ResolvedParameters, error) { - genericBusParameters := bus.Parameters - var parameters *[]channelsv1alpha1.Parameter - if genericBusParameters != nil { - parameters = genericBusParameters.Channel - } - return h.resolveParameters(parameters, channel.Arguments) -} - -// resolveSubscriptionParameters resolves the given Subscription Parameters and -// the Bus' Subscription Parameters, returning an Attributes or an Error. -func (h EventHandlerFuncs) resolveSubscriptionParameters(bus *channelsv1alpha1.BusSpec, subscription channelsv1alpha1.SubscriptionSpec) (ResolvedParameters, error) { - genericBusParameters := bus.Parameters - var parameters *[]channelsv1alpha1.Parameter - if genericBusParameters != nil { - parameters = genericBusParameters.Subscription - } - return h.resolveParameters(parameters, subscription.Arguments) -} - -// resolveParameters resolves a slice of Parameters and a slice of arguments and -// returns an Attributes or an error if there are missing Arguments. Each -// Parameter represents a variable that must be provided by an Argument or -// optionally defaulted if a default value for the Parameter is specified. -// resolveAttributes combines the given arrays of Parameters and Arguments, -// using default values where necessary and returning an error if there are -// missing Arguments. -func (h EventHandlerFuncs) resolveParameters(parameters *[]channelsv1alpha1.Parameter, arguments *[]channelsv1alpha1.Argument) (ResolvedParameters, error) { - resolved := make(ResolvedParameters) - known := make(map[string]interface{}) - required := make(map[string]interface{}) - - // apply parameters - if parameters != nil { - for _, param := range *parameters { - known[param.Name] = true - if param.Default != nil { - resolved[param.Name] = *param.Default - } else { - required[param.Name] = true - } - } - } - // apply arguments - if arguments != nil { - for _, arg := range *arguments { - if _, ok := known[arg.Name]; !ok { - // ignore arguments not defined by parameters - h.logger.Warnf("Skipping unknown argument: %s", arg.Name) - continue - } - delete(required, arg.Name) - resolved[arg.Name] = arg.Value - } - } - - // check for missing arguments - if len(required) != 0 { - missing := []string{} - for name := range required { - missing = append(missing, name) - } - return nil, fmt.Errorf("missing required arguments: %v", missing) - } - - return resolved, nil -} diff --git a/pkg/buses/kafka/bus.go b/pkg/buses/kafka/bus.go deleted file mode 100644 index 269a8102aa1..00000000000 --- a/pkg/buses/kafka/bus.go +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package kafka - -import ( - "fmt" - "strconv" - - "go.uber.org/zap" - - "github.com/Shopify/sarama" - cluster "github.com/bsm/sarama-cluster" - "github.com/knative/eventing/pkg/buses" -) - -const ( - // BusType is the type of the kafka bus - BusType = "kafka" - - initialOffset = "initialOffset" - numPartitions = "NumPartitions" - newest = "Newest" - oldest = "Oldest" -) - -type KafkaBus struct { - ref buses.BusReference - dispatcher buses.BusDispatcher - provisioner buses.BusProvisioner - - kafkaBrokers []string - kafkaClusterAdmin sarama.ClusterAdmin - kafkaAsyncProducer sarama.AsyncProducer - kafkaConsumers map[buses.SubscriptionReference]*cluster.Consumer - - logger *zap.SugaredLogger -} - -func NewKafkaBusDispatcher(ref buses.BusReference, brokers []string, opts *buses.BusOpts) (*KafkaBus, error) { - bus := &KafkaBus{ - ref: ref, - kafkaBrokers: brokers, - } - eventHandlers := buses.EventHandlerFuncs{ - SubscribeFunc: func(channel buses.ChannelReference, subscription buses.SubscriptionReference, parameters buses.ResolvedParameters) error { - return bus.subscribe(channel, subscription, parameters) - }, - UnsubscribeFunc: func(channel buses.ChannelReference, subscription buses.SubscriptionReference) error { - return bus.unsubscribe(channel, subscription) - }, - ReceiveMessageFunc: func(channel buses.ChannelReference, message *buses.Message) error { - bus.kafkaAsyncProducer.Input() <- toKafkaMessage(channel, message) - return nil - }, - } - bus.dispatcher = buses.NewBusDispatcher(ref, eventHandlers, opts) - bus.logger = opts.Logger - bus.kafkaConsumers = make(map[buses.SubscriptionReference]*cluster.Consumer) - - conf := sarama.NewConfig() - conf.Version = sarama.V1_1_0_0 - conf.ClientID = ref.Name + "-dispatcher" - client, err := sarama.NewClient(brokers, conf) - if err != nil { - return nil, fmt.Errorf("unable to create kafka client: %v", err) - } - producer, err := sarama.NewAsyncProducerFromClient(client) - if err != nil { - return nil, fmt.Errorf("unable to create kafka producer: %v", err) - } - bus.kafkaAsyncProducer = producer - - return bus, nil -} - -func NewKafkaBusProvisioner(ref buses.BusReference, brokers []string, opts *buses.BusOpts) (*KafkaBus, error) { - bus := &KafkaBus{ - ref: ref, - kafkaBrokers: brokers, - } - eventHandlers := buses.EventHandlerFuncs{ - ProvisionFunc: func(channel buses.ChannelReference, parameters buses.ResolvedParameters) error { - return bus.provision(channel, parameters) - }, - UnprovisionFunc: func(channel buses.ChannelReference) error { - return bus.unprovision(channel) - }, - } - bus.provisioner = buses.NewBusProvisioner(ref, eventHandlers, opts) - bus.logger = opts.Logger - - conf := sarama.NewConfig() - conf.Version = sarama.V1_1_0_0 - conf.ClientID = ref.Name + "-provisioner" - - clusterAdmin, err := sarama.NewClusterAdmin(brokers, conf) - if err != nil { - return nil, fmt.Errorf("unable to building kafka admin client: %v", err) - } - bus.kafkaClusterAdmin = clusterAdmin - - return bus, nil -} - -func (b *KafkaBus) Run(threadness int, stopCh <-chan struct{}) { - if b.kafkaAsyncProducer != nil { - go func() { - for { - select { - case e := <-b.kafkaAsyncProducer.Errors(): - b.logger.Warnf("Got %v", e) - case s := <-b.kafkaAsyncProducer.Successes(): - b.logger.Infof("Sent %v", s) - case <-stopCh: - return - } - } - }() - } - if b.dispatcher != nil { - b.dispatcher.Run(threadness, stopCh) - } - if b.provisioner != nil { - b.provisioner.Run(threadness, stopCh) - } -} - -func (b *KafkaBus) subscribe(channel buses.ChannelReference, subscription buses.SubscriptionReference, parameters buses.ResolvedParameters) error { - if _, ok := b.kafkaConsumers[subscription]; ok { - // subscribe can be called multiple times for the same subscription, - //unsubscribe before we resubscribe - err := b.unsubscribe(channel, subscription) - if err != nil { - return err - } - } - - b.logger.Infof("Subscribing %q to %q (%v)", subscription.String(), channel.String(), parameters) - - topicName := topicName(channel) - - initialOffset, err := resolveInitialOffset(parameters) - if err != nil { - return err - } - - group := fmt.Sprintf("%s.%s.%s", b.ref.Name, subscription.Namespace, subscription.Name) - consumerConfig := cluster.NewConfig() - consumerConfig.Version = sarama.V1_1_0_0 - consumerConfig.Consumer.Offsets.Initial = initialOffset - consumer, err := cluster.NewConsumer(b.kafkaBrokers, group, []string{topicName}, consumerConfig) - if err != nil { - return err - } - - b.kafkaConsumers[subscription] = consumer - - go func() { - for { - msg, more := <-consumer.Messages() - if more { - b.logger.Infof("Dispatching a message for subscription %q", subscription.String()) - message := fromKafkaMessage(msg) - err := b.dispatcher.DispatchMessage(subscription, message) - if err != nil { - b.logger.Warnf("Got error trying to dispatch message: %v", err) - } - // TODO: handle errors with pluggable strategy - consumer.MarkOffset(msg, "") // Mark message as processed - } else { - break - } - } - b.logger.Infof("Consumer for subscription %q stopped", subscription.String()) - }() - - return nil -} - -func (b *KafkaBus) unsubscribe(channel buses.ChannelReference, subscription buses.SubscriptionReference) error { - b.logger.Infof("Un-Subscribing %q from %q", subscription.String(), channel.String()) - if consumer, ok := b.kafkaConsumers[subscription]; ok { - delete(b.kafkaConsumers, subscription) - return consumer.Close() - } - return nil -} - -func (b *KafkaBus) provision(channel buses.ChannelReference, parameters buses.ResolvedParameters) error { - topicName := topicName(channel) - b.logger.Infof("Provisioning topic %s on bus backed by Kafka", topicName) - - partitions := 1 - if p, ok := parameters[numPartitions]; ok { - var err error - intPartitions, err := strconv.Atoi(p) - if err != nil { - b.logger.Warnf("Could not parse partition count for %q: %s", channel.String(), p) - } else { - partitions = intPartitions - } - } - - err := b.kafkaClusterAdmin.CreateTopic(topicName, &sarama.TopicDetail{ - ReplicationFactor: 1, - NumPartitions: int32(partitions), - }, false) - if err == sarama.ErrTopicAlreadyExists { - return nil - } else if err != nil { - b.logger.Errorf("Error creating topic %s: %v", topicName, err) - } else { - b.logger.Infof("Successfully created topic %s", topicName) - } - return err -} - -func (b *KafkaBus) unprovision(channel buses.ChannelReference) error { - topicName := topicName(channel) - b.logger.Infof("Un-provisioning topic %s from bus backed by Kafka", topicName) - - err := b.kafkaClusterAdmin.DeleteTopic(topicName) - if err == sarama.ErrUnknownTopicOrPartition { - return nil - } else if err != nil { - b.logger.Errorf("Error deleting topic %s: %v", topicName, err) - } else { - b.logger.Infof("Successfully deleted topic %s", topicName) - } - - return err -} - -func toKafkaMessage(channel buses.ChannelReference, message *buses.Message) *sarama.ProducerMessage { - kafkaMessage := sarama.ProducerMessage{ - Topic: topicName(channel), - Value: sarama.ByteEncoder(message.Payload), - } - for h, v := range message.Headers { - kafkaMessage.Headers = append(kafkaMessage.Headers, sarama.RecordHeader{ - Key: []byte(h), - Value: []byte(v), - }) - } - return &kafkaMessage -} - -func fromKafkaMessage(kafkaMessage *sarama.ConsumerMessage) *buses.Message { - headers := make(map[string]string) - for _, header := range kafkaMessage.Headers { - headers[string(header.Key)] = string(header.Value) - } - message := buses.Message{ - Headers: headers, - Payload: kafkaMessage.Value, - } - return &message -} - -func topicName(channel buses.ChannelReference) string { - return fmt.Sprintf("%s.%s", channel.Namespace, channel.Name) -} - -func resolveInitialOffset(parameters buses.ResolvedParameters) (int64, error) { - switch parameters[initialOffset] { - case oldest: - return sarama.OffsetOldest, nil - case newest: - return sarama.OffsetNewest, nil - default: - return 0, fmt.Errorf("unsupported initialOffset value. Must be one of %s or %s", oldest, newest) - } -} diff --git a/pkg/buses/kafka/bus_test.go b/pkg/buses/kafka/bus_test.go deleted file mode 100644 index 59652629577..00000000000 --- a/pkg/buses/kafka/bus_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package kafka - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/buses/kafka/dispatcher/kodata/LICENSE b/pkg/buses/kafka/dispatcher/kodata/LICENSE deleted file mode 120000 index 2a64f9d0fc6..00000000000 --- a/pkg/buses/kafka/dispatcher/kodata/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../LICENSE \ No newline at end of file diff --git a/pkg/buses/kafka/dispatcher/kodata/VENDOR-LICENSE b/pkg/buses/kafka/dispatcher/kodata/VENDOR-LICENSE deleted file mode 120000 index 681789ed22f..00000000000 --- a/pkg/buses/kafka/dispatcher/kodata/VENDOR-LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/pkg/buses/kafka/dispatcher/main.go b/pkg/buses/kafka/dispatcher/main.go deleted file mode 100644 index 7014f9d8061..00000000000 --- a/pkg/buses/kafka/dispatcher/main.go +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "flag" - "os" - "strings" - - "github.com/Shopify/sarama" - "github.com/knative/eventing/pkg/buses" - "github.com/knative/eventing/pkg/buses/kafka" - "github.com/knative/pkg/signals" - "go.uber.org/zap" -) - -const ( - threadsPerReconciler = 1 -) - -func main() { - ref := buses.NewBusReferenceFromNames( - os.Getenv("BUS_NAME"), - os.Getenv("BUS_NAMESPACE"), - ) - - config := buses.NewLoggingConfig() - logger := buses.NewBusLoggerFromConfig(config) - defer logger.Sync() - logger = logger.With( - zap.String("channels.knative.dev/bus", ref.String()), - zap.String("channels.knative.dev/busType", kafka.BusType), - zap.String("channels.knative.dev/busComponent", buses.Dispatcher), - ) - sarama.Logger = zap.NewStdLog(logger.With(zap.Namespace("Sarama")).Desugar()) - - brokers := strings.Split(os.Getenv("KAFKA_BOOTSTRAP_SERVERS"), ",") - if len(brokers) == 0 { - logger.Fatalf("Environment variable KAFKA_BOOTSTRAP_SERVERS not set") - } - - opts := &buses.BusOpts{ - Logger: logger, - } - - flag.StringVar(&opts.KubeConfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") - flag.StringVar(&opts.MasterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.") - flag.Parse() - - bus, err := kafka.NewKafkaBusDispatcher(ref, brokers, opts) - if err != nil { - logger.Fatalf("Error starting kafka bus dispatcher: %v", err) - } - - // set up signals so we handle the first shutdown signal gracefully - stopCh := signals.SetupSignalHandler() - bus.Run(threadsPerReconciler, stopCh) -} diff --git a/pkg/buses/kafka/provisioner/kodata/LICENSE b/pkg/buses/kafka/provisioner/kodata/LICENSE deleted file mode 120000 index 2a64f9d0fc6..00000000000 --- a/pkg/buses/kafka/provisioner/kodata/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../LICENSE \ No newline at end of file diff --git a/pkg/buses/kafka/provisioner/kodata/VENDOR-LICENSE b/pkg/buses/kafka/provisioner/kodata/VENDOR-LICENSE deleted file mode 120000 index 681789ed22f..00000000000 --- a/pkg/buses/kafka/provisioner/kodata/VENDOR-LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/pkg/buses/kafka/provisioner/main.go b/pkg/buses/kafka/provisioner/main.go deleted file mode 100644 index bc07b7dc3a8..00000000000 --- a/pkg/buses/kafka/provisioner/main.go +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "flag" - "os" - "strings" - - "github.com/Shopify/sarama" - "github.com/knative/eventing/pkg/buses" - "github.com/knative/eventing/pkg/buses/kafka" - "github.com/knative/pkg/signals" - "go.uber.org/zap" -) - -const ( - threadsPerReconciler = 1 -) - -func main() { - ref := buses.NewBusReferenceFromNames( - os.Getenv("BUS_NAME"), - os.Getenv("BUS_NAMESPACE"), - ) - - config := buses.NewLoggingConfig() - logger := buses.NewBusLoggerFromConfig(config) - defer logger.Sync() - logger = logger.With( - zap.String("channels.knative.dev/bus", ref.String()), - zap.String("channels.knative.dev/busType", kafka.BusType), - zap.String("channels.knative.dev/busComponent", buses.Provisioner), - ) - sarama.Logger = zap.NewStdLog(logger.With(zap.Namespace("Sarama")).Desugar()) - - opts := &buses.BusOpts{ - Logger: logger, - } - - brokers := strings.Split(os.Getenv("KAFKA_BOOTSTRAP_SERVERS"), ",") - if len(brokers) == 0 { - logger.Fatalf("Environment variable KAFKA_BOOTSTRAP_SERVERS not set") - } - - flag.StringVar(&opts.KubeConfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") - flag.StringVar(&opts.MasterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.") - flag.Parse() - - bus, err := kafka.NewKafkaBusProvisioner(ref, brokers, opts) - if err != nil { - logger.Fatalf("Error starting kafka bus provisioner: %v", err) - } - - // set up signals so we handle the first shutdown signal gracefully - stopCh := signals.SetupSignalHandler() - bus.Run(threadsPerReconciler, stopCh) -} diff --git a/pkg/buses/logger.go b/pkg/buses/logger.go index 261b583a5f9..ed439cc34ae 100644 --- a/pkg/buses/logger.go +++ b/pkg/buses/logger.go @@ -24,8 +24,6 @@ import ( const ( busLoggingComponent = "bus" - reconcilerLoggingComponent = "reconciler" - handlerLoggingComponent = "handler" dispatcherLoggingComponent = "dispatcher" receiverLoggingComponent = "receiver" ) diff --git a/pkg/buses/reconciler.go b/pkg/buses/reconciler.go deleted file mode 100644 index a0605c03188..00000000000 --- a/pkg/buses/reconciler.go +++ /dev/null @@ -1,681 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package buses - -import ( - "fmt" - "strings" - "time" - - "go.uber.org/zap" - - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - eventingclientset "github.com/knative/eventing/pkg/client/clientset/versioned" - eventingscheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - eventinginformers "github.com/knative/eventing/pkg/client/informers/externalversions" - channelslisters "github.com/knative/eventing/pkg/client/listers/channels/v1alpha1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - kubernetesclientset "k8s.io/client-go/kubernetes" - typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" - informercache "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/workqueue" -) - -const ( - // Dispatcher manages the data plane for a bus - Dispatcher = "dispatcher" - // Provisioner manages the control plane for a bus - Provisioner = "provisioner" - - busKind = "Bus" - clusterBusKind = "ClusterBus" - channelKind = "Channel" - subscriptionKind = "Subscription" -) - -// Reconciler is a utility mix-in intended to be used by Bus authors to easily -// write provisioners and dispatchers for buses. Bus provisioners are -// responsible for managing the storage asset(s) that back a channel. Bus -// dispatchers are responsible for dispatching events on the Channel to the -// Channel's Subscriptions. Reconciler handles setting up informers that watch -// a Bus, its Channels, and their Subscriptions and allows Bus authors to -// register event handler functions to be called when Provision/Unprovision and -// Subscribe/Unsubscribe happen. -type Reconciler struct { - bus channelsv1alpha1.GenericBus - ref BusReference - handler EventHandlerFuncs - cache *Cache - - eventingInformerFactory eventinginformers.SharedInformerFactory - eventingClient eventingclientset.Interface - busesLister channelslisters.BusLister - busesSynced informercache.InformerSynced - clusterBusesLister channelslisters.ClusterBusLister - clusterBusesSynced informercache.InformerSynced - channelsLister channelslisters.ChannelLister - channelsSynced informercache.InformerSynced - subscriptionsLister channelslisters.SubscriptionLister - subscriptionsSynced informercache.InformerSynced - - // workqueue is a rate limited work queue. This is used to queue work to be - // processed instead of performing it as soon as a change happens. This - // means we can ensure we only process a fixed amount of resources at a - // time, and makes it easy to ensure we are never processing the same item - // simultaneously in two different workers. - workqueue workqueue.RateLimitingInterface - // recorder is an event recorder for recording Event resources to the - // Kubernetes API. - recorder record.EventRecorder - - logger *zap.SugaredLogger -} - -// NewReconciler creates a reconciler for a bus given: -// -// component: the name of the component this reconciler should use in created k8s events -// masterURL: the URL of the API server the reconciler should communicate with -// kubeconfig: the path of a kubeconfig file to create a client connection to the masterURL with -// handler: a EventHandlerFuncs with handler functions for the reconciler to call -func NewReconciler( - ref BusReference, - component, masterURL, kubeconfig string, - cache *Cache, handler EventHandlerFuncs, - logger *zap.SugaredLogger, -) *Reconciler { - cfg, err := clientcmd.BuildConfigFromFlags(masterURL, kubeconfig) - if err != nil { - logger.Fatalf("Error building kubeconfig: %v", err) - } - - kubeClient, err := kubernetesclientset.NewForConfig(cfg) - if err != nil { - logger.Fatalf("Error building kubernetes clientset: %v", err) - } - - eventingClient, err := eventingclientset.NewForConfig(cfg) - if err != nil { - logger.Fatalf("Error building eventing clientset: %v", err) - } - - eventingInformerFactory := eventinginformers.NewSharedInformerFactory(eventingClient, time.Second*30) - busInformer := eventingInformerFactory.Channels().V1alpha1().Buses() - clusterBusInformer := eventingInformerFactory.Channels().V1alpha1().ClusterBuses() - channelInformer := eventingInformerFactory.Channels().V1alpha1().Channels() - subscriptionInformer := eventingInformerFactory.Channels().V1alpha1().Subscriptions() - - // Create event broadcaster - // Add types to the default Kubernetes Scheme so Events can be logged for the component. - eventingscheme.AddToScheme(eventingscheme.Scheme) - logger.Info("Creating event broadcaster") - eventBroadcaster := record.NewBroadcaster() - eventBroadcaster.StartLogging(logger.Infof) - eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeClient.CoreV1().Events("")}) - recorder := eventBroadcaster.NewRecorder(eventingscheme.Scheme, corev1.EventSource{Component: component}) - - reconciler := &Reconciler{ - bus: nil, - ref: ref, - handler: handler, - cache: cache, - - eventingClient: eventingClient, - eventingInformerFactory: eventingInformerFactory, - busesLister: busInformer.Lister(), - busesSynced: busInformer.Informer().HasSynced, - clusterBusesLister: clusterBusInformer.Lister(), - clusterBusesSynced: clusterBusInformer.Informer().HasSynced, - channelsLister: channelInformer.Lister(), - channelsSynced: channelInformer.Informer().HasSynced, - subscriptionsLister: subscriptionInformer.Lister(), - subscriptionsSynced: subscriptionInformer.Informer().HasSynced, - - workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Reconciler"), - recorder: recorder, - - logger: logger, - } - - logger.Info("Setting up event handlers") - if reconciler.ref.IsNamespaced() { - // Set up an event handler for when Bus resources change - busInformer.Informer().AddEventHandler(informercache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - bus := obj.(*channelsv1alpha1.Bus) - reconciler.workqueue.AddRateLimited(makeWorkqueueKeyForBus(bus)) - }, - UpdateFunc: func(old, new interface{}) { - oldBus := old.(*channelsv1alpha1.Bus) - newBus := new.(*channelsv1alpha1.Bus) - - if oldBus.ResourceVersion == newBus.ResourceVersion { - // Periodic resync will send update events for all known Buses. - // Two different versions of the same Bus will always have different RVs. - return - } - - reconciler.workqueue.AddRateLimited(makeWorkqueueKeyForBus(newBus)) - }, - }) - } else { - // Set up an event handler for when ClusterBus resources change - clusterBusInformer.Informer().AddEventHandler(informercache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - clusterBus := obj.(*channelsv1alpha1.ClusterBus) - reconciler.workqueue.AddRateLimited(makeWorkqueueKeyForClusterBus(clusterBus)) - }, - UpdateFunc: func(old, new interface{}) { - oldClusterBus := old.(*channelsv1alpha1.ClusterBus) - newClusterBus := new.(*channelsv1alpha1.ClusterBus) - - if oldClusterBus.ResourceVersion == newClusterBus.ResourceVersion { - // Periodic resync will send update events for all known ClusterBuses. - // Two different versions of the same ClusterBus will always have different RVs. - return - } - - reconciler.workqueue.AddRateLimited(makeWorkqueueKeyForClusterBus(newClusterBus)) - }, - }) - } - // Set up an event handler for when Channel resources change - channelInformer.Informer().AddEventHandler(informercache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - channel := obj.(*channelsv1alpha1.Channel) - reconciler.workqueue.AddRateLimited(makeWorkqueueKeyForChannel(channel)) - }, - UpdateFunc: func(old, new interface{}) { - oldChannel := old.(*channelsv1alpha1.Channel) - newChannel := new.(*channelsv1alpha1.Channel) - - if oldChannel.ResourceVersion == newChannel.ResourceVersion { - // Periodic resync will send update events for all known Channels. - // Two different versions of the same Channel will always have different RVs. - return - } - - reconciler.workqueue.AddRateLimited(makeWorkqueueKeyForChannel(newChannel)) - }, - DeleteFunc: func(obj interface{}) { - channel := obj.(*channelsv1alpha1.Channel) - reconciler.workqueue.AddRateLimited(makeWorkqueueKeyForChannel(channel)) - }, - }) - // Set up an event handler for when Subscription resources change - subscriptionInformer.Informer().AddEventHandler(informercache.ResourceEventHandlerFuncs{ - AddFunc: func(obj interface{}) { - subscription := obj.(*channelsv1alpha1.Subscription) - reconciler.workqueue.AddRateLimited(makeWorkqueueKeyForSubscription(subscription)) - }, - UpdateFunc: func(old, new interface{}) { - oldSubscription := old.(*channelsv1alpha1.Subscription) - newSubscription := new.(*channelsv1alpha1.Subscription) - - if oldSubscription.ResourceVersion == newSubscription.ResourceVersion { - // Periodic resync will send update events for all known Subscriptions. - // Two different versions of the same Subscription will always have different RVs. - return - } - - reconciler.workqueue.AddRateLimited(makeWorkqueueKeyForSubscription(newSubscription)) - }, - DeleteFunc: func(obj interface{}) { - subscription := obj.(*channelsv1alpha1.Subscription) - reconciler.workqueue.AddRateLimited(makeWorkqueueKeyForSubscription(subscription)) - }, - }) - - return reconciler -} - -// RequeueSubscription will add the Subscription to the workqueue for future -// processing. Reprocessing a Subscription is often used within a dispatcher -// when a long lived receiver is interrupted by an asynchronous error. -func (r *Reconciler) RequeueSubscription(subscription SubscriptionReference) { - r.logger.Infof("Requeue subscription %q", subscription.String()) - r.workqueue.AddRateLimited(makeWorkqueueKey(subscriptionKind, subscription.Namespace, subscription.Name)) -} - -// RecordBusEventf creates a new event for the reconciled bus and records it -// with the api server. -func (r *Reconciler) RecordBusEventf(eventtype, reason, messageFmt string, args ...interface{}) { - r.recorder.Eventf(r.bus, eventtype, reason, messageFmt, args...) -} - -// RecordChannelEventf creates a new event for the channel and records it with -// the api server. Attempts to records an event for an unknown channel are -// ignored. -func (r *Reconciler) RecordChannelEventf(ref ChannelReference, eventtype, reason, messageFmt string, args ...interface{}) { - channel, err := r.cache.Channel(ref) - if err != nil { - // TODO handle error - return - } - r.recorder.Eventf(channel, eventtype, reason, messageFmt, args...) -} - -// RecordSubscriptionEventf creates a new event for the subscription and -// records it with the api server. Attempts to records an event for an unknown -// subscription are ignored. -func (r *Reconciler) RecordSubscriptionEventf(ref SubscriptionReference, eventtype, reason, messageFmt string, args ...interface{}) { - subscription, err := r.cache.Subscription(ref) - if err != nil { - // TODO handle error - return - } - r.recorder.Eventf(subscription, eventtype, reason, messageFmt, args...) -} - -// Run will set up the event handlers for types we are interested in, as well -// as syncing informer caches and starting workers. It will block until stopCh -// is closed, at which point it will shutdown the workqueue and wait for -// workers to finish processing their current work items. -func (r *Reconciler) Run(threadiness int, stopCh <-chan struct{}) error { - defer runtime.HandleCrash() - defer r.workqueue.ShutDown() - - // Start the informer factories to begin populating the informer caches - r.logger.Info("Starting reconciler") - go r.eventingInformerFactory.Start(stopCh) - - // Wait for the caches to be synced before starting workers - r.logger.Info("Waiting for informer caches to sync") - if err := r.WaitForCacheSync(stopCh); err != nil { - return err - } - - if r.ref.IsNamespaced() { - // reconciler is for a namespaced Bus - bus, err := r.busesLister.Buses(r.ref.Namespace).Get(r.ref.Name) - if err != nil { - r.logger.Fatalf("Unknown bus %q: %v", r.ref.String(), err) - } - r.bus = bus.DeepCopy() - } else { - // reconciler is for a ClusterBus - clusterBus, err := r.clusterBusesLister.Get(r.ref.Name) - if err != nil { - r.logger.Fatalf("Unknown clusterbus %q: %v", r.ref.String(), err) - } - r.bus = clusterBus.DeepCopy() - } - - r.logger.Info("Starting workers") - // Launch workers to process resources - for i := 0; i < threadiness; i++ { - go wait.Until(r.runWorker, time.Second, stopCh) - } - - r.logger.Info("Started workers") - <-stopCh - r.logger.Info("Shutting down workers") - - return nil -} - -// WaitForCacheSync blocks returning until the reconciler's informers have -// synchronized. It returns an error if the caches cannot sync. -func (r *Reconciler) WaitForCacheSync(stopCh <-chan struct{}) error { - var busesSynced informercache.InformerSynced - // get correct synced reference for bus type - if r.ref.IsNamespaced() { - busesSynced = r.busesSynced - } else { - busesSynced = r.clusterBusesSynced - } - if ok := informercache.WaitForCacheSync(stopCh, busesSynced, r.channelsSynced, r.subscriptionsSynced); !ok { - return fmt.Errorf("failed to wait for caches to sync") - } - return nil -} - -// runWorker is a long-running function that will continually call the -// processNextWorkItem function in order to read and process a message on the -// workqueue. -func (r *Reconciler) runWorker() { - for r.processNextWorkItem() { - } -} - -// processNextWorkItem will read a single work item off the workqueue and -// attempt to process it, by calling the syncHandler. -func (r *Reconciler) processNextWorkItem() bool { - obj, shutdown := r.workqueue.Get() - - if shutdown { - return false - } - - // We wrap this block in a func so we can defer r.workqueue.Done. - err := func(obj interface{}) error { - // We call Done here so the workqueue knows we have finished - // processing this item. We also must remember to call Forget if we - // do not want this work item being re-queued. For example, we do - // not call Forget if a transient error occurs, instead the item is - // put back on the workqueue and attempted again after a back-off - // period. - defer r.workqueue.Done(obj) - var key string - var ok bool - // We expect strings to come off the workqueue. These are of the - // form kind/namespace/name. We do this as the delayed nature of the - // workqueue means the items in the informer cache may actually be - // more up to date that when the item was initially put onto the - // workqueue. - if key, ok = obj.(string); !ok { - // As the item in the workqueue is actually invalid, we call - // Forget here else we'd go into a loop of attempting to - // process a work item that is invalid. - r.workqueue.Forget(obj) - runtime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) - return nil - } - // Run the syncHandler, passing it the kind/namespace/name key of the - // resource to be synced. - if err := r.syncHandler(key); err != nil { - r.workqueue.AddRateLimited(obj) - return fmt.Errorf("error syncing reconciler '%s': %v", key, err) - } - // Finally, if no error occurs we Forget this item so it does not - // get queued again until another change happens. - r.workqueue.Forget(obj) - r.logger.Infof("Successfully synced reconciler '%s'", key) - return nil - }(obj) - - if err != nil { - runtime.HandleError(err) - return true - } - - return true -} - -// syncHandler compares the actual state with the desired, and attempts to -// converge the two. It then updates the Status block of the resource with the -// current status. -func (r *Reconciler) syncHandler(key string) error { - // Convert the kind/namespace/name string into a distinct kind, namespace and name - kind, namespace, name, err := splitWorkqueueKey(key) - if err != nil { - runtime.HandleError(fmt.Errorf("invalid resource key: %s", key)) - return nil - } - - switch kind { - case busKind: - err = r.syncBus(namespace, name) - case clusterBusKind: - err = r.syncClusterBus(name) - case channelKind: - err = r.syncChannel(namespace, name) - case subscriptionKind: - err = r.syncSubscription(namespace, name) - default: - runtime.HandleError(fmt.Errorf("Unknown resource kind %s", kind)) - return nil - } - - if err != nil { - return err - } - - return nil -} - -func (r *Reconciler) syncBus(namespace string, name string) error { - // Get the Bus resource with this namespace/name - bus, err := r.busesLister.Buses(namespace).Get(name) - if err != nil { - // The Bus resource may no longer exist - if errors.IsNotFound(err) { - // nothing to do - return nil - } - - return err - } - - // Sync the Bus - err = r.createOrUpdateBus(bus.DeepCopy()) - if err != nil { - return err - } - - return nil -} - -func (r *Reconciler) syncClusterBus(name string) error { - // Get the ClusterBus resource with this name - clusterBus, err := r.clusterBusesLister.Get(name) - if err != nil { - // The ClusterBus resource may no longer exist - if errors.IsNotFound(err) { - // nothing to do - return nil - } - - return err - } - - // Sync the ClusterBus - err = r.createOrUpdateBus(clusterBus.DeepCopy()) - if err != nil { - return err - } - - return nil -} - -func (r *Reconciler) syncChannel(namespace string, name string) error { - // Get the Channel resource with this namespace/name - channel, err := r.channelsLister.Channels(namespace).Get(name) - if err != nil { - // The Channel resource may no longer exist - if errors.IsNotFound(err) { - ref := NewChannelReferenceFromNames(name, namespace) - err = r.removeChannel(ref) - if err != nil { - return err - } - return nil - } - - return err - } - - // Sync the Channel - err = r.createOrUpdateChannel(channel) - if err != nil { - return err - } - - return nil -} - -func (r *Reconciler) syncSubscription(namespace string, name string) error { - // Get the Subscription resource with this namespace/name - subscription, err := r.subscriptionsLister.Subscriptions(namespace).Get(name) - if err != nil { - // The Subscription resource may no longer exist - if errors.IsNotFound(err) { - ref := NewSubscriptionReferenceFromNames(name, namespace) - err = r.removeSubscription(ref) - if err != nil { - return err - } - return nil - } - - return err - } - - // Sync the Subscription - err = r.createOrUpdateSubscription(subscription) - if err != nil { - return err - } - - return nil -} - -func (r *Reconciler) createOrUpdateBus(bus channelsv1alpha1.GenericBus) error { - if r.ref != NewBusReference(bus) { - // not the bus for this reconciler - return nil - } - - // stash the new bus on the reconciler while retaining the old bus. This - // operation is threadsafe because there is only a single Bus/ClusterBus - // that is valid for the Reconciler and the workqueue guarantees that it - // will not emit the same key concurrently. Any bus received is an updated - // revision of the current bus. - bus, r.bus = r.bus, bus - - if !equality.Semantic.DeepEqual(r.bus.GetSpec(), bus.GetSpec()) { - err := r.handler.onBus(r.bus, r) - if err != nil { - return err - } - - oldParams := bus.GetSpec().Parameters - newParams := r.bus.GetSpec().Parameters - // If channel parameters changed we need to reprovision - if !equality.Semantic.DeepEqual(oldParams.Channel, newParams.Channel) { - r.logger.Infof("Bus channel parameters changed. Reprovisioning channels.") - for _, channel := range r.cache.AllChannels() { - r.workqueue.AddRateLimited(makeWorkqueueKeyForChannel(channel)) - } - } - - // If subscription parameters changed we need to resubscribe - if !equality.Semantic.DeepEqual(oldParams.Subscription, newParams.Subscription) { - r.logger.Infof("Bus subscription parameters changed. Resubscribing.") - for _, subscription := range r.cache.AllSubscriptions() { - r.workqueue.AddRateLimited(makeWorkqueueKeyForSubscription(subscription)) - } - } - } - - return nil -} - -func (r *Reconciler) createOrUpdateChannel(channel *channelsv1alpha1.Channel) error { - if !r.bus.BacksChannel(channel) { - return nil - } - - r.cache.AddChannel(channel) - err := r.handler.onProvision(channel, r) - if err != nil { - return err - } - - return nil -} - -func (r *Reconciler) removeChannel(ref ChannelReference) error { - channel, err := r.cache.Channel(ref) - if err != nil { - // the channel isn't provisioned - return nil - } - - err = r.handler.onUnprovision(channel, r) - if err != nil { - return err - } - r.cache.RemoveChannel(channel) - - return nil -} - -func (r *Reconciler) createOrUpdateSubscription(subscription *channelsv1alpha1.Subscription) error { - ref := NewChannelReferenceFromSubscription(subscription) - _, err := r.cache.Channel(ref) - if err != nil { - // channel is not provisioned, before erring we need to check if the channel is provionable - channel, errS := r.channelsLister.Channels(ref.Namespace).Get(ref.Name) - if errS != nil { - return err - } - if !r.bus.BacksChannel(channel) { - return nil - } - return err - } - - r.cache.AddSubscription(subscription) - err = r.handler.onSubscribe(subscription, r) - if err != nil { - return err - } - - return nil -} - -func (r *Reconciler) removeSubscription(ref SubscriptionReference) error { - subscription, err := r.cache.Subscription(ref) - if err != nil { - return nil - } - - err = r.handler.onUnsubscribe(subscription, r) - if err != nil { - return err - } - r.cache.RemoveSubscription(subscription) - - return nil -} - -func makeWorkqueueKeyForBus(bus *channelsv1alpha1.Bus) string { - return makeWorkqueueKey(busKind, bus.Namespace, bus.Name) -} - -func makeWorkqueueKeyForClusterBus(clusterBus *channelsv1alpha1.ClusterBus) string { - return makeWorkqueueKey(clusterBusKind, "", clusterBus.Name) -} - -func makeWorkqueueKeyForChannel(channel *channelsv1alpha1.Channel) string { - return makeWorkqueueKey(channelKind, channel.Namespace, channel.Name) -} - -func makeWorkqueueKeyForSubscription(subscription *channelsv1alpha1.Subscription) string { - return makeWorkqueueKey(subscriptionKind, subscription.Namespace, subscription.Name) -} - -func makeWorkqueueKey(kind string, namespace string, name string) string { - return fmt.Sprintf("%s/%s/%s", kind, namespace, name) -} - -func splitWorkqueueKey(key string) (string, string, string, error) { - chunks := strings.Split(key, "/") - if len(chunks) != 3 { - return "", "", "", fmt.Errorf("Unknown workqueue key %v", key) - } - kind := chunks[0] - namespace := chunks[1] - name := chunks[2] - return kind, namespace, name, nil -} diff --git a/pkg/buses/references.go b/pkg/buses/references.go index 1a0b58608ca..7455022fee5 100644 --- a/pkg/buses/references.go +++ b/pkg/buses/references.go @@ -19,45 +19,9 @@ package buses import ( "fmt" - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" ) -// BusReference references a Bus or ClusterBus within the cluster by name and -// namespace. For ClusterBus the namespace will be an empty string. -type BusReference struct { - Namespace string - Name string -} - -// NewBusReference creates a BusReference for a bus. -func NewBusReference(bus channelsv1alpha1.GenericBus) BusReference { - meta := bus.GetObjectMeta() - return BusReference{ - Namespace: meta.GetNamespace(), - Name: meta.GetName(), - } -} - -// NewBusReferenceFromNames creates a BusReference for a name and namespace. -func NewBusReferenceFromNames(name, namespace string) BusReference { - return BusReference{ - Namespace: namespace, - Name: name, - } -} - -// IsNamespaced returns true is the reference is for a Bus and not a ClusterBus -func (r *BusReference) IsNamespaced() bool { - return r.Namespace != "" -} - -func (r *BusReference) String() string { - if r.IsNamespaced() { - return fmt.Sprintf("%s/%s", r.Namespace, r.Name) - } - return r.Name -} - // ChannelReference references a Channel within the cluster by name and // namespace. type ChannelReference struct { @@ -66,14 +30,14 @@ type ChannelReference struct { } // NewChannelReference creates a ChannelReference from a Channel -func NewChannelReference(channel *channelsv1alpha1.Channel) ChannelReference { +func NewChannelReference(channel *eventingv1alpha1.Channel) ChannelReference { return NewChannelReferenceFromNames(channel.Name, channel.Namespace) } // NewChannelReferenceFromSubscription creates a ChannelReference from a // Subscription for a Channel. -func NewChannelReferenceFromSubscription(subscription *channelsv1alpha1.Subscription) ChannelReference { - return NewChannelReferenceFromNames(subscription.Spec.Channel, subscription.Namespace) +func NewChannelReferenceFromSubscription(subscription *eventingv1alpha1.Subscription) ChannelReference { + return NewChannelReferenceFromNames(subscription.Spec.Channel.Name, subscription.Namespace) } // NewChannelReferenceFromNames creates a ChannelReference for a name and @@ -88,28 +52,3 @@ func NewChannelReferenceFromNames(name, namespace string) ChannelReference { func (r *ChannelReference) String() string { return fmt.Sprintf("%s/%s", r.Namespace, r.Name) } - -// SubscriptionReference references a Subscription within the cluster by name -// and namespace. -type SubscriptionReference struct { - Namespace string - Name string -} - -// NewSubscriptionReference creates a SubscriptionReference from a Subscription -func NewSubscriptionReference(subscription *channelsv1alpha1.Subscription) SubscriptionReference { - return NewSubscriptionReferenceFromNames(subscription.Name, subscription.Namespace) -} - -// NewSubscriptionReferenceFromNames creates a SubscriptionReference for a name and -// namespace. -func NewSubscriptionReferenceFromNames(name, namespace string) SubscriptionReference { - return SubscriptionReference{ - Namespace: namespace, - Name: name, - } -} - -func (r *SubscriptionReference) String() string { - return fmt.Sprintf("%s/%s", r.Namespace, r.Name) -} diff --git a/pkg/buses/references_test.go b/pkg/buses/references_test.go index febff414ec1..ef75a5070fc 100644 --- a/pkg/buses/references_test.go +++ b/pkg/buses/references_test.go @@ -20,120 +20,20 @@ import ( "fmt" "testing" - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "github.com/knative/eventing/pkg/buses" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) const ( referencesTestNamespace = "test-namespace" - referencesTestBusName = "test-bus" - referencesTestClusterBusName = "test-clusterbus" referencesTestChannelName = "test-channel" referencesTestSubscriptionName = "test-subscription" ) -func TestNewBusReference(t *testing.T) { - bus := &channelsv1alpha1.Bus{ - ObjectMeta: metav1.ObjectMeta{ - Name: referencesTestBusName, - Namespace: referencesTestNamespace, - }, - } - expected := buses.BusReference{ - Name: referencesTestBusName, - Namespace: referencesTestNamespace, - } - actual := buses.NewBusReference(bus) - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "BusReference", expected, actual) - } -} - -func TestNewBusReference_ClusterBus(t *testing.T) { - clusterBus := &channelsv1alpha1.ClusterBus{ - ObjectMeta: metav1.ObjectMeta{ - Name: referencesTestClusterBusName, - }, - } - expected := buses.BusReference{ - Name: referencesTestClusterBusName, - } - actual := buses.NewBusReference(clusterBus) - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "BusReference", expected, actual) - } -} - -func TestNewBusReferenceFromNames(t *testing.T) { - expected := buses.BusReference{ - Name: referencesTestBusName, - Namespace: referencesTestNamespace, - } - actual := buses.NewBusReferenceFromNames(referencesTestBusName, referencesTestNamespace) - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "BusReference", expected, actual) - } -} - -func TestNewBusReferenceFromNames_ClusterBus(t *testing.T) { - expected := buses.BusReference{ - Name: referencesTestClusterBusName, - } - actual := buses.NewBusReferenceFromNames(referencesTestClusterBusName, "") - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "BusReference", expected, actual) - } -} - -func TestBusReference_IsNamespaced(t *testing.T) { - busRef := buses.BusReference{ - Name: referencesTestBusName, - Namespace: referencesTestNamespace, - } - expected := true - actual := busRef.IsNamespaced() - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "IsNamespaced", expected, actual) - } -} - -func TestBusReference_IsNamespaced_ClusterBus(t *testing.T) { - busRef := buses.BusReference{ - Name: referencesTestClusterBusName, - } - expected := false - actual := busRef.IsNamespaced() - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "IsNamespaced", expected, actual) - } -} - -func TestBusReference_String(t *testing.T) { - ref := buses.BusReference{ - Name: referencesTestBusName, - Namespace: referencesTestNamespace, - } - expected := fmt.Sprintf("%s/%s", referencesTestNamespace, referencesTestBusName) - actual := ref.String() - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "BusReference", expected, actual) - } -} - -func TestBusReference_String_ClusterBus(t *testing.T) { - ref := buses.BusReference{ - Name: referencesTestClusterBusName, - } - expected := referencesTestClusterBusName - actual := ref.String() - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "BusReference", expected, actual) - } -} - func TestNewChannelReference(t *testing.T) { - channel := &channelsv1alpha1.Channel{ + channel := &eventingv1alpha1.Channel{ ObjectMeta: metav1.ObjectMeta{ Name: referencesTestChannelName, Namespace: referencesTestNamespace, @@ -150,13 +50,15 @@ func TestNewChannelReference(t *testing.T) { } func TestNewChannelReferenceFromSubscription(t *testing.T) { - subscription := &channelsv1alpha1.Subscription{ + subscription := &eventingv1alpha1.Subscription{ ObjectMeta: metav1.ObjectMeta{ Name: referencesTestSubscriptionName, Namespace: referencesTestNamespace, }, - Spec: channelsv1alpha1.SubscriptionSpec{ - Channel: referencesTestChannelName, + Spec: eventingv1alpha1.SubscriptionSpec{ + Channel: corev1.ObjectReference{ + Name: referencesTestChannelName, + }, }, } expected := buses.ChannelReference{ @@ -191,43 +93,3 @@ func TestChannelReference_String(t *testing.T) { t.Errorf("%s expected: %+v got: %+v", "ChannelReference", expected, actual) } } - -func TestNewSubscriptionReference(t *testing.T) { - subscription := &channelsv1alpha1.Subscription{ - ObjectMeta: metav1.ObjectMeta{ - Name: referencesTestSubscriptionName, - Namespace: referencesTestNamespace, - }, - } - expected := buses.SubscriptionReference{ - Name: referencesTestSubscriptionName, - Namespace: referencesTestNamespace, - } - actual := buses.NewSubscriptionReference(subscription) - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "SubscriptionReference", expected, actual) - } -} - -func TestNewSubscriptionReferenceFromNames(t *testing.T) { - expected := buses.SubscriptionReference{ - Name: referencesTestSubscriptionName, - Namespace: referencesTestNamespace, - } - actual := buses.NewSubscriptionReferenceFromNames(referencesTestSubscriptionName, referencesTestNamespace) - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "SubscriptionReference", expected, actual) - } -} - -func TestSubscriptionReference_String(t *testing.T) { - ref := buses.SubscriptionReference{ - Name: referencesTestSubscriptionName, - Namespace: referencesTestNamespace, - } - expected := fmt.Sprintf("%s/%s", referencesTestNamespace, referencesTestSubscriptionName) - actual := ref.String() - if expected != actual { - t.Errorf("%s expected: %+v got: %+v", "SubscriptionReference", expected, actual) - } -} diff --git a/pkg/buses/stub/bus.go b/pkg/buses/stub/bus.go deleted file mode 100644 index 0376364b17e..00000000000 --- a/pkg/buses/stub/bus.go +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package stub - -import ( - "go.uber.org/zap" - - "github.com/knative/eventing/pkg/buses" -) - -// BusType is the type of the stub bus -const BusType = "stub" - -func NewStubBusDispatcher(ref buses.BusReference, opts *buses.BusOpts) *StubBus { - bus := &StubBus{ - channels: make(map[buses.ChannelReference]*stubChannel), - } - handlerFuncs := buses.EventHandlerFuncs{ - ProvisionFunc: func(channel buses.ChannelReference, parameters buses.ResolvedParameters) error { - bus.logger.Infof("Provision channel %q", channel.String()) - bus.addChannel(channel, parameters) - return nil - }, - UnprovisionFunc: func(channel buses.ChannelReference) error { - bus.logger.Infof("Unprovision channel %q", channel.String()) - bus.removeChannel(channel) - return nil - }, - SubscribeFunc: func(channel buses.ChannelReference, subscription buses.SubscriptionReference, parameters buses.ResolvedParameters) error { - bus.logger.Infof("Subscribe %q to %q channel", subscription.String(), channel.String()) - bus.channel(channel).addSubscription(subscription, parameters, bus.dispatcher) - return nil - }, - UnsubscribeFunc: func(channel buses.ChannelReference, subscription buses.SubscriptionReference) error { - bus.logger.Infof("Unsubscribe %q from %q channel", subscription.String(), channel.String()) - bus.channel(channel).removeSubscription(subscription) - return nil - }, - ReceiveMessageFunc: func(channel buses.ChannelReference, message *buses.Message) error { - bus.logger.Infof("Recieved message for %q channel", channel.String()) - bus.channel(channel).receiveMessage(message) - return nil - }, - } - bus.dispatcher = buses.NewBusDispatcher(ref, handlerFuncs, opts) - bus.logger = opts.Logger - - return bus -} - -type StubBus struct { - dispatcher buses.BusDispatcher - channels map[buses.ChannelReference]*stubChannel - - logger *zap.SugaredLogger -} - -func (b *StubBus) Run(threadness int, stopCh <-chan struct{}) { - b.dispatcher.Run(threadness, stopCh) -} - -func (b *StubBus) addChannel(ref buses.ChannelReference, parameters buses.ResolvedParameters) { - if channel, ok := b.channels[ref]; ok { - // update channel - channel.parameters = parameters - } else { - // create channel - b.channels[ref] = &stubChannel{ - ref: ref, - parameters: parameters, - subscriptions: make(map[buses.SubscriptionReference]*stubSubscription), - logger: b.logger.With(zap.String("channels.knative.dev/channel", ref.String())), - } - } -} - -func (b *StubBus) removeChannel(channel buses.ChannelReference) { - delete(b.channels, channel) -} - -func (b *StubBus) channel(channel buses.ChannelReference) *stubChannel { - return b.channels[channel] -} - -type stubChannel struct { - ref buses.ChannelReference - parameters buses.ResolvedParameters - subscriptions map[buses.SubscriptionReference]*stubSubscription - - logger *zap.SugaredLogger -} - -func (c *stubChannel) receiveMessage(message *buses.Message) { - for _, stubSubscription := range c.subscriptions { - go stubSubscription.dispatchMessage(message) - } -} - -func (c *stubChannel) addSubscription(ref buses.SubscriptionReference, parameters buses.ResolvedParameters, bus buses.BusDispatcher) { - if subscription, ok := c.subscriptions[ref]; ok { - // update subscription - subscription.parameters = parameters - } else { - // create subscription - c.subscriptions[ref] = &stubSubscription{ - ref: ref, - bus: bus, - parameters: parameters, - - logger: c.logger.With(zap.String("channels.knative.dev/subscription", ref.String())), - } - } -} - -func (c *stubChannel) removeSubscription(subscription buses.SubscriptionReference) { - delete(c.subscriptions, subscription) -} - -type stubSubscription struct { - ref buses.SubscriptionReference - bus buses.BusDispatcher - parameters buses.ResolvedParameters - - logger *zap.SugaredLogger -} - -func (s *stubSubscription) dispatchMessage(message *buses.Message) error { - err := s.bus.DispatchMessage(s.ref, message) - if err != nil { - s.logger.Warnf("Failed to dispatch message: %v", err) - } - return err -} diff --git a/pkg/buses/stub/bus_test.go b/pkg/buses/stub/bus_test.go deleted file mode 100644 index 2a75accdd4c..00000000000 --- a/pkg/buses/stub/bus_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package stub - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/buses/stub/dispatcher/kodata/LICENSE b/pkg/buses/stub/dispatcher/kodata/LICENSE deleted file mode 120000 index 2a64f9d0fc6..00000000000 --- a/pkg/buses/stub/dispatcher/kodata/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../LICENSE \ No newline at end of file diff --git a/pkg/buses/stub/dispatcher/kodata/VENDOR-LICENSE b/pkg/buses/stub/dispatcher/kodata/VENDOR-LICENSE deleted file mode 120000 index 681789ed22f..00000000000 --- a/pkg/buses/stub/dispatcher/kodata/VENDOR-LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/pkg/buses/stub/dispatcher/main.go b/pkg/buses/stub/dispatcher/main.go deleted file mode 100644 index 4064c2c5e8e..00000000000 --- a/pkg/buses/stub/dispatcher/main.go +++ /dev/null @@ -1,61 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package main - -import ( - "flag" - "os" - - "github.com/knative/eventing/pkg/buses" - "github.com/knative/eventing/pkg/buses/stub" - "github.com/knative/pkg/signals" - "go.uber.org/zap" -) - -const ( - threadsPerReconciler = 1 -) - -func main() { - ref := buses.NewBusReferenceFromNames( - os.Getenv("BUS_NAME"), - os.Getenv("BUS_NAMESPACE"), - ) - - config := buses.NewLoggingConfig() - logger := buses.NewBusLoggerFromConfig(config) - defer logger.Sync() - logger = logger.With( - zap.String("channels.knative.dev/bus", ref.String()), - zap.String("channels.knative.dev/busType", stub.BusType), - zap.String("channels.knative.dev/busComponent", buses.Dispatcher), - ) - - opts := &buses.BusOpts{ - Logger: logger, - } - - flag.StringVar(&opts.KubeConfig, "kubeconfig", "", "Path to a kubeconfig. Only required if out-of-cluster.") - flag.StringVar(&opts.MasterURL, "master", "", "The address of the Kubernetes API server. Overrides any value in kubeconfig. Only required if out-of-cluster.") - flag.Parse() - - bus := stub.NewStubBusDispatcher(ref, opts) - - // set up signals so we handle the first shutdown signal gracefully - stopCh := signals.SetupSignalHandler() - bus.Run(threadsPerReconciler, stopCh) -} diff --git a/pkg/client/clientset/versioned/clientset.go b/pkg/client/clientset/versioned/clientset.go index c9df07b37da..4f8f6c7c4c3 100644 --- a/pkg/client/clientset/versioned/clientset.go +++ b/pkg/client/clientset/versioned/clientset.go @@ -19,10 +19,7 @@ limitations under the License. package versioned import ( - channelsv1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/channels/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/eventing/v1alpha1" - feedsv1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/feeds/v1alpha1" - flowsv1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/flows/v1alpha1" discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" flowcontrol "k8s.io/client-go/util/flowcontrol" @@ -30,39 +27,16 @@ import ( type Interface interface { Discovery() discovery.DiscoveryInterface - ChannelsV1alpha1() channelsv1alpha1.ChannelsV1alpha1Interface - // Deprecated: please explicitly pick a version if possible. - Channels() channelsv1alpha1.ChannelsV1alpha1Interface EventingV1alpha1() eventingv1alpha1.EventingV1alpha1Interface // Deprecated: please explicitly pick a version if possible. Eventing() eventingv1alpha1.EventingV1alpha1Interface - FeedsV1alpha1() feedsv1alpha1.FeedsV1alpha1Interface - // Deprecated: please explicitly pick a version if possible. - Feeds() feedsv1alpha1.FeedsV1alpha1Interface - FlowsV1alpha1() flowsv1alpha1.FlowsV1alpha1Interface - // Deprecated: please explicitly pick a version if possible. - Flows() flowsv1alpha1.FlowsV1alpha1Interface } // Clientset contains the clients for groups. Each group has exactly one // version included in a Clientset. type Clientset struct { *discovery.DiscoveryClient - channelsV1alpha1 *channelsv1alpha1.ChannelsV1alpha1Client eventingV1alpha1 *eventingv1alpha1.EventingV1alpha1Client - feedsV1alpha1 *feedsv1alpha1.FeedsV1alpha1Client - flowsV1alpha1 *flowsv1alpha1.FlowsV1alpha1Client -} - -// ChannelsV1alpha1 retrieves the ChannelsV1alpha1Client -func (c *Clientset) ChannelsV1alpha1() channelsv1alpha1.ChannelsV1alpha1Interface { - return c.channelsV1alpha1 -} - -// Deprecated: Channels retrieves the default version of ChannelsClient. -// Please explicitly pick a version. -func (c *Clientset) Channels() channelsv1alpha1.ChannelsV1alpha1Interface { - return c.channelsV1alpha1 } // EventingV1alpha1 retrieves the EventingV1alpha1Client @@ -76,28 +50,6 @@ func (c *Clientset) Eventing() eventingv1alpha1.EventingV1alpha1Interface { return c.eventingV1alpha1 } -// FeedsV1alpha1 retrieves the FeedsV1alpha1Client -func (c *Clientset) FeedsV1alpha1() feedsv1alpha1.FeedsV1alpha1Interface { - return c.feedsV1alpha1 -} - -// Deprecated: Feeds retrieves the default version of FeedsClient. -// Please explicitly pick a version. -func (c *Clientset) Feeds() feedsv1alpha1.FeedsV1alpha1Interface { - return c.feedsV1alpha1 -} - -// FlowsV1alpha1 retrieves the FlowsV1alpha1Client -func (c *Clientset) FlowsV1alpha1() flowsv1alpha1.FlowsV1alpha1Interface { - return c.flowsV1alpha1 -} - -// Deprecated: Flows retrieves the default version of FlowsClient. -// Please explicitly pick a version. -func (c *Clientset) Flows() flowsv1alpha1.FlowsV1alpha1Interface { - return c.flowsV1alpha1 -} - // Discovery retrieves the DiscoveryClient func (c *Clientset) Discovery() discovery.DiscoveryInterface { if c == nil { @@ -114,22 +66,10 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { } var cs Clientset var err error - cs.channelsV1alpha1, err = channelsv1alpha1.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } cs.eventingV1alpha1, err = eventingv1alpha1.NewForConfig(&configShallowCopy) if err != nil { return nil, err } - cs.feedsV1alpha1, err = feedsv1alpha1.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } - cs.flowsV1alpha1, err = flowsv1alpha1.NewForConfig(&configShallowCopy) - if err != nil { - return nil, err - } cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) if err != nil { @@ -142,10 +82,7 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { // panics if there is an error in the config. func NewForConfigOrDie(c *rest.Config) *Clientset { var cs Clientset - cs.channelsV1alpha1 = channelsv1alpha1.NewForConfigOrDie(c) cs.eventingV1alpha1 = eventingv1alpha1.NewForConfigOrDie(c) - cs.feedsV1alpha1 = feedsv1alpha1.NewForConfigOrDie(c) - cs.flowsV1alpha1 = flowsv1alpha1.NewForConfigOrDie(c) cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) return &cs @@ -154,10 +91,7 @@ func NewForConfigOrDie(c *rest.Config) *Clientset { // New creates a new Clientset for the given RESTClient. func New(c rest.Interface) *Clientset { var cs Clientset - cs.channelsV1alpha1 = channelsv1alpha1.New(c) cs.eventingV1alpha1 = eventingv1alpha1.New(c) - cs.feedsV1alpha1 = feedsv1alpha1.New(c) - cs.flowsV1alpha1 = flowsv1alpha1.New(c) cs.DiscoveryClient = discovery.NewDiscoveryClient(c) return &cs diff --git a/pkg/client/clientset/versioned/fake/clientset_generated.go b/pkg/client/clientset/versioned/fake/clientset_generated.go index 1a6301babe4..0a4b10bfa14 100644 --- a/pkg/client/clientset/versioned/fake/clientset_generated.go +++ b/pkg/client/clientset/versioned/fake/clientset_generated.go @@ -20,14 +20,8 @@ package fake import ( clientset "github.com/knative/eventing/pkg/client/clientset/versioned" - channelsv1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/channels/v1alpha1" - fakechannelsv1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake" eventingv1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/eventing/v1alpha1" fakeeventingv1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/eventing/v1alpha1/fake" - feedsv1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/feeds/v1alpha1" - fakefeedsv1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake" - flowsv1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/flows/v1alpha1" - fakeflowsv1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/flows/v1alpha1/fake" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/watch" "k8s.io/client-go/discovery" @@ -77,16 +71,6 @@ func (c *Clientset) Discovery() discovery.DiscoveryInterface { var _ clientset.Interface = &Clientset{} -// ChannelsV1alpha1 retrieves the ChannelsV1alpha1Client -func (c *Clientset) ChannelsV1alpha1() channelsv1alpha1.ChannelsV1alpha1Interface { - return &fakechannelsv1alpha1.FakeChannelsV1alpha1{Fake: &c.Fake} -} - -// Channels retrieves the ChannelsV1alpha1Client -func (c *Clientset) Channels() channelsv1alpha1.ChannelsV1alpha1Interface { - return &fakechannelsv1alpha1.FakeChannelsV1alpha1{Fake: &c.Fake} -} - // EventingV1alpha1 retrieves the EventingV1alpha1Client func (c *Clientset) EventingV1alpha1() eventingv1alpha1.EventingV1alpha1Interface { return &fakeeventingv1alpha1.FakeEventingV1alpha1{Fake: &c.Fake} @@ -96,23 +80,3 @@ func (c *Clientset) EventingV1alpha1() eventingv1alpha1.EventingV1alpha1Interfac func (c *Clientset) Eventing() eventingv1alpha1.EventingV1alpha1Interface { return &fakeeventingv1alpha1.FakeEventingV1alpha1{Fake: &c.Fake} } - -// FeedsV1alpha1 retrieves the FeedsV1alpha1Client -func (c *Clientset) FeedsV1alpha1() feedsv1alpha1.FeedsV1alpha1Interface { - return &fakefeedsv1alpha1.FakeFeedsV1alpha1{Fake: &c.Fake} -} - -// Feeds retrieves the FeedsV1alpha1Client -func (c *Clientset) Feeds() feedsv1alpha1.FeedsV1alpha1Interface { - return &fakefeedsv1alpha1.FakeFeedsV1alpha1{Fake: &c.Fake} -} - -// FlowsV1alpha1 retrieves the FlowsV1alpha1Client -func (c *Clientset) FlowsV1alpha1() flowsv1alpha1.FlowsV1alpha1Interface { - return &fakeflowsv1alpha1.FakeFlowsV1alpha1{Fake: &c.Fake} -} - -// Flows retrieves the FlowsV1alpha1Client -func (c *Clientset) Flows() flowsv1alpha1.FlowsV1alpha1Interface { - return &fakeflowsv1alpha1.FakeFlowsV1alpha1{Fake: &c.Fake} -} diff --git a/pkg/client/clientset/versioned/fake/register.go b/pkg/client/clientset/versioned/fake/register.go index 9c7ef66dd10..8e722c05baa 100644 --- a/pkg/client/clientset/versioned/fake/register.go +++ b/pkg/client/clientset/versioned/fake/register.go @@ -19,10 +19,7 @@ limitations under the License. package fake import ( - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - flowsv1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" @@ -53,8 +50,5 @@ func init() { // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. func AddToScheme(scheme *runtime.Scheme) { - channelsv1alpha1.AddToScheme(scheme) eventingv1alpha1.AddToScheme(scheme) - feedsv1alpha1.AddToScheme(scheme) - flowsv1alpha1.AddToScheme(scheme) } diff --git a/pkg/client/clientset/versioned/scheme/register.go b/pkg/client/clientset/versioned/scheme/register.go index b3afb7f218d..48c902c17c3 100644 --- a/pkg/client/clientset/versioned/scheme/register.go +++ b/pkg/client/clientset/versioned/scheme/register.go @@ -19,10 +19,7 @@ limitations under the License. package scheme import ( - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - flowsv1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" schema "k8s.io/apimachinery/pkg/runtime/schema" @@ -53,8 +50,5 @@ func init() { // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. func AddToScheme(scheme *runtime.Scheme) { - channelsv1alpha1.AddToScheme(scheme) eventingv1alpha1.AddToScheme(scheme) - feedsv1alpha1.AddToScheme(scheme) - flowsv1alpha1.AddToScheme(scheme) } diff --git a/pkg/client/clientset/versioned/typed/channels/v1alpha1/bus.go b/pkg/client/clientset/versioned/typed/channels/v1alpha1/bus.go deleted file mode 100644 index 12450331d50..00000000000 --- a/pkg/client/clientset/versioned/typed/channels/v1alpha1/bus.go +++ /dev/null @@ -1,157 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// BusesGetter has a method to return a BusInterface. -// A group's client should implement this interface. -type BusesGetter interface { - Buses(namespace string) BusInterface -} - -// BusInterface has methods to work with Bus resources. -type BusInterface interface { - Create(*v1alpha1.Bus) (*v1alpha1.Bus, error) - Update(*v1alpha1.Bus) (*v1alpha1.Bus, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.Bus, error) - List(opts v1.ListOptions) (*v1alpha1.BusList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Bus, err error) - BusExpansion -} - -// buses implements BusInterface -type buses struct { - client rest.Interface - ns string -} - -// newBuses returns a Buses -func newBuses(c *ChannelsV1alpha1Client, namespace string) *buses { - return &buses{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the bus, and returns the corresponding bus object, and an error if there is any. -func (c *buses) Get(name string, options v1.GetOptions) (result *v1alpha1.Bus, err error) { - result = &v1alpha1.Bus{} - err = c.client.Get(). - Namespace(c.ns). - Resource("buses"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Buses that match those selectors. -func (c *buses) List(opts v1.ListOptions) (result *v1alpha1.BusList, err error) { - result = &v1alpha1.BusList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("buses"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested buses. -func (c *buses) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("buses"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a bus and creates it. Returns the server's representation of the bus, and an error, if there is any. -func (c *buses) Create(bus *v1alpha1.Bus) (result *v1alpha1.Bus, err error) { - result = &v1alpha1.Bus{} - err = c.client.Post(). - Namespace(c.ns). - Resource("buses"). - Body(bus). - Do(). - Into(result) - return -} - -// Update takes the representation of a bus and updates it. Returns the server's representation of the bus, and an error, if there is any. -func (c *buses) Update(bus *v1alpha1.Bus) (result *v1alpha1.Bus, err error) { - result = &v1alpha1.Bus{} - err = c.client.Put(). - Namespace(c.ns). - Resource("buses"). - Name(bus.Name). - Body(bus). - Do(). - Into(result) - return -} - -// Delete takes name of the bus and deletes it. Returns an error if one occurs. -func (c *buses) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("buses"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *buses) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("buses"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched bus. -func (c *buses) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Bus, err error) { - result = &v1alpha1.Bus{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("buses"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/channels/v1alpha1/channel.go b/pkg/client/clientset/versioned/typed/channels/v1alpha1/channel.go deleted file mode 100644 index f9a51328615..00000000000 --- a/pkg/client/clientset/versioned/typed/channels/v1alpha1/channel.go +++ /dev/null @@ -1,157 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// ChannelsGetter has a method to return a ChannelInterface. -// A group's client should implement this interface. -type ChannelsGetter interface { - Channels(namespace string) ChannelInterface -} - -// ChannelInterface has methods to work with Channel resources. -type ChannelInterface interface { - Create(*v1alpha1.Channel) (*v1alpha1.Channel, error) - Update(*v1alpha1.Channel) (*v1alpha1.Channel, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.Channel, error) - List(opts v1.ListOptions) (*v1alpha1.ChannelList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Channel, err error) - ChannelExpansion -} - -// channels implements ChannelInterface -type channels struct { - client rest.Interface - ns string -} - -// newChannels returns a Channels -func newChannels(c *ChannelsV1alpha1Client, namespace string) *channels { - return &channels{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the channel, and returns the corresponding channel object, and an error if there is any. -func (c *channels) Get(name string, options v1.GetOptions) (result *v1alpha1.Channel, err error) { - result = &v1alpha1.Channel{} - err = c.client.Get(). - Namespace(c.ns). - Resource("channels"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Channels that match those selectors. -func (c *channels) List(opts v1.ListOptions) (result *v1alpha1.ChannelList, err error) { - result = &v1alpha1.ChannelList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("channels"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested channels. -func (c *channels) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("channels"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a channel and creates it. Returns the server's representation of the channel, and an error, if there is any. -func (c *channels) Create(channel *v1alpha1.Channel) (result *v1alpha1.Channel, err error) { - result = &v1alpha1.Channel{} - err = c.client.Post(). - Namespace(c.ns). - Resource("channels"). - Body(channel). - Do(). - Into(result) - return -} - -// Update takes the representation of a channel and updates it. Returns the server's representation of the channel, and an error, if there is any. -func (c *channels) Update(channel *v1alpha1.Channel) (result *v1alpha1.Channel, err error) { - result = &v1alpha1.Channel{} - err = c.client.Put(). - Namespace(c.ns). - Resource("channels"). - Name(channel.Name). - Body(channel). - Do(). - Into(result) - return -} - -// Delete takes name of the channel and deletes it. Returns an error if one occurs. -func (c *channels) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("channels"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *channels) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("channels"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched channel. -func (c *channels) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Channel, err error) { - result = &v1alpha1.Channel{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("channels"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/channels/v1alpha1/channels_client.go b/pkg/client/clientset/versioned/typed/channels/v1alpha1/channels_client.go deleted file mode 100644 index d99d1390cef..00000000000 --- a/pkg/client/clientset/versioned/typed/channels/v1alpha1/channels_client.go +++ /dev/null @@ -1,105 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - rest "k8s.io/client-go/rest" -) - -type ChannelsV1alpha1Interface interface { - RESTClient() rest.Interface - BusesGetter - ChannelsGetter - ClusterBusesGetter - SubscriptionsGetter -} - -// ChannelsV1alpha1Client is used to interact with features provided by the channels.knative.dev group. -type ChannelsV1alpha1Client struct { - restClient rest.Interface -} - -func (c *ChannelsV1alpha1Client) Buses(namespace string) BusInterface { - return newBuses(c, namespace) -} - -func (c *ChannelsV1alpha1Client) Channels(namespace string) ChannelInterface { - return newChannels(c, namespace) -} - -func (c *ChannelsV1alpha1Client) ClusterBuses() ClusterBusInterface { - return newClusterBuses(c) -} - -func (c *ChannelsV1alpha1Client) Subscriptions(namespace string) SubscriptionInterface { - return newSubscriptions(c, namespace) -} - -// NewForConfig creates a new ChannelsV1alpha1Client for the given config. -func NewForConfig(c *rest.Config) (*ChannelsV1alpha1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &ChannelsV1alpha1Client{client}, nil -} - -// NewForConfigOrDie creates a new ChannelsV1alpha1Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *ChannelsV1alpha1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new ChannelsV1alpha1Client for the given RESTClient. -func New(c rest.Interface) *ChannelsV1alpha1Client { - return &ChannelsV1alpha1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1alpha1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *ChannelsV1alpha1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/pkg/client/clientset/versioned/typed/channels/v1alpha1/clusterbus.go b/pkg/client/clientset/versioned/typed/channels/v1alpha1/clusterbus.go deleted file mode 100644 index 707d2e04e4e..00000000000 --- a/pkg/client/clientset/versioned/typed/channels/v1alpha1/clusterbus.go +++ /dev/null @@ -1,147 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// ClusterBusesGetter has a method to return a ClusterBusInterface. -// A group's client should implement this interface. -type ClusterBusesGetter interface { - ClusterBuses() ClusterBusInterface -} - -// ClusterBusInterface has methods to work with ClusterBus resources. -type ClusterBusInterface interface { - Create(*v1alpha1.ClusterBus) (*v1alpha1.ClusterBus, error) - Update(*v1alpha1.ClusterBus) (*v1alpha1.ClusterBus, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.ClusterBus, error) - List(opts v1.ListOptions) (*v1alpha1.ClusterBusList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterBus, err error) - ClusterBusExpansion -} - -// clusterBuses implements ClusterBusInterface -type clusterBuses struct { - client rest.Interface -} - -// newClusterBuses returns a ClusterBuses -func newClusterBuses(c *ChannelsV1alpha1Client) *clusterBuses { - return &clusterBuses{ - client: c.RESTClient(), - } -} - -// Get takes name of the clusterBus, and returns the corresponding clusterBus object, and an error if there is any. -func (c *clusterBuses) Get(name string, options v1.GetOptions) (result *v1alpha1.ClusterBus, err error) { - result = &v1alpha1.ClusterBus{} - err = c.client.Get(). - Resource("clusterbuses"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of ClusterBuses that match those selectors. -func (c *clusterBuses) List(opts v1.ListOptions) (result *v1alpha1.ClusterBusList, err error) { - result = &v1alpha1.ClusterBusList{} - err = c.client.Get(). - Resource("clusterbuses"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested clusterBuses. -func (c *clusterBuses) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Resource("clusterbuses"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a clusterBus and creates it. Returns the server's representation of the clusterBus, and an error, if there is any. -func (c *clusterBuses) Create(clusterBus *v1alpha1.ClusterBus) (result *v1alpha1.ClusterBus, err error) { - result = &v1alpha1.ClusterBus{} - err = c.client.Post(). - Resource("clusterbuses"). - Body(clusterBus). - Do(). - Into(result) - return -} - -// Update takes the representation of a clusterBus and updates it. Returns the server's representation of the clusterBus, and an error, if there is any. -func (c *clusterBuses) Update(clusterBus *v1alpha1.ClusterBus) (result *v1alpha1.ClusterBus, err error) { - result = &v1alpha1.ClusterBus{} - err = c.client.Put(). - Resource("clusterbuses"). - Name(clusterBus.Name). - Body(clusterBus). - Do(). - Into(result) - return -} - -// Delete takes name of the clusterBus and deletes it. Returns an error if one occurs. -func (c *clusterBuses) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("clusterbuses"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *clusterBuses) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Resource("clusterbuses"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched clusterBus. -func (c *clusterBuses) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterBus, err error) { - result = &v1alpha1.ClusterBus{} - err = c.client.Patch(pt). - Resource("clusterbuses"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/channels/v1alpha1/doc.go b/pkg/client/clientset/versioned/typed/channels/v1alpha1/doc.go deleted file mode 100644 index 75445c17900..00000000000 --- a/pkg/client/clientset/versioned/typed/channels/v1alpha1/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1alpha1 diff --git a/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/doc.go b/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/doc.go deleted file mode 100644 index 128aa183a91..00000000000 --- a/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_bus.go b/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_bus.go deleted file mode 100644 index 3c68d02db36..00000000000 --- a/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_bus.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeBuses implements BusInterface -type FakeBuses struct { - Fake *FakeChannelsV1alpha1 - ns string -} - -var busesResource = schema.GroupVersionResource{Group: "channels.knative.dev", Version: "v1alpha1", Resource: "buses"} - -var busesKind = schema.GroupVersionKind{Group: "channels.knative.dev", Version: "v1alpha1", Kind: "Bus"} - -// Get takes name of the bus, and returns the corresponding bus object, and an error if there is any. -func (c *FakeBuses) Get(name string, options v1.GetOptions) (result *v1alpha1.Bus, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(busesResource, c.ns, name), &v1alpha1.Bus{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Bus), err -} - -// List takes label and field selectors, and returns the list of Buses that match those selectors. -func (c *FakeBuses) List(opts v1.ListOptions) (result *v1alpha1.BusList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(busesResource, busesKind, c.ns, opts), &v1alpha1.BusList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.BusList{ListMeta: obj.(*v1alpha1.BusList).ListMeta} - for _, item := range obj.(*v1alpha1.BusList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested buses. -func (c *FakeBuses) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(busesResource, c.ns, opts)) - -} - -// Create takes the representation of a bus and creates it. Returns the server's representation of the bus, and an error, if there is any. -func (c *FakeBuses) Create(bus *v1alpha1.Bus) (result *v1alpha1.Bus, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(busesResource, c.ns, bus), &v1alpha1.Bus{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Bus), err -} - -// Update takes the representation of a bus and updates it. Returns the server's representation of the bus, and an error, if there is any. -func (c *FakeBuses) Update(bus *v1alpha1.Bus) (result *v1alpha1.Bus, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(busesResource, c.ns, bus), &v1alpha1.Bus{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Bus), err -} - -// Delete takes name of the bus and deletes it. Returns an error if one occurs. -func (c *FakeBuses) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(busesResource, c.ns, name), &v1alpha1.Bus{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeBuses) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(busesResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.BusList{}) - return err -} - -// Patch applies the patch and returns the patched bus. -func (c *FakeBuses) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Bus, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(busesResource, c.ns, name, data, subresources...), &v1alpha1.Bus{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Bus), err -} diff --git a/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_channel.go b/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_channel.go deleted file mode 100644 index cf32da5a015..00000000000 --- a/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_channel.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeChannels implements ChannelInterface -type FakeChannels struct { - Fake *FakeChannelsV1alpha1 - ns string -} - -var channelsResource = schema.GroupVersionResource{Group: "channels.knative.dev", Version: "v1alpha1", Resource: "channels"} - -var channelsKind = schema.GroupVersionKind{Group: "channels.knative.dev", Version: "v1alpha1", Kind: "Channel"} - -// Get takes name of the channel, and returns the corresponding channel object, and an error if there is any. -func (c *FakeChannels) Get(name string, options v1.GetOptions) (result *v1alpha1.Channel, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(channelsResource, c.ns, name), &v1alpha1.Channel{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Channel), err -} - -// List takes label and field selectors, and returns the list of Channels that match those selectors. -func (c *FakeChannels) List(opts v1.ListOptions) (result *v1alpha1.ChannelList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(channelsResource, channelsKind, c.ns, opts), &v1alpha1.ChannelList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.ChannelList{ListMeta: obj.(*v1alpha1.ChannelList).ListMeta} - for _, item := range obj.(*v1alpha1.ChannelList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested channels. -func (c *FakeChannels) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(channelsResource, c.ns, opts)) - -} - -// Create takes the representation of a channel and creates it. Returns the server's representation of the channel, and an error, if there is any. -func (c *FakeChannels) Create(channel *v1alpha1.Channel) (result *v1alpha1.Channel, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(channelsResource, c.ns, channel), &v1alpha1.Channel{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Channel), err -} - -// Update takes the representation of a channel and updates it. Returns the server's representation of the channel, and an error, if there is any. -func (c *FakeChannels) Update(channel *v1alpha1.Channel) (result *v1alpha1.Channel, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(channelsResource, c.ns, channel), &v1alpha1.Channel{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Channel), err -} - -// Delete takes name of the channel and deletes it. Returns an error if one occurs. -func (c *FakeChannels) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(channelsResource, c.ns, name), &v1alpha1.Channel{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeChannels) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(channelsResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.ChannelList{}) - return err -} - -// Patch applies the patch and returns the patched channel. -func (c *FakeChannels) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Channel, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(channelsResource, c.ns, name, data, subresources...), &v1alpha1.Channel{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Channel), err -} diff --git a/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_channels_client.go b/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_channels_client.go deleted file mode 100644 index 3c498bd6dcb..00000000000 --- a/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_channels_client.go +++ /dev/null @@ -1,52 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/channels/v1alpha1" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeChannelsV1alpha1 struct { - *testing.Fake -} - -func (c *FakeChannelsV1alpha1) Buses(namespace string) v1alpha1.BusInterface { - return &FakeBuses{c, namespace} -} - -func (c *FakeChannelsV1alpha1) Channels(namespace string) v1alpha1.ChannelInterface { - return &FakeChannels{c, namespace} -} - -func (c *FakeChannelsV1alpha1) ClusterBuses() v1alpha1.ClusterBusInterface { - return &FakeClusterBuses{c} -} - -func (c *FakeChannelsV1alpha1) Subscriptions(namespace string) v1alpha1.SubscriptionInterface { - return &FakeSubscriptions{c, namespace} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeChannelsV1alpha1) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_clusterbus.go b/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_clusterbus.go deleted file mode 100644 index 82f4f9dc6d4..00000000000 --- a/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_clusterbus.go +++ /dev/null @@ -1,120 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeClusterBuses implements ClusterBusInterface -type FakeClusterBuses struct { - Fake *FakeChannelsV1alpha1 -} - -var clusterbusesResource = schema.GroupVersionResource{Group: "channels.knative.dev", Version: "v1alpha1", Resource: "clusterbuses"} - -var clusterbusesKind = schema.GroupVersionKind{Group: "channels.knative.dev", Version: "v1alpha1", Kind: "ClusterBus"} - -// Get takes name of the clusterBus, and returns the corresponding clusterBus object, and an error if there is any. -func (c *FakeClusterBuses) Get(name string, options v1.GetOptions) (result *v1alpha1.ClusterBus, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(clusterbusesResource, name), &v1alpha1.ClusterBus{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterBus), err -} - -// List takes label and field selectors, and returns the list of ClusterBuses that match those selectors. -func (c *FakeClusterBuses) List(opts v1.ListOptions) (result *v1alpha1.ClusterBusList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(clusterbusesResource, clusterbusesKind, opts), &v1alpha1.ClusterBusList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.ClusterBusList{ListMeta: obj.(*v1alpha1.ClusterBusList).ListMeta} - for _, item := range obj.(*v1alpha1.ClusterBusList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested clusterBuses. -func (c *FakeClusterBuses) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(clusterbusesResource, opts)) -} - -// Create takes the representation of a clusterBus and creates it. Returns the server's representation of the clusterBus, and an error, if there is any. -func (c *FakeClusterBuses) Create(clusterBus *v1alpha1.ClusterBus) (result *v1alpha1.ClusterBus, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(clusterbusesResource, clusterBus), &v1alpha1.ClusterBus{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterBus), err -} - -// Update takes the representation of a clusterBus and updates it. Returns the server's representation of the clusterBus, and an error, if there is any. -func (c *FakeClusterBuses) Update(clusterBus *v1alpha1.ClusterBus) (result *v1alpha1.ClusterBus, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(clusterbusesResource, clusterBus), &v1alpha1.ClusterBus{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterBus), err -} - -// Delete takes name of the clusterBus and deletes it. Returns an error if one occurs. -func (c *FakeClusterBuses) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(clusterbusesResource, name), &v1alpha1.ClusterBus{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeClusterBuses) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(clusterbusesResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.ClusterBusList{}) - return err -} - -// Patch applies the patch and returns the patched clusterBus. -func (c *FakeClusterBuses) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterBus, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(clusterbusesResource, name, data, subresources...), &v1alpha1.ClusterBus{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterBus), err -} diff --git a/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_subscription.go b/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_subscription.go deleted file mode 100644 index b3fdebf5308..00000000000 --- a/pkg/client/clientset/versioned/typed/channels/v1alpha1/fake/fake_subscription.go +++ /dev/null @@ -1,128 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeSubscriptions implements SubscriptionInterface -type FakeSubscriptions struct { - Fake *FakeChannelsV1alpha1 - ns string -} - -var subscriptionsResource = schema.GroupVersionResource{Group: "channels.knative.dev", Version: "v1alpha1", Resource: "subscriptions"} - -var subscriptionsKind = schema.GroupVersionKind{Group: "channels.knative.dev", Version: "v1alpha1", Kind: "Subscription"} - -// Get takes name of the subscription, and returns the corresponding subscription object, and an error if there is any. -func (c *FakeSubscriptions) Get(name string, options v1.GetOptions) (result *v1alpha1.Subscription, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(subscriptionsResource, c.ns, name), &v1alpha1.Subscription{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Subscription), err -} - -// List takes label and field selectors, and returns the list of Subscriptions that match those selectors. -func (c *FakeSubscriptions) List(opts v1.ListOptions) (result *v1alpha1.SubscriptionList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(subscriptionsResource, subscriptionsKind, c.ns, opts), &v1alpha1.SubscriptionList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.SubscriptionList{ListMeta: obj.(*v1alpha1.SubscriptionList).ListMeta} - for _, item := range obj.(*v1alpha1.SubscriptionList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested subscriptions. -func (c *FakeSubscriptions) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(subscriptionsResource, c.ns, opts)) - -} - -// Create takes the representation of a subscription and creates it. Returns the server's representation of the subscription, and an error, if there is any. -func (c *FakeSubscriptions) Create(subscription *v1alpha1.Subscription) (result *v1alpha1.Subscription, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(subscriptionsResource, c.ns, subscription), &v1alpha1.Subscription{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Subscription), err -} - -// Update takes the representation of a subscription and updates it. Returns the server's representation of the subscription, and an error, if there is any. -func (c *FakeSubscriptions) Update(subscription *v1alpha1.Subscription) (result *v1alpha1.Subscription, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(subscriptionsResource, c.ns, subscription), &v1alpha1.Subscription{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Subscription), err -} - -// Delete takes name of the subscription and deletes it. Returns an error if one occurs. -func (c *FakeSubscriptions) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(subscriptionsResource, c.ns, name), &v1alpha1.Subscription{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeSubscriptions) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(subscriptionsResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.SubscriptionList{}) - return err -} - -// Patch applies the patch and returns the patched subscription. -func (c *FakeSubscriptions) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Subscription, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(subscriptionsResource, c.ns, name, data, subresources...), &v1alpha1.Subscription{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Subscription), err -} diff --git a/pkg/client/clientset/versioned/typed/channels/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/channels/v1alpha1/generated_expansion.go deleted file mode 100644 index 8979577193d..00000000000 --- a/pkg/client/clientset/versioned/typed/channels/v1alpha1/generated_expansion.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -type BusExpansion interface{} - -type ChannelExpansion interface{} - -type ClusterBusExpansion interface{} - -type SubscriptionExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/channels/v1alpha1/subscription.go b/pkg/client/clientset/versioned/typed/channels/v1alpha1/subscription.go deleted file mode 100644 index c91104a08ea..00000000000 --- a/pkg/client/clientset/versioned/typed/channels/v1alpha1/subscription.go +++ /dev/null @@ -1,157 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// SubscriptionsGetter has a method to return a SubscriptionInterface. -// A group's client should implement this interface. -type SubscriptionsGetter interface { - Subscriptions(namespace string) SubscriptionInterface -} - -// SubscriptionInterface has methods to work with Subscription resources. -type SubscriptionInterface interface { - Create(*v1alpha1.Subscription) (*v1alpha1.Subscription, error) - Update(*v1alpha1.Subscription) (*v1alpha1.Subscription, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.Subscription, error) - List(opts v1.ListOptions) (*v1alpha1.SubscriptionList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Subscription, err error) - SubscriptionExpansion -} - -// subscriptions implements SubscriptionInterface -type subscriptions struct { - client rest.Interface - ns string -} - -// newSubscriptions returns a Subscriptions -func newSubscriptions(c *ChannelsV1alpha1Client, namespace string) *subscriptions { - return &subscriptions{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the subscription, and returns the corresponding subscription object, and an error if there is any. -func (c *subscriptions) Get(name string, options v1.GetOptions) (result *v1alpha1.Subscription, err error) { - result = &v1alpha1.Subscription{} - err = c.client.Get(). - Namespace(c.ns). - Resource("subscriptions"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Subscriptions that match those selectors. -func (c *subscriptions) List(opts v1.ListOptions) (result *v1alpha1.SubscriptionList, err error) { - result = &v1alpha1.SubscriptionList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("subscriptions"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested subscriptions. -func (c *subscriptions) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("subscriptions"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a subscription and creates it. Returns the server's representation of the subscription, and an error, if there is any. -func (c *subscriptions) Create(subscription *v1alpha1.Subscription) (result *v1alpha1.Subscription, err error) { - result = &v1alpha1.Subscription{} - err = c.client.Post(). - Namespace(c.ns). - Resource("subscriptions"). - Body(subscription). - Do(). - Into(result) - return -} - -// Update takes the representation of a subscription and updates it. Returns the server's representation of the subscription, and an error, if there is any. -func (c *subscriptions) Update(subscription *v1alpha1.Subscription) (result *v1alpha1.Subscription, err error) { - result = &v1alpha1.Subscription{} - err = c.client.Put(). - Namespace(c.ns). - Resource("subscriptions"). - Name(subscription.Name). - Body(subscription). - Do(). - Into(result) - return -} - -// Delete takes name of the subscription and deletes it. Returns an error if one occurs. -func (c *subscriptions) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("subscriptions"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *subscriptions) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("subscriptions"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched subscription. -func (c *subscriptions) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Subscription, err error) { - result = &v1alpha1.Subscription{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("subscriptions"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/clustereventsource.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/clustereventsource.go deleted file mode 100644 index 45328f1dd72..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/clustereventsource.go +++ /dev/null @@ -1,163 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// ClusterEventSourcesGetter has a method to return a ClusterEventSourceInterface. -// A group's client should implement this interface. -type ClusterEventSourcesGetter interface { - ClusterEventSources() ClusterEventSourceInterface -} - -// ClusterEventSourceInterface has methods to work with ClusterEventSource resources. -type ClusterEventSourceInterface interface { - Create(*v1alpha1.ClusterEventSource) (*v1alpha1.ClusterEventSource, error) - Update(*v1alpha1.ClusterEventSource) (*v1alpha1.ClusterEventSource, error) - UpdateStatus(*v1alpha1.ClusterEventSource) (*v1alpha1.ClusterEventSource, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.ClusterEventSource, error) - List(opts v1.ListOptions) (*v1alpha1.ClusterEventSourceList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterEventSource, err error) - ClusterEventSourceExpansion -} - -// clusterEventSources implements ClusterEventSourceInterface -type clusterEventSources struct { - client rest.Interface -} - -// newClusterEventSources returns a ClusterEventSources -func newClusterEventSources(c *FeedsV1alpha1Client) *clusterEventSources { - return &clusterEventSources{ - client: c.RESTClient(), - } -} - -// Get takes name of the clusterEventSource, and returns the corresponding clusterEventSource object, and an error if there is any. -func (c *clusterEventSources) Get(name string, options v1.GetOptions) (result *v1alpha1.ClusterEventSource, err error) { - result = &v1alpha1.ClusterEventSource{} - err = c.client.Get(). - Resource("clustereventsources"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of ClusterEventSources that match those selectors. -func (c *clusterEventSources) List(opts v1.ListOptions) (result *v1alpha1.ClusterEventSourceList, err error) { - result = &v1alpha1.ClusterEventSourceList{} - err = c.client.Get(). - Resource("clustereventsources"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested clusterEventSources. -func (c *clusterEventSources) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Resource("clustereventsources"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a clusterEventSource and creates it. Returns the server's representation of the clusterEventSource, and an error, if there is any. -func (c *clusterEventSources) Create(clusterEventSource *v1alpha1.ClusterEventSource) (result *v1alpha1.ClusterEventSource, err error) { - result = &v1alpha1.ClusterEventSource{} - err = c.client.Post(). - Resource("clustereventsources"). - Body(clusterEventSource). - Do(). - Into(result) - return -} - -// Update takes the representation of a clusterEventSource and updates it. Returns the server's representation of the clusterEventSource, and an error, if there is any. -func (c *clusterEventSources) Update(clusterEventSource *v1alpha1.ClusterEventSource) (result *v1alpha1.ClusterEventSource, err error) { - result = &v1alpha1.ClusterEventSource{} - err = c.client.Put(). - Resource("clustereventsources"). - Name(clusterEventSource.Name). - Body(clusterEventSource). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *clusterEventSources) UpdateStatus(clusterEventSource *v1alpha1.ClusterEventSource) (result *v1alpha1.ClusterEventSource, err error) { - result = &v1alpha1.ClusterEventSource{} - err = c.client.Put(). - Resource("clustereventsources"). - Name(clusterEventSource.Name). - SubResource("status"). - Body(clusterEventSource). - Do(). - Into(result) - return -} - -// Delete takes name of the clusterEventSource and deletes it. Returns an error if one occurs. -func (c *clusterEventSources) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("clustereventsources"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *clusterEventSources) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Resource("clustereventsources"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched clusterEventSource. -func (c *clusterEventSources) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterEventSource, err error) { - result = &v1alpha1.ClusterEventSource{} - err = c.client.Patch(pt). - Resource("clustereventsources"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/clustereventtype.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/clustereventtype.go deleted file mode 100644 index 6b2d24d295d..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/clustereventtype.go +++ /dev/null @@ -1,163 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// ClusterEventTypesGetter has a method to return a ClusterEventTypeInterface. -// A group's client should implement this interface. -type ClusterEventTypesGetter interface { - ClusterEventTypes() ClusterEventTypeInterface -} - -// ClusterEventTypeInterface has methods to work with ClusterEventType resources. -type ClusterEventTypeInterface interface { - Create(*v1alpha1.ClusterEventType) (*v1alpha1.ClusterEventType, error) - Update(*v1alpha1.ClusterEventType) (*v1alpha1.ClusterEventType, error) - UpdateStatus(*v1alpha1.ClusterEventType) (*v1alpha1.ClusterEventType, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.ClusterEventType, error) - List(opts v1.ListOptions) (*v1alpha1.ClusterEventTypeList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterEventType, err error) - ClusterEventTypeExpansion -} - -// clusterEventTypes implements ClusterEventTypeInterface -type clusterEventTypes struct { - client rest.Interface -} - -// newClusterEventTypes returns a ClusterEventTypes -func newClusterEventTypes(c *FeedsV1alpha1Client) *clusterEventTypes { - return &clusterEventTypes{ - client: c.RESTClient(), - } -} - -// Get takes name of the clusterEventType, and returns the corresponding clusterEventType object, and an error if there is any. -func (c *clusterEventTypes) Get(name string, options v1.GetOptions) (result *v1alpha1.ClusterEventType, err error) { - result = &v1alpha1.ClusterEventType{} - err = c.client.Get(). - Resource("clustereventtypes"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of ClusterEventTypes that match those selectors. -func (c *clusterEventTypes) List(opts v1.ListOptions) (result *v1alpha1.ClusterEventTypeList, err error) { - result = &v1alpha1.ClusterEventTypeList{} - err = c.client.Get(). - Resource("clustereventtypes"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested clusterEventTypes. -func (c *clusterEventTypes) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Resource("clustereventtypes"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a clusterEventType and creates it. Returns the server's representation of the clusterEventType, and an error, if there is any. -func (c *clusterEventTypes) Create(clusterEventType *v1alpha1.ClusterEventType) (result *v1alpha1.ClusterEventType, err error) { - result = &v1alpha1.ClusterEventType{} - err = c.client.Post(). - Resource("clustereventtypes"). - Body(clusterEventType). - Do(). - Into(result) - return -} - -// Update takes the representation of a clusterEventType and updates it. Returns the server's representation of the clusterEventType, and an error, if there is any. -func (c *clusterEventTypes) Update(clusterEventType *v1alpha1.ClusterEventType) (result *v1alpha1.ClusterEventType, err error) { - result = &v1alpha1.ClusterEventType{} - err = c.client.Put(). - Resource("clustereventtypes"). - Name(clusterEventType.Name). - Body(clusterEventType). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *clusterEventTypes) UpdateStatus(clusterEventType *v1alpha1.ClusterEventType) (result *v1alpha1.ClusterEventType, err error) { - result = &v1alpha1.ClusterEventType{} - err = c.client.Put(). - Resource("clustereventtypes"). - Name(clusterEventType.Name). - SubResource("status"). - Body(clusterEventType). - Do(). - Into(result) - return -} - -// Delete takes name of the clusterEventType and deletes it. Returns an error if one occurs. -func (c *clusterEventTypes) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Resource("clustereventtypes"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *clusterEventTypes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Resource("clustereventtypes"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched clusterEventType. -func (c *clusterEventTypes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterEventType, err error) { - result = &v1alpha1.ClusterEventType{} - err = c.client.Patch(pt). - Resource("clustereventtypes"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/doc.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/doc.go deleted file mode 100644 index 75445c17900..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1alpha1 diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/eventsource.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/eventsource.go deleted file mode 100644 index 6d28a8ae4e8..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/eventsource.go +++ /dev/null @@ -1,174 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// EventSourcesGetter has a method to return a EventSourceInterface. -// A group's client should implement this interface. -type EventSourcesGetter interface { - EventSources(namespace string) EventSourceInterface -} - -// EventSourceInterface has methods to work with EventSource resources. -type EventSourceInterface interface { - Create(*v1alpha1.EventSource) (*v1alpha1.EventSource, error) - Update(*v1alpha1.EventSource) (*v1alpha1.EventSource, error) - UpdateStatus(*v1alpha1.EventSource) (*v1alpha1.EventSource, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.EventSource, error) - List(opts v1.ListOptions) (*v1alpha1.EventSourceList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.EventSource, err error) - EventSourceExpansion -} - -// eventSources implements EventSourceInterface -type eventSources struct { - client rest.Interface - ns string -} - -// newEventSources returns a EventSources -func newEventSources(c *FeedsV1alpha1Client, namespace string) *eventSources { - return &eventSources{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the eventSource, and returns the corresponding eventSource object, and an error if there is any. -func (c *eventSources) Get(name string, options v1.GetOptions) (result *v1alpha1.EventSource, err error) { - result = &v1alpha1.EventSource{} - err = c.client.Get(). - Namespace(c.ns). - Resource("eventsources"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of EventSources that match those selectors. -func (c *eventSources) List(opts v1.ListOptions) (result *v1alpha1.EventSourceList, err error) { - result = &v1alpha1.EventSourceList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("eventsources"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested eventSources. -func (c *eventSources) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("eventsources"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a eventSource and creates it. Returns the server's representation of the eventSource, and an error, if there is any. -func (c *eventSources) Create(eventSource *v1alpha1.EventSource) (result *v1alpha1.EventSource, err error) { - result = &v1alpha1.EventSource{} - err = c.client.Post(). - Namespace(c.ns). - Resource("eventsources"). - Body(eventSource). - Do(). - Into(result) - return -} - -// Update takes the representation of a eventSource and updates it. Returns the server's representation of the eventSource, and an error, if there is any. -func (c *eventSources) Update(eventSource *v1alpha1.EventSource) (result *v1alpha1.EventSource, err error) { - result = &v1alpha1.EventSource{} - err = c.client.Put(). - Namespace(c.ns). - Resource("eventsources"). - Name(eventSource.Name). - Body(eventSource). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *eventSources) UpdateStatus(eventSource *v1alpha1.EventSource) (result *v1alpha1.EventSource, err error) { - result = &v1alpha1.EventSource{} - err = c.client.Put(). - Namespace(c.ns). - Resource("eventsources"). - Name(eventSource.Name). - SubResource("status"). - Body(eventSource). - Do(). - Into(result) - return -} - -// Delete takes name of the eventSource and deletes it. Returns an error if one occurs. -func (c *eventSources) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("eventsources"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *eventSources) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("eventsources"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched eventSource. -func (c *eventSources) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.EventSource, err error) { - result = &v1alpha1.EventSource{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("eventsources"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/eventtype.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/eventtype.go deleted file mode 100644 index 2f0b0a9998d..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/eventtype.go +++ /dev/null @@ -1,174 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// EventTypesGetter has a method to return a EventTypeInterface. -// A group's client should implement this interface. -type EventTypesGetter interface { - EventTypes(namespace string) EventTypeInterface -} - -// EventTypeInterface has methods to work with EventType resources. -type EventTypeInterface interface { - Create(*v1alpha1.EventType) (*v1alpha1.EventType, error) - Update(*v1alpha1.EventType) (*v1alpha1.EventType, error) - UpdateStatus(*v1alpha1.EventType) (*v1alpha1.EventType, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.EventType, error) - List(opts v1.ListOptions) (*v1alpha1.EventTypeList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.EventType, err error) - EventTypeExpansion -} - -// eventTypes implements EventTypeInterface -type eventTypes struct { - client rest.Interface - ns string -} - -// newEventTypes returns a EventTypes -func newEventTypes(c *FeedsV1alpha1Client, namespace string) *eventTypes { - return &eventTypes{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the eventType, and returns the corresponding eventType object, and an error if there is any. -func (c *eventTypes) Get(name string, options v1.GetOptions) (result *v1alpha1.EventType, err error) { - result = &v1alpha1.EventType{} - err = c.client.Get(). - Namespace(c.ns). - Resource("eventtypes"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of EventTypes that match those selectors. -func (c *eventTypes) List(opts v1.ListOptions) (result *v1alpha1.EventTypeList, err error) { - result = &v1alpha1.EventTypeList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("eventtypes"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested eventTypes. -func (c *eventTypes) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("eventtypes"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a eventType and creates it. Returns the server's representation of the eventType, and an error, if there is any. -func (c *eventTypes) Create(eventType *v1alpha1.EventType) (result *v1alpha1.EventType, err error) { - result = &v1alpha1.EventType{} - err = c.client.Post(). - Namespace(c.ns). - Resource("eventtypes"). - Body(eventType). - Do(). - Into(result) - return -} - -// Update takes the representation of a eventType and updates it. Returns the server's representation of the eventType, and an error, if there is any. -func (c *eventTypes) Update(eventType *v1alpha1.EventType) (result *v1alpha1.EventType, err error) { - result = &v1alpha1.EventType{} - err = c.client.Put(). - Namespace(c.ns). - Resource("eventtypes"). - Name(eventType.Name). - Body(eventType). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *eventTypes) UpdateStatus(eventType *v1alpha1.EventType) (result *v1alpha1.EventType, err error) { - result = &v1alpha1.EventType{} - err = c.client.Put(). - Namespace(c.ns). - Resource("eventtypes"). - Name(eventType.Name). - SubResource("status"). - Body(eventType). - Do(). - Into(result) - return -} - -// Delete takes name of the eventType and deletes it. Returns an error if one occurs. -func (c *eventTypes) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("eventtypes"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *eventTypes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("eventtypes"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched eventType. -func (c *eventTypes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.EventType, err error) { - result = &v1alpha1.EventType{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("eventtypes"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/doc.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/doc.go deleted file mode 100644 index 128aa183a91..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_clustereventsource.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_clustereventsource.go deleted file mode 100644 index 52f724b39ab..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_clustereventsource.go +++ /dev/null @@ -1,131 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeClusterEventSources implements ClusterEventSourceInterface -type FakeClusterEventSources struct { - Fake *FakeFeedsV1alpha1 -} - -var clustereventsourcesResource = schema.GroupVersionResource{Group: "feeds.knative.dev", Version: "v1alpha1", Resource: "clustereventsources"} - -var clustereventsourcesKind = schema.GroupVersionKind{Group: "feeds.knative.dev", Version: "v1alpha1", Kind: "ClusterEventSource"} - -// Get takes name of the clusterEventSource, and returns the corresponding clusterEventSource object, and an error if there is any. -func (c *FakeClusterEventSources) Get(name string, options v1.GetOptions) (result *v1alpha1.ClusterEventSource, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(clustereventsourcesResource, name), &v1alpha1.ClusterEventSource{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterEventSource), err -} - -// List takes label and field selectors, and returns the list of ClusterEventSources that match those selectors. -func (c *FakeClusterEventSources) List(opts v1.ListOptions) (result *v1alpha1.ClusterEventSourceList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(clustereventsourcesResource, clustereventsourcesKind, opts), &v1alpha1.ClusterEventSourceList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.ClusterEventSourceList{ListMeta: obj.(*v1alpha1.ClusterEventSourceList).ListMeta} - for _, item := range obj.(*v1alpha1.ClusterEventSourceList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested clusterEventSources. -func (c *FakeClusterEventSources) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(clustereventsourcesResource, opts)) -} - -// Create takes the representation of a clusterEventSource and creates it. Returns the server's representation of the clusterEventSource, and an error, if there is any. -func (c *FakeClusterEventSources) Create(clusterEventSource *v1alpha1.ClusterEventSource) (result *v1alpha1.ClusterEventSource, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(clustereventsourcesResource, clusterEventSource), &v1alpha1.ClusterEventSource{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterEventSource), err -} - -// Update takes the representation of a clusterEventSource and updates it. Returns the server's representation of the clusterEventSource, and an error, if there is any. -func (c *FakeClusterEventSources) Update(clusterEventSource *v1alpha1.ClusterEventSource) (result *v1alpha1.ClusterEventSource, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(clustereventsourcesResource, clusterEventSource), &v1alpha1.ClusterEventSource{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterEventSource), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeClusterEventSources) UpdateStatus(clusterEventSource *v1alpha1.ClusterEventSource) (*v1alpha1.ClusterEventSource, error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateSubresourceAction(clustereventsourcesResource, "status", clusterEventSource), &v1alpha1.ClusterEventSource{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterEventSource), err -} - -// Delete takes name of the clusterEventSource and deletes it. Returns an error if one occurs. -func (c *FakeClusterEventSources) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(clustereventsourcesResource, name), &v1alpha1.ClusterEventSource{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeClusterEventSources) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(clustereventsourcesResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.ClusterEventSourceList{}) - return err -} - -// Patch applies the patch and returns the patched clusterEventSource. -func (c *FakeClusterEventSources) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterEventSource, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(clustereventsourcesResource, name, data, subresources...), &v1alpha1.ClusterEventSource{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterEventSource), err -} diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_clustereventtype.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_clustereventtype.go deleted file mode 100644 index 87ba368afbf..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_clustereventtype.go +++ /dev/null @@ -1,131 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeClusterEventTypes implements ClusterEventTypeInterface -type FakeClusterEventTypes struct { - Fake *FakeFeedsV1alpha1 -} - -var clustereventtypesResource = schema.GroupVersionResource{Group: "feeds.knative.dev", Version: "v1alpha1", Resource: "clustereventtypes"} - -var clustereventtypesKind = schema.GroupVersionKind{Group: "feeds.knative.dev", Version: "v1alpha1", Kind: "ClusterEventType"} - -// Get takes name of the clusterEventType, and returns the corresponding clusterEventType object, and an error if there is any. -func (c *FakeClusterEventTypes) Get(name string, options v1.GetOptions) (result *v1alpha1.ClusterEventType, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootGetAction(clustereventtypesResource, name), &v1alpha1.ClusterEventType{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterEventType), err -} - -// List takes label and field selectors, and returns the list of ClusterEventTypes that match those selectors. -func (c *FakeClusterEventTypes) List(opts v1.ListOptions) (result *v1alpha1.ClusterEventTypeList, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootListAction(clustereventtypesResource, clustereventtypesKind, opts), &v1alpha1.ClusterEventTypeList{}) - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.ClusterEventTypeList{ListMeta: obj.(*v1alpha1.ClusterEventTypeList).ListMeta} - for _, item := range obj.(*v1alpha1.ClusterEventTypeList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested clusterEventTypes. -func (c *FakeClusterEventTypes) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewRootWatchAction(clustereventtypesResource, opts)) -} - -// Create takes the representation of a clusterEventType and creates it. Returns the server's representation of the clusterEventType, and an error, if there is any. -func (c *FakeClusterEventTypes) Create(clusterEventType *v1alpha1.ClusterEventType) (result *v1alpha1.ClusterEventType, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootCreateAction(clustereventtypesResource, clusterEventType), &v1alpha1.ClusterEventType{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterEventType), err -} - -// Update takes the representation of a clusterEventType and updates it. Returns the server's representation of the clusterEventType, and an error, if there is any. -func (c *FakeClusterEventTypes) Update(clusterEventType *v1alpha1.ClusterEventType) (result *v1alpha1.ClusterEventType, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateAction(clustereventtypesResource, clusterEventType), &v1alpha1.ClusterEventType{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterEventType), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeClusterEventTypes) UpdateStatus(clusterEventType *v1alpha1.ClusterEventType) (*v1alpha1.ClusterEventType, error) { - obj, err := c.Fake. - Invokes(testing.NewRootUpdateSubresourceAction(clustereventtypesResource, "status", clusterEventType), &v1alpha1.ClusterEventType{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterEventType), err -} - -// Delete takes name of the clusterEventType and deletes it. Returns an error if one occurs. -func (c *FakeClusterEventTypes) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewRootDeleteAction(clustereventtypesResource, name), &v1alpha1.ClusterEventType{}) - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeClusterEventTypes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewRootDeleteCollectionAction(clustereventtypesResource, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.ClusterEventTypeList{}) - return err -} - -// Patch applies the patch and returns the patched clusterEventType. -func (c *FakeClusterEventTypes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterEventType, err error) { - obj, err := c.Fake. - Invokes(testing.NewRootPatchSubresourceAction(clustereventtypesResource, name, data, subresources...), &v1alpha1.ClusterEventType{}) - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.ClusterEventType), err -} diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_eventsource.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_eventsource.go deleted file mode 100644 index fbd3b10ed0a..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_eventsource.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeEventSources implements EventSourceInterface -type FakeEventSources struct { - Fake *FakeFeedsV1alpha1 - ns string -} - -var eventsourcesResource = schema.GroupVersionResource{Group: "feeds.knative.dev", Version: "v1alpha1", Resource: "eventsources"} - -var eventsourcesKind = schema.GroupVersionKind{Group: "feeds.knative.dev", Version: "v1alpha1", Kind: "EventSource"} - -// Get takes name of the eventSource, and returns the corresponding eventSource object, and an error if there is any. -func (c *FakeEventSources) Get(name string, options v1.GetOptions) (result *v1alpha1.EventSource, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(eventsourcesResource, c.ns, name), &v1alpha1.EventSource{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.EventSource), err -} - -// List takes label and field selectors, and returns the list of EventSources that match those selectors. -func (c *FakeEventSources) List(opts v1.ListOptions) (result *v1alpha1.EventSourceList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(eventsourcesResource, eventsourcesKind, c.ns, opts), &v1alpha1.EventSourceList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.EventSourceList{ListMeta: obj.(*v1alpha1.EventSourceList).ListMeta} - for _, item := range obj.(*v1alpha1.EventSourceList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested eventSources. -func (c *FakeEventSources) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(eventsourcesResource, c.ns, opts)) - -} - -// Create takes the representation of a eventSource and creates it. Returns the server's representation of the eventSource, and an error, if there is any. -func (c *FakeEventSources) Create(eventSource *v1alpha1.EventSource) (result *v1alpha1.EventSource, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(eventsourcesResource, c.ns, eventSource), &v1alpha1.EventSource{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.EventSource), err -} - -// Update takes the representation of a eventSource and updates it. Returns the server's representation of the eventSource, and an error, if there is any. -func (c *FakeEventSources) Update(eventSource *v1alpha1.EventSource) (result *v1alpha1.EventSource, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(eventsourcesResource, c.ns, eventSource), &v1alpha1.EventSource{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.EventSource), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeEventSources) UpdateStatus(eventSource *v1alpha1.EventSource) (*v1alpha1.EventSource, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(eventsourcesResource, "status", c.ns, eventSource), &v1alpha1.EventSource{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.EventSource), err -} - -// Delete takes name of the eventSource and deletes it. Returns an error if one occurs. -func (c *FakeEventSources) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(eventsourcesResource, c.ns, name), &v1alpha1.EventSource{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeEventSources) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(eventsourcesResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.EventSourceList{}) - return err -} - -// Patch applies the patch and returns the patched eventSource. -func (c *FakeEventSources) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.EventSource, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(eventsourcesResource, c.ns, name, data, subresources...), &v1alpha1.EventSource{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.EventSource), err -} diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_eventtype.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_eventtype.go deleted file mode 100644 index bdef7163d5e..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_eventtype.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeEventTypes implements EventTypeInterface -type FakeEventTypes struct { - Fake *FakeFeedsV1alpha1 - ns string -} - -var eventtypesResource = schema.GroupVersionResource{Group: "feeds.knative.dev", Version: "v1alpha1", Resource: "eventtypes"} - -var eventtypesKind = schema.GroupVersionKind{Group: "feeds.knative.dev", Version: "v1alpha1", Kind: "EventType"} - -// Get takes name of the eventType, and returns the corresponding eventType object, and an error if there is any. -func (c *FakeEventTypes) Get(name string, options v1.GetOptions) (result *v1alpha1.EventType, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(eventtypesResource, c.ns, name), &v1alpha1.EventType{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.EventType), err -} - -// List takes label and field selectors, and returns the list of EventTypes that match those selectors. -func (c *FakeEventTypes) List(opts v1.ListOptions) (result *v1alpha1.EventTypeList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(eventtypesResource, eventtypesKind, c.ns, opts), &v1alpha1.EventTypeList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.EventTypeList{ListMeta: obj.(*v1alpha1.EventTypeList).ListMeta} - for _, item := range obj.(*v1alpha1.EventTypeList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested eventTypes. -func (c *FakeEventTypes) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(eventtypesResource, c.ns, opts)) - -} - -// Create takes the representation of a eventType and creates it. Returns the server's representation of the eventType, and an error, if there is any. -func (c *FakeEventTypes) Create(eventType *v1alpha1.EventType) (result *v1alpha1.EventType, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(eventtypesResource, c.ns, eventType), &v1alpha1.EventType{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.EventType), err -} - -// Update takes the representation of a eventType and updates it. Returns the server's representation of the eventType, and an error, if there is any. -func (c *FakeEventTypes) Update(eventType *v1alpha1.EventType) (result *v1alpha1.EventType, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(eventtypesResource, c.ns, eventType), &v1alpha1.EventType{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.EventType), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeEventTypes) UpdateStatus(eventType *v1alpha1.EventType) (*v1alpha1.EventType, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(eventtypesResource, "status", c.ns, eventType), &v1alpha1.EventType{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.EventType), err -} - -// Delete takes name of the eventType and deletes it. Returns an error if one occurs. -func (c *FakeEventTypes) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(eventtypesResource, c.ns, name), &v1alpha1.EventType{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeEventTypes) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(eventtypesResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.EventTypeList{}) - return err -} - -// Patch applies the patch and returns the patched eventType. -func (c *FakeEventTypes) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.EventType, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(eventtypesResource, c.ns, name, data, subresources...), &v1alpha1.EventType{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.EventType), err -} diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_feed.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_feed.go deleted file mode 100644 index 45f6beaa92c..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_feed.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeFeeds implements FeedInterface -type FakeFeeds struct { - Fake *FakeFeedsV1alpha1 - ns string -} - -var feedsResource = schema.GroupVersionResource{Group: "feeds.knative.dev", Version: "v1alpha1", Resource: "feeds"} - -var feedsKind = schema.GroupVersionKind{Group: "feeds.knative.dev", Version: "v1alpha1", Kind: "Feed"} - -// Get takes name of the feed, and returns the corresponding feed object, and an error if there is any. -func (c *FakeFeeds) Get(name string, options v1.GetOptions) (result *v1alpha1.Feed, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(feedsResource, c.ns, name), &v1alpha1.Feed{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Feed), err -} - -// List takes label and field selectors, and returns the list of Feeds that match those selectors. -func (c *FakeFeeds) List(opts v1.ListOptions) (result *v1alpha1.FeedList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(feedsResource, feedsKind, c.ns, opts), &v1alpha1.FeedList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.FeedList{ListMeta: obj.(*v1alpha1.FeedList).ListMeta} - for _, item := range obj.(*v1alpha1.FeedList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested feeds. -func (c *FakeFeeds) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(feedsResource, c.ns, opts)) - -} - -// Create takes the representation of a feed and creates it. Returns the server's representation of the feed, and an error, if there is any. -func (c *FakeFeeds) Create(feed *v1alpha1.Feed) (result *v1alpha1.Feed, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(feedsResource, c.ns, feed), &v1alpha1.Feed{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Feed), err -} - -// Update takes the representation of a feed and updates it. Returns the server's representation of the feed, and an error, if there is any. -func (c *FakeFeeds) Update(feed *v1alpha1.Feed) (result *v1alpha1.Feed, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(feedsResource, c.ns, feed), &v1alpha1.Feed{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Feed), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeFeeds) UpdateStatus(feed *v1alpha1.Feed) (*v1alpha1.Feed, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(feedsResource, "status", c.ns, feed), &v1alpha1.Feed{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Feed), err -} - -// Delete takes name of the feed and deletes it. Returns an error if one occurs. -func (c *FakeFeeds) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(feedsResource, c.ns, name), &v1alpha1.Feed{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeFeeds) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(feedsResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.FeedList{}) - return err -} - -// Patch applies the patch and returns the patched feed. -func (c *FakeFeeds) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Feed, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(feedsResource, c.ns, name, data, subresources...), &v1alpha1.Feed{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Feed), err -} diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_feeds_client.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_feeds_client.go deleted file mode 100644 index a1a506c5aef..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/fake/fake_feeds_client.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/feeds/v1alpha1" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeFeedsV1alpha1 struct { - *testing.Fake -} - -func (c *FakeFeedsV1alpha1) ClusterEventSources() v1alpha1.ClusterEventSourceInterface { - return &FakeClusterEventSources{c} -} - -func (c *FakeFeedsV1alpha1) ClusterEventTypes() v1alpha1.ClusterEventTypeInterface { - return &FakeClusterEventTypes{c} -} - -func (c *FakeFeedsV1alpha1) EventSources(namespace string) v1alpha1.EventSourceInterface { - return &FakeEventSources{c, namespace} -} - -func (c *FakeFeedsV1alpha1) EventTypes(namespace string) v1alpha1.EventTypeInterface { - return &FakeEventTypes{c, namespace} -} - -func (c *FakeFeedsV1alpha1) Feeds(namespace string) v1alpha1.FeedInterface { - return &FakeFeeds{c, namespace} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeFeedsV1alpha1) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/feed.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/feed.go deleted file mode 100644 index f521a734677..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/feed.go +++ /dev/null @@ -1,174 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// FeedsGetter has a method to return a FeedInterface. -// A group's client should implement this interface. -type FeedsGetter interface { - Feeds(namespace string) FeedInterface -} - -// FeedInterface has methods to work with Feed resources. -type FeedInterface interface { - Create(*v1alpha1.Feed) (*v1alpha1.Feed, error) - Update(*v1alpha1.Feed) (*v1alpha1.Feed, error) - UpdateStatus(*v1alpha1.Feed) (*v1alpha1.Feed, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.Feed, error) - List(opts v1.ListOptions) (*v1alpha1.FeedList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Feed, err error) - FeedExpansion -} - -// feeds implements FeedInterface -type feeds struct { - client rest.Interface - ns string -} - -// newFeeds returns a Feeds -func newFeeds(c *FeedsV1alpha1Client, namespace string) *feeds { - return &feeds{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the feed, and returns the corresponding feed object, and an error if there is any. -func (c *feeds) Get(name string, options v1.GetOptions) (result *v1alpha1.Feed, err error) { - result = &v1alpha1.Feed{} - err = c.client.Get(). - Namespace(c.ns). - Resource("feeds"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Feeds that match those selectors. -func (c *feeds) List(opts v1.ListOptions) (result *v1alpha1.FeedList, err error) { - result = &v1alpha1.FeedList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("feeds"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested feeds. -func (c *feeds) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("feeds"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a feed and creates it. Returns the server's representation of the feed, and an error, if there is any. -func (c *feeds) Create(feed *v1alpha1.Feed) (result *v1alpha1.Feed, err error) { - result = &v1alpha1.Feed{} - err = c.client.Post(). - Namespace(c.ns). - Resource("feeds"). - Body(feed). - Do(). - Into(result) - return -} - -// Update takes the representation of a feed and updates it. Returns the server's representation of the feed, and an error, if there is any. -func (c *feeds) Update(feed *v1alpha1.Feed) (result *v1alpha1.Feed, err error) { - result = &v1alpha1.Feed{} - err = c.client.Put(). - Namespace(c.ns). - Resource("feeds"). - Name(feed.Name). - Body(feed). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *feeds) UpdateStatus(feed *v1alpha1.Feed) (result *v1alpha1.Feed, err error) { - result = &v1alpha1.Feed{} - err = c.client.Put(). - Namespace(c.ns). - Resource("feeds"). - Name(feed.Name). - SubResource("status"). - Body(feed). - Do(). - Into(result) - return -} - -// Delete takes name of the feed and deletes it. Returns an error if one occurs. -func (c *feeds) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("feeds"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *feeds) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("feeds"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched feed. -func (c *feeds) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Feed, err error) { - result = &v1alpha1.Feed{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("feeds"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/feeds_client.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/feeds_client.go deleted file mode 100644 index df66a2000b0..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/feeds_client.go +++ /dev/null @@ -1,110 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - rest "k8s.io/client-go/rest" -) - -type FeedsV1alpha1Interface interface { - RESTClient() rest.Interface - ClusterEventSourcesGetter - ClusterEventTypesGetter - EventSourcesGetter - EventTypesGetter - FeedsGetter -} - -// FeedsV1alpha1Client is used to interact with features provided by the feeds.knative.dev group. -type FeedsV1alpha1Client struct { - restClient rest.Interface -} - -func (c *FeedsV1alpha1Client) ClusterEventSources() ClusterEventSourceInterface { - return newClusterEventSources(c) -} - -func (c *FeedsV1alpha1Client) ClusterEventTypes() ClusterEventTypeInterface { - return newClusterEventTypes(c) -} - -func (c *FeedsV1alpha1Client) EventSources(namespace string) EventSourceInterface { - return newEventSources(c, namespace) -} - -func (c *FeedsV1alpha1Client) EventTypes(namespace string) EventTypeInterface { - return newEventTypes(c, namespace) -} - -func (c *FeedsV1alpha1Client) Feeds(namespace string) FeedInterface { - return newFeeds(c, namespace) -} - -// NewForConfig creates a new FeedsV1alpha1Client for the given config. -func NewForConfig(c *rest.Config) (*FeedsV1alpha1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &FeedsV1alpha1Client{client}, nil -} - -// NewForConfigOrDie creates a new FeedsV1alpha1Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *FeedsV1alpha1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new FeedsV1alpha1Client for the given RESTClient. -func New(c rest.Interface) *FeedsV1alpha1Client { - return &FeedsV1alpha1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1alpha1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FeedsV1alpha1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/feeds/v1alpha1/generated_expansion.go deleted file mode 100644 index bb9c1c83f55..00000000000 --- a/pkg/client/clientset/versioned/typed/feeds/v1alpha1/generated_expansion.go +++ /dev/null @@ -1,29 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -type ClusterEventSourceExpansion interface{} - -type ClusterEventTypeExpansion interface{} - -type EventSourceExpansion interface{} - -type EventTypeExpansion interface{} - -type FeedExpansion interface{} diff --git a/pkg/client/clientset/versioned/typed/flows/v1alpha1/doc.go b/pkg/client/clientset/versioned/typed/flows/v1alpha1/doc.go deleted file mode 100644 index 75445c17900..00000000000 --- a/pkg/client/clientset/versioned/typed/flows/v1alpha1/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// This package has the automatically generated typed clients. -package v1alpha1 diff --git a/pkg/client/clientset/versioned/typed/flows/v1alpha1/fake/doc.go b/pkg/client/clientset/versioned/typed/flows/v1alpha1/fake/doc.go deleted file mode 100644 index 128aa183a91..00000000000 --- a/pkg/client/clientset/versioned/typed/flows/v1alpha1/fake/doc.go +++ /dev/null @@ -1,20 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -// Package fake has the automatically generated clients. -package fake diff --git a/pkg/client/clientset/versioned/typed/flows/v1alpha1/fake/fake_flow.go b/pkg/client/clientset/versioned/typed/flows/v1alpha1/fake/fake_flow.go deleted file mode 100644 index 786dfd44e29..00000000000 --- a/pkg/client/clientset/versioned/typed/flows/v1alpha1/fake/fake_flow.go +++ /dev/null @@ -1,140 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - labels "k8s.io/apimachinery/pkg/labels" - schema "k8s.io/apimachinery/pkg/runtime/schema" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - testing "k8s.io/client-go/testing" -) - -// FakeFlows implements FlowInterface -type FakeFlows struct { - Fake *FakeFlowsV1alpha1 - ns string -} - -var flowsResource = schema.GroupVersionResource{Group: "flows.knative.dev", Version: "v1alpha1", Resource: "flows"} - -var flowsKind = schema.GroupVersionKind{Group: "flows.knative.dev", Version: "v1alpha1", Kind: "Flow"} - -// Get takes name of the flow, and returns the corresponding flow object, and an error if there is any. -func (c *FakeFlows) Get(name string, options v1.GetOptions) (result *v1alpha1.Flow, err error) { - obj, err := c.Fake. - Invokes(testing.NewGetAction(flowsResource, c.ns, name), &v1alpha1.Flow{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Flow), err -} - -// List takes label and field selectors, and returns the list of Flows that match those selectors. -func (c *FakeFlows) List(opts v1.ListOptions) (result *v1alpha1.FlowList, err error) { - obj, err := c.Fake. - Invokes(testing.NewListAction(flowsResource, flowsKind, c.ns, opts), &v1alpha1.FlowList{}) - - if obj == nil { - return nil, err - } - - label, _, _ := testing.ExtractFromListOptions(opts) - if label == nil { - label = labels.Everything() - } - list := &v1alpha1.FlowList{ListMeta: obj.(*v1alpha1.FlowList).ListMeta} - for _, item := range obj.(*v1alpha1.FlowList).Items { - if label.Matches(labels.Set(item.Labels)) { - list.Items = append(list.Items, item) - } - } - return list, err -} - -// Watch returns a watch.Interface that watches the requested flows. -func (c *FakeFlows) Watch(opts v1.ListOptions) (watch.Interface, error) { - return c.Fake. - InvokesWatch(testing.NewWatchAction(flowsResource, c.ns, opts)) - -} - -// Create takes the representation of a flow and creates it. Returns the server's representation of the flow, and an error, if there is any. -func (c *FakeFlows) Create(flow *v1alpha1.Flow) (result *v1alpha1.Flow, err error) { - obj, err := c.Fake. - Invokes(testing.NewCreateAction(flowsResource, c.ns, flow), &v1alpha1.Flow{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Flow), err -} - -// Update takes the representation of a flow and updates it. Returns the server's representation of the flow, and an error, if there is any. -func (c *FakeFlows) Update(flow *v1alpha1.Flow) (result *v1alpha1.Flow, err error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateAction(flowsResource, c.ns, flow), &v1alpha1.Flow{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Flow), err -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). -func (c *FakeFlows) UpdateStatus(flow *v1alpha1.Flow) (*v1alpha1.Flow, error) { - obj, err := c.Fake. - Invokes(testing.NewUpdateSubresourceAction(flowsResource, "status", c.ns, flow), &v1alpha1.Flow{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Flow), err -} - -// Delete takes name of the flow and deletes it. Returns an error if one occurs. -func (c *FakeFlows) Delete(name string, options *v1.DeleteOptions) error { - _, err := c.Fake. - Invokes(testing.NewDeleteAction(flowsResource, c.ns, name), &v1alpha1.Flow{}) - - return err -} - -// DeleteCollection deletes a collection of objects. -func (c *FakeFlows) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - action := testing.NewDeleteCollectionAction(flowsResource, c.ns, listOptions) - - _, err := c.Fake.Invokes(action, &v1alpha1.FlowList{}) - return err -} - -// Patch applies the patch and returns the patched flow. -func (c *FakeFlows) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Flow, err error) { - obj, err := c.Fake. - Invokes(testing.NewPatchSubresourceAction(flowsResource, c.ns, name, data, subresources...), &v1alpha1.Flow{}) - - if obj == nil { - return nil, err - } - return obj.(*v1alpha1.Flow), err -} diff --git a/pkg/client/clientset/versioned/typed/flows/v1alpha1/fake/fake_flows_client.go b/pkg/client/clientset/versioned/typed/flows/v1alpha1/fake/fake_flows_client.go deleted file mode 100644 index 266afaff88b..00000000000 --- a/pkg/client/clientset/versioned/typed/flows/v1alpha1/fake/fake_flows_client.go +++ /dev/null @@ -1,40 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package fake - -import ( - v1alpha1 "github.com/knative/eventing/pkg/client/clientset/versioned/typed/flows/v1alpha1" - rest "k8s.io/client-go/rest" - testing "k8s.io/client-go/testing" -) - -type FakeFlowsV1alpha1 struct { - *testing.Fake -} - -func (c *FakeFlowsV1alpha1) Flows(namespace string) v1alpha1.FlowInterface { - return &FakeFlows{c, namespace} -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FakeFlowsV1alpha1) RESTClient() rest.Interface { - var ret *rest.RESTClient - return ret -} diff --git a/pkg/client/clientset/versioned/typed/flows/v1alpha1/flow.go b/pkg/client/clientset/versioned/typed/flows/v1alpha1/flow.go deleted file mode 100644 index f687c0dd387..00000000000 --- a/pkg/client/clientset/versioned/typed/flows/v1alpha1/flow.go +++ /dev/null @@ -1,174 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - scheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// FlowsGetter has a method to return a FlowInterface. -// A group's client should implement this interface. -type FlowsGetter interface { - Flows(namespace string) FlowInterface -} - -// FlowInterface has methods to work with Flow resources. -type FlowInterface interface { - Create(*v1alpha1.Flow) (*v1alpha1.Flow, error) - Update(*v1alpha1.Flow) (*v1alpha1.Flow, error) - UpdateStatus(*v1alpha1.Flow) (*v1alpha1.Flow, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha1.Flow, error) - List(opts v1.ListOptions) (*v1alpha1.FlowList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Flow, err error) - FlowExpansion -} - -// flows implements FlowInterface -type flows struct { - client rest.Interface - ns string -} - -// newFlows returns a Flows -func newFlows(c *FlowsV1alpha1Client, namespace string) *flows { - return &flows{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the flow, and returns the corresponding flow object, and an error if there is any. -func (c *flows) Get(name string, options v1.GetOptions) (result *v1alpha1.Flow, err error) { - result = &v1alpha1.Flow{} - err = c.client.Get(). - Namespace(c.ns). - Resource("flows"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Flows that match those selectors. -func (c *flows) List(opts v1.ListOptions) (result *v1alpha1.FlowList, err error) { - result = &v1alpha1.FlowList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("flows"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested flows. -func (c *flows) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("flows"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a flow and creates it. Returns the server's representation of the flow, and an error, if there is any. -func (c *flows) Create(flow *v1alpha1.Flow) (result *v1alpha1.Flow, err error) { - result = &v1alpha1.Flow{} - err = c.client.Post(). - Namespace(c.ns). - Resource("flows"). - Body(flow). - Do(). - Into(result) - return -} - -// Update takes the representation of a flow and updates it. Returns the server's representation of the flow, and an error, if there is any. -func (c *flows) Update(flow *v1alpha1.Flow) (result *v1alpha1.Flow, err error) { - result = &v1alpha1.Flow{} - err = c.client.Put(). - Namespace(c.ns). - Resource("flows"). - Name(flow.Name). - Body(flow). - Do(). - Into(result) - return -} - -// UpdateStatus was generated because the type contains a Status member. -// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). - -func (c *flows) UpdateStatus(flow *v1alpha1.Flow) (result *v1alpha1.Flow, err error) { - result = &v1alpha1.Flow{} - err = c.client.Put(). - Namespace(c.ns). - Resource("flows"). - Name(flow.Name). - SubResource("status"). - Body(flow). - Do(). - Into(result) - return -} - -// Delete takes name of the flow and deletes it. Returns an error if one occurs. -func (c *flows) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("flows"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *flows) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("flows"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched flow. -func (c *flows) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.Flow, err error) { - result = &v1alpha1.Flow{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("flows"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/pkg/client/clientset/versioned/typed/flows/v1alpha1/flows_client.go b/pkg/client/clientset/versioned/typed/flows/v1alpha1/flows_client.go deleted file mode 100644 index 961c1d1972b..00000000000 --- a/pkg/client/clientset/versioned/typed/flows/v1alpha1/flows_client.go +++ /dev/null @@ -1,90 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - serializer "k8s.io/apimachinery/pkg/runtime/serializer" - rest "k8s.io/client-go/rest" -) - -type FlowsV1alpha1Interface interface { - RESTClient() rest.Interface - FlowsGetter -} - -// FlowsV1alpha1Client is used to interact with features provided by the flows.knative.dev group. -type FlowsV1alpha1Client struct { - restClient rest.Interface -} - -func (c *FlowsV1alpha1Client) Flows(namespace string) FlowInterface { - return newFlows(c, namespace) -} - -// NewForConfig creates a new FlowsV1alpha1Client for the given config. -func NewForConfig(c *rest.Config) (*FlowsV1alpha1Client, error) { - config := *c - if err := setConfigDefaults(&config); err != nil { - return nil, err - } - client, err := rest.RESTClientFor(&config) - if err != nil { - return nil, err - } - return &FlowsV1alpha1Client{client}, nil -} - -// NewForConfigOrDie creates a new FlowsV1alpha1Client for the given config and -// panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *FlowsV1alpha1Client { - client, err := NewForConfig(c) - if err != nil { - panic(err) - } - return client -} - -// New creates a new FlowsV1alpha1Client for the given RESTClient. -func New(c rest.Interface) *FlowsV1alpha1Client { - return &FlowsV1alpha1Client{c} -} - -func setConfigDefaults(config *rest.Config) error { - gv := v1alpha1.SchemeGroupVersion - config.GroupVersion = &gv - config.APIPath = "/apis" - config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} - - if config.UserAgent == "" { - config.UserAgent = rest.DefaultKubernetesUserAgent() - } - - return nil -} - -// RESTClient returns a RESTClient that is used to communicate -// with API server by this client implementation. -func (c *FlowsV1alpha1Client) RESTClient() rest.Interface { - if c == nil { - return nil - } - return c.restClient -} diff --git a/pkg/client/clientset/versioned/typed/flows/v1alpha1/generated_expansion.go b/pkg/client/clientset/versioned/typed/flows/v1alpha1/generated_expansion.go deleted file mode 100644 index 2e4de776975..00000000000 --- a/pkg/client/clientset/versioned/typed/flows/v1alpha1/generated_expansion.go +++ /dev/null @@ -1,21 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by client-gen. DO NOT EDIT. - -package v1alpha1 - -type FlowExpansion interface{} diff --git a/pkg/client/informers/externalversions/channels/interface.go b/pkg/client/informers/externalversions/channels/interface.go deleted file mode 100644 index ab211697acd..00000000000 --- a/pkg/client/informers/externalversions/channels/interface.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package channels - -import ( - v1alpha1 "github.com/knative/eventing/pkg/client/informers/externalversions/channels/v1alpha1" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to each of this group's versions. -type Interface interface { - // V1alpha1 provides access to shared informers for resources in V1alpha1. - V1alpha1() v1alpha1.Interface -} - -type group struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// V1alpha1 returns a new v1alpha1.Interface. -func (g *group) V1alpha1() v1alpha1.Interface { - return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) -} diff --git a/pkg/client/informers/externalversions/channels/v1alpha1/bus.go b/pkg/client/informers/externalversions/channels/v1alpha1/bus.go deleted file mode 100644 index 43ee3cffb56..00000000000 --- a/pkg/client/informers/externalversions/channels/v1alpha1/bus.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - channels_v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - versioned "github.com/knative/eventing/pkg/client/clientset/versioned" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/knative/eventing/pkg/client/listers/channels/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// BusInformer provides access to a shared informer and lister for -// Buses. -type BusInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.BusLister -} - -type busInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewBusInformer constructs a new informer for Bus type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewBusInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredBusInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredBusInformer constructs a new informer for Bus type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredBusInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.ChannelsV1alpha1().Buses(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.ChannelsV1alpha1().Buses(namespace).Watch(options) - }, - }, - &channels_v1alpha1.Bus{}, - resyncPeriod, - indexers, - ) -} - -func (f *busInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredBusInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *busInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&channels_v1alpha1.Bus{}, f.defaultInformer) -} - -func (f *busInformer) Lister() v1alpha1.BusLister { - return v1alpha1.NewBusLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/channels/v1alpha1/channel.go b/pkg/client/informers/externalversions/channels/v1alpha1/channel.go deleted file mode 100644 index b00e1fe36c5..00000000000 --- a/pkg/client/informers/externalversions/channels/v1alpha1/channel.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - channels_v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - versioned "github.com/knative/eventing/pkg/client/clientset/versioned" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/knative/eventing/pkg/client/listers/channels/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// ChannelInformer provides access to a shared informer and lister for -// Channels. -type ChannelInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.ChannelLister -} - -type channelInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewChannelInformer constructs a new informer for Channel type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewChannelInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredChannelInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredChannelInformer constructs a new informer for Channel type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredChannelInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.ChannelsV1alpha1().Channels(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.ChannelsV1alpha1().Channels(namespace).Watch(options) - }, - }, - &channels_v1alpha1.Channel{}, - resyncPeriod, - indexers, - ) -} - -func (f *channelInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredChannelInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *channelInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&channels_v1alpha1.Channel{}, f.defaultInformer) -} - -func (f *channelInformer) Lister() v1alpha1.ChannelLister { - return v1alpha1.NewChannelLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/channels/v1alpha1/clusterbus.go b/pkg/client/informers/externalversions/channels/v1alpha1/clusterbus.go deleted file mode 100644 index 109af846e71..00000000000 --- a/pkg/client/informers/externalversions/channels/v1alpha1/clusterbus.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - channels_v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - versioned "github.com/knative/eventing/pkg/client/clientset/versioned" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/knative/eventing/pkg/client/listers/channels/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// ClusterBusInformer provides access to a shared informer and lister for -// ClusterBuses. -type ClusterBusInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.ClusterBusLister -} - -type clusterBusInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// NewClusterBusInformer constructs a new informer for ClusterBus type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewClusterBusInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredClusterBusInformer(client, resyncPeriod, indexers, nil) -} - -// NewFilteredClusterBusInformer constructs a new informer for ClusterBus type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredClusterBusInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.ChannelsV1alpha1().ClusterBuses().List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.ChannelsV1alpha1().ClusterBuses().Watch(options) - }, - }, - &channels_v1alpha1.ClusterBus{}, - resyncPeriod, - indexers, - ) -} - -func (f *clusterBusInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredClusterBusInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *clusterBusInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&channels_v1alpha1.ClusterBus{}, f.defaultInformer) -} - -func (f *clusterBusInformer) Lister() v1alpha1.ClusterBusLister { - return v1alpha1.NewClusterBusLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/channels/v1alpha1/interface.go b/pkg/client/informers/externalversions/channels/v1alpha1/interface.go deleted file mode 100644 index 0831ac85fba..00000000000 --- a/pkg/client/informers/externalversions/channels/v1alpha1/interface.go +++ /dev/null @@ -1,66 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // Buses returns a BusInformer. - Buses() BusInformer - // Channels returns a ChannelInformer. - Channels() ChannelInformer - // ClusterBuses returns a ClusterBusInformer. - ClusterBuses() ClusterBusInformer - // Subscriptions returns a SubscriptionInformer. - Subscriptions() SubscriptionInformer -} - -type version struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// Buses returns a BusInformer. -func (v *version) Buses() BusInformer { - return &busInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// Channels returns a ChannelInformer. -func (v *version) Channels() ChannelInformer { - return &channelInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// ClusterBuses returns a ClusterBusInformer. -func (v *version) ClusterBuses() ClusterBusInformer { - return &clusterBusInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} -} - -// Subscriptions returns a SubscriptionInformer. -func (v *version) Subscriptions() SubscriptionInformer { - return &subscriptionInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/client/informers/externalversions/channels/v1alpha1/subscription.go b/pkg/client/informers/externalversions/channels/v1alpha1/subscription.go deleted file mode 100644 index ee2ae6d1a23..00000000000 --- a/pkg/client/informers/externalversions/channels/v1alpha1/subscription.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - channels_v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - versioned "github.com/knative/eventing/pkg/client/clientset/versioned" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/knative/eventing/pkg/client/listers/channels/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// SubscriptionInformer provides access to a shared informer and lister for -// Subscriptions. -type SubscriptionInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.SubscriptionLister -} - -type subscriptionInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewSubscriptionInformer constructs a new informer for Subscription type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewSubscriptionInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredSubscriptionInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredSubscriptionInformer constructs a new informer for Subscription type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredSubscriptionInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.ChannelsV1alpha1().Subscriptions(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.ChannelsV1alpha1().Subscriptions(namespace).Watch(options) - }, - }, - &channels_v1alpha1.Subscription{}, - resyncPeriod, - indexers, - ) -} - -func (f *subscriptionInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredSubscriptionInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *subscriptionInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&channels_v1alpha1.Subscription{}, f.defaultInformer) -} - -func (f *subscriptionInformer) Lister() v1alpha1.SubscriptionLister { - return v1alpha1.NewSubscriptionLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/factory.go b/pkg/client/informers/externalversions/factory.go index adee6a64b79..0cde7d2cdd1 100644 --- a/pkg/client/informers/externalversions/factory.go +++ b/pkg/client/informers/externalversions/factory.go @@ -24,10 +24,7 @@ import ( time "time" versioned "github.com/knative/eventing/pkg/client/clientset/versioned" - channels "github.com/knative/eventing/pkg/client/informers/externalversions/channels" eventing "github.com/knative/eventing/pkg/client/informers/externalversions/eventing" - feeds "github.com/knative/eventing/pkg/client/informers/externalversions/feeds" - flows "github.com/knative/eventing/pkg/client/informers/externalversions/flows" internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" @@ -175,24 +172,9 @@ type SharedInformerFactory interface { ForResource(resource schema.GroupVersionResource) (GenericInformer, error) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool - Channels() channels.Interface Eventing() eventing.Interface - Feeds() feeds.Interface - Flows() flows.Interface -} - -func (f *sharedInformerFactory) Channels() channels.Interface { - return channels.New(f, f.namespace, f.tweakListOptions) } func (f *sharedInformerFactory) Eventing() eventing.Interface { return eventing.New(f, f.namespace, f.tweakListOptions) } - -func (f *sharedInformerFactory) Feeds() feeds.Interface { - return feeds.New(f, f.namespace, f.tweakListOptions) -} - -func (f *sharedInformerFactory) Flows() flows.Interface { - return flows.New(f, f.namespace, f.tweakListOptions) -} diff --git a/pkg/client/informers/externalversions/feeds/interface.go b/pkg/client/informers/externalversions/feeds/interface.go deleted file mode 100644 index 92dd958b383..00000000000 --- a/pkg/client/informers/externalversions/feeds/interface.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package feeds - -import ( - v1alpha1 "github.com/knative/eventing/pkg/client/informers/externalversions/feeds/v1alpha1" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to each of this group's versions. -type Interface interface { - // V1alpha1 provides access to shared informers for resources in V1alpha1. - V1alpha1() v1alpha1.Interface -} - -type group struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// V1alpha1 returns a new v1alpha1.Interface. -func (g *group) V1alpha1() v1alpha1.Interface { - return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) -} diff --git a/pkg/client/informers/externalversions/feeds/v1alpha1/clustereventsource.go b/pkg/client/informers/externalversions/feeds/v1alpha1/clustereventsource.go deleted file mode 100644 index fe59444d500..00000000000 --- a/pkg/client/informers/externalversions/feeds/v1alpha1/clustereventsource.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - feeds_v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - versioned "github.com/knative/eventing/pkg/client/clientset/versioned" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/knative/eventing/pkg/client/listers/feeds/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// ClusterEventSourceInformer provides access to a shared informer and lister for -// ClusterEventSources. -type ClusterEventSourceInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.ClusterEventSourceLister -} - -type clusterEventSourceInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// NewClusterEventSourceInformer constructs a new informer for ClusterEventSource type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewClusterEventSourceInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredClusterEventSourceInformer(client, resyncPeriod, indexers, nil) -} - -// NewFilteredClusterEventSourceInformer constructs a new informer for ClusterEventSource type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredClusterEventSourceInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FeedsV1alpha1().ClusterEventSources().List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FeedsV1alpha1().ClusterEventSources().Watch(options) - }, - }, - &feeds_v1alpha1.ClusterEventSource{}, - resyncPeriod, - indexers, - ) -} - -func (f *clusterEventSourceInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredClusterEventSourceInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *clusterEventSourceInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&feeds_v1alpha1.ClusterEventSource{}, f.defaultInformer) -} - -func (f *clusterEventSourceInformer) Lister() v1alpha1.ClusterEventSourceLister { - return v1alpha1.NewClusterEventSourceLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/feeds/v1alpha1/clustereventtype.go b/pkg/client/informers/externalversions/feeds/v1alpha1/clustereventtype.go deleted file mode 100644 index 16dedce0e0a..00000000000 --- a/pkg/client/informers/externalversions/feeds/v1alpha1/clustereventtype.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - feeds_v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - versioned "github.com/knative/eventing/pkg/client/clientset/versioned" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/knative/eventing/pkg/client/listers/feeds/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// ClusterEventTypeInformer provides access to a shared informer and lister for -// ClusterEventTypes. -type ClusterEventTypeInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.ClusterEventTypeLister -} - -type clusterEventTypeInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// NewClusterEventTypeInformer constructs a new informer for ClusterEventType type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewClusterEventTypeInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredClusterEventTypeInformer(client, resyncPeriod, indexers, nil) -} - -// NewFilteredClusterEventTypeInformer constructs a new informer for ClusterEventType type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredClusterEventTypeInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FeedsV1alpha1().ClusterEventTypes().List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FeedsV1alpha1().ClusterEventTypes().Watch(options) - }, - }, - &feeds_v1alpha1.ClusterEventType{}, - resyncPeriod, - indexers, - ) -} - -func (f *clusterEventTypeInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredClusterEventTypeInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *clusterEventTypeInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&feeds_v1alpha1.ClusterEventType{}, f.defaultInformer) -} - -func (f *clusterEventTypeInformer) Lister() v1alpha1.ClusterEventTypeLister { - return v1alpha1.NewClusterEventTypeLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/feeds/v1alpha1/eventsource.go b/pkg/client/informers/externalversions/feeds/v1alpha1/eventsource.go deleted file mode 100644 index 8a08e9918ec..00000000000 --- a/pkg/client/informers/externalversions/feeds/v1alpha1/eventsource.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - feeds_v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - versioned "github.com/knative/eventing/pkg/client/clientset/versioned" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/knative/eventing/pkg/client/listers/feeds/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// EventSourceInformer provides access to a shared informer and lister for -// EventSources. -type EventSourceInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.EventSourceLister -} - -type eventSourceInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewEventSourceInformer constructs a new informer for EventSource type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewEventSourceInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredEventSourceInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredEventSourceInformer constructs a new informer for EventSource type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredEventSourceInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FeedsV1alpha1().EventSources(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FeedsV1alpha1().EventSources(namespace).Watch(options) - }, - }, - &feeds_v1alpha1.EventSource{}, - resyncPeriod, - indexers, - ) -} - -func (f *eventSourceInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredEventSourceInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *eventSourceInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&feeds_v1alpha1.EventSource{}, f.defaultInformer) -} - -func (f *eventSourceInformer) Lister() v1alpha1.EventSourceLister { - return v1alpha1.NewEventSourceLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/feeds/v1alpha1/eventtype.go b/pkg/client/informers/externalversions/feeds/v1alpha1/eventtype.go deleted file mode 100644 index a31e490ed13..00000000000 --- a/pkg/client/informers/externalversions/feeds/v1alpha1/eventtype.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - feeds_v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - versioned "github.com/knative/eventing/pkg/client/clientset/versioned" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/knative/eventing/pkg/client/listers/feeds/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// EventTypeInformer provides access to a shared informer and lister for -// EventTypes. -type EventTypeInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.EventTypeLister -} - -type eventTypeInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewEventTypeInformer constructs a new informer for EventType type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewEventTypeInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredEventTypeInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredEventTypeInformer constructs a new informer for EventType type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredEventTypeInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FeedsV1alpha1().EventTypes(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FeedsV1alpha1().EventTypes(namespace).Watch(options) - }, - }, - &feeds_v1alpha1.EventType{}, - resyncPeriod, - indexers, - ) -} - -func (f *eventTypeInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredEventTypeInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *eventTypeInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&feeds_v1alpha1.EventType{}, f.defaultInformer) -} - -func (f *eventTypeInformer) Lister() v1alpha1.EventTypeLister { - return v1alpha1.NewEventTypeLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/feeds/v1alpha1/feed.go b/pkg/client/informers/externalversions/feeds/v1alpha1/feed.go deleted file mode 100644 index a4397c4c71e..00000000000 --- a/pkg/client/informers/externalversions/feeds/v1alpha1/feed.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - feeds_v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - versioned "github.com/knative/eventing/pkg/client/clientset/versioned" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/knative/eventing/pkg/client/listers/feeds/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// FeedInformer provides access to a shared informer and lister for -// Feeds. -type FeedInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.FeedLister -} - -type feedInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewFeedInformer constructs a new informer for Feed type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFeedInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredFeedInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredFeedInformer constructs a new informer for Feed type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredFeedInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FeedsV1alpha1().Feeds(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FeedsV1alpha1().Feeds(namespace).Watch(options) - }, - }, - &feeds_v1alpha1.Feed{}, - resyncPeriod, - indexers, - ) -} - -func (f *feedInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredFeedInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *feedInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&feeds_v1alpha1.Feed{}, f.defaultInformer) -} - -func (f *feedInformer) Lister() v1alpha1.FeedLister { - return v1alpha1.NewFeedLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/feeds/v1alpha1/interface.go b/pkg/client/informers/externalversions/feeds/v1alpha1/interface.go deleted file mode 100644 index b514cc2f3d7..00000000000 --- a/pkg/client/informers/externalversions/feeds/v1alpha1/interface.go +++ /dev/null @@ -1,73 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // ClusterEventSources returns a ClusterEventSourceInformer. - ClusterEventSources() ClusterEventSourceInformer - // ClusterEventTypes returns a ClusterEventTypeInformer. - ClusterEventTypes() ClusterEventTypeInformer - // EventSources returns a EventSourceInformer. - EventSources() EventSourceInformer - // EventTypes returns a EventTypeInformer. - EventTypes() EventTypeInformer - // Feeds returns a FeedInformer. - Feeds() FeedInformer -} - -type version struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// ClusterEventSources returns a ClusterEventSourceInformer. -func (v *version) ClusterEventSources() ClusterEventSourceInformer { - return &clusterEventSourceInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} -} - -// ClusterEventTypes returns a ClusterEventTypeInformer. -func (v *version) ClusterEventTypes() ClusterEventTypeInformer { - return &clusterEventTypeInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} -} - -// EventSources returns a EventSourceInformer. -func (v *version) EventSources() EventSourceInformer { - return &eventSourceInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// EventTypes returns a EventTypeInformer. -func (v *version) EventTypes() EventTypeInformer { - return &eventTypeInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} - -// Feeds returns a FeedInformer. -func (v *version) Feeds() FeedInformer { - return &feedInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/client/informers/externalversions/flows/interface.go b/pkg/client/informers/externalversions/flows/interface.go deleted file mode 100644 index 5534891ac8e..00000000000 --- a/pkg/client/informers/externalversions/flows/interface.go +++ /dev/null @@ -1,46 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package flows - -import ( - v1alpha1 "github.com/knative/eventing/pkg/client/informers/externalversions/flows/v1alpha1" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to each of this group's versions. -type Interface interface { - // V1alpha1 provides access to shared informers for resources in V1alpha1. - V1alpha1() v1alpha1.Interface -} - -type group struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// V1alpha1 returns a new v1alpha1.Interface. -func (g *group) V1alpha1() v1alpha1.Interface { - return v1alpha1.New(g.factory, g.namespace, g.tweakListOptions) -} diff --git a/pkg/client/informers/externalversions/flows/v1alpha1/flow.go b/pkg/client/informers/externalversions/flows/v1alpha1/flow.go deleted file mode 100644 index 19a5b8cf9ce..00000000000 --- a/pkg/client/informers/externalversions/flows/v1alpha1/flow.go +++ /dev/null @@ -1,89 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - time "time" - - flows_v1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - versioned "github.com/knative/eventing/pkg/client/clientset/versioned" - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" - v1alpha1 "github.com/knative/eventing/pkg/client/listers/flows/v1alpha1" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - runtime "k8s.io/apimachinery/pkg/runtime" - watch "k8s.io/apimachinery/pkg/watch" - cache "k8s.io/client-go/tools/cache" -) - -// FlowInformer provides access to a shared informer and lister for -// Flows. -type FlowInformer interface { - Informer() cache.SharedIndexInformer - Lister() v1alpha1.FlowLister -} - -type flowInformer struct { - factory internalinterfaces.SharedInformerFactory - tweakListOptions internalinterfaces.TweakListOptionsFunc - namespace string -} - -// NewFlowInformer constructs a new informer for Flow type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFlowInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { - return NewFilteredFlowInformer(client, namespace, resyncPeriod, indexers, nil) -} - -// NewFilteredFlowInformer constructs a new informer for Flow type. -// Always prefer using an informer factory to get a shared informer instead of getting an independent -// one. This reduces memory footprint and number of connections to the server. -func NewFilteredFlowInformer(client versioned.Interface, namespace string, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { - return cache.NewSharedIndexInformer( - &cache.ListWatch{ - ListFunc: func(options v1.ListOptions) (runtime.Object, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FlowsV1alpha1().Flows(namespace).List(options) - }, - WatchFunc: func(options v1.ListOptions) (watch.Interface, error) { - if tweakListOptions != nil { - tweakListOptions(&options) - } - return client.FlowsV1alpha1().Flows(namespace).Watch(options) - }, - }, - &flows_v1alpha1.Flow{}, - resyncPeriod, - indexers, - ) -} - -func (f *flowInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { - return NewFilteredFlowInformer(client, f.namespace, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) -} - -func (f *flowInformer) Informer() cache.SharedIndexInformer { - return f.factory.InformerFor(&flows_v1alpha1.Flow{}, f.defaultInformer) -} - -func (f *flowInformer) Lister() v1alpha1.FlowLister { - return v1alpha1.NewFlowLister(f.Informer().GetIndexer()) -} diff --git a/pkg/client/informers/externalversions/flows/v1alpha1/interface.go b/pkg/client/informers/externalversions/flows/v1alpha1/interface.go deleted file mode 100644 index dceacfb7090..00000000000 --- a/pkg/client/informers/externalversions/flows/v1alpha1/interface.go +++ /dev/null @@ -1,45 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by informer-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - internalinterfaces "github.com/knative/eventing/pkg/client/informers/externalversions/internalinterfaces" -) - -// Interface provides access to all the informers in this group version. -type Interface interface { - // Flows returns a FlowInformer. - Flows() FlowInformer -} - -type version struct { - factory internalinterfaces.SharedInformerFactory - namespace string - tweakListOptions internalinterfaces.TweakListOptionsFunc -} - -// New returns a new Interface. -func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { - return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} -} - -// Flows returns a FlowInformer. -func (v *version) Flows() FlowInformer { - return &flowInformer{factory: v.factory, namespace: v.namespace, tweakListOptions: v.tweakListOptions} -} diff --git a/pkg/client/informers/externalversions/generic.go b/pkg/client/informers/externalversions/generic.go index 78530117deb..eb07d8f78ef 100644 --- a/pkg/client/informers/externalversions/generic.go +++ b/pkg/client/informers/externalversions/generic.go @@ -21,10 +21,7 @@ package externalversions import ( "fmt" - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - eventing_v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - feeds_v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - flows_v1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" + v1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" schema "k8s.io/apimachinery/pkg/runtime/schema" cache "k8s.io/client-go/tools/cache" ) @@ -55,40 +52,14 @@ func (f *genericInformer) Lister() cache.GenericLister { // TODO extend this to unknown resources with a client pool func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { switch resource { - // Group=channels.knative.dev, Version=v1alpha1 - case v1alpha1.SchemeGroupVersion.WithResource("buses"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Channels().V1alpha1().Buses().Informer()}, nil + // Group=eventing.knative.dev, Version=v1alpha1 case v1alpha1.SchemeGroupVersion.WithResource("channels"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Channels().V1alpha1().Channels().Informer()}, nil - case v1alpha1.SchemeGroupVersion.WithResource("clusterbuses"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Channels().V1alpha1().ClusterBuses().Informer()}, nil - case v1alpha1.SchemeGroupVersion.WithResource("subscriptions"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Channels().V1alpha1().Subscriptions().Informer()}, nil - - // Group=eventing.knative.dev, Version=v1alpha1 - case eventing_v1alpha1.SchemeGroupVersion.WithResource("channels"): return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().Channels().Informer()}, nil - case eventing_v1alpha1.SchemeGroupVersion.WithResource("clusterchannelprovisioners"): + case v1alpha1.SchemeGroupVersion.WithResource("clusterchannelprovisioners"): return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().ClusterChannelProvisioners().Informer()}, nil - case eventing_v1alpha1.SchemeGroupVersion.WithResource("subscriptions"): + case v1alpha1.SchemeGroupVersion.WithResource("subscriptions"): return &genericInformer{resource: resource.GroupResource(), informer: f.Eventing().V1alpha1().Subscriptions().Informer()}, nil - // Group=feeds.knative.dev, Version=v1alpha1 - case feeds_v1alpha1.SchemeGroupVersion.WithResource("clustereventsources"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Feeds().V1alpha1().ClusterEventSources().Informer()}, nil - case feeds_v1alpha1.SchemeGroupVersion.WithResource("clustereventtypes"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Feeds().V1alpha1().ClusterEventTypes().Informer()}, nil - case feeds_v1alpha1.SchemeGroupVersion.WithResource("eventsources"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Feeds().V1alpha1().EventSources().Informer()}, nil - case feeds_v1alpha1.SchemeGroupVersion.WithResource("eventtypes"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Feeds().V1alpha1().EventTypes().Informer()}, nil - case feeds_v1alpha1.SchemeGroupVersion.WithResource("feeds"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Feeds().V1alpha1().Feeds().Informer()}, nil - - // Group=flows.knative.dev, Version=v1alpha1 - case flows_v1alpha1.SchemeGroupVersion.WithResource("flows"): - return &genericInformer{resource: resource.GroupResource(), informer: f.Flows().V1alpha1().Flows().Informer()}, nil - } return nil, fmt.Errorf("no informer found for %v", resource) diff --git a/pkg/client/listers/channels/v1alpha1/bus.go b/pkg/client/listers/channels/v1alpha1/bus.go deleted file mode 100644 index 52d0d164e29..00000000000 --- a/pkg/client/listers/channels/v1alpha1/bus.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// BusLister helps list Buses. -type BusLister interface { - // List lists all Buses in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.Bus, err error) - // Buses returns an object that can list and get Buses. - Buses(namespace string) BusNamespaceLister - BusListerExpansion -} - -// busLister implements the BusLister interface. -type busLister struct { - indexer cache.Indexer -} - -// NewBusLister returns a new BusLister. -func NewBusLister(indexer cache.Indexer) BusLister { - return &busLister{indexer: indexer} -} - -// List lists all Buses in the indexer. -func (s *busLister) List(selector labels.Selector) (ret []*v1alpha1.Bus, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Bus)) - }) - return ret, err -} - -// Buses returns an object that can list and get Buses. -func (s *busLister) Buses(namespace string) BusNamespaceLister { - return busNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// BusNamespaceLister helps list and get Buses. -type BusNamespaceLister interface { - // List lists all Buses in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha1.Bus, err error) - // Get retrieves the Bus from the indexer for a given namespace and name. - Get(name string) (*v1alpha1.Bus, error) - BusNamespaceListerExpansion -} - -// busNamespaceLister implements the BusNamespaceLister -// interface. -type busNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Buses in the indexer for a given namespace. -func (s busNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Bus, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Bus)) - }) - return ret, err -} - -// Get retrieves the Bus from the indexer for a given namespace and name. -func (s busNamespaceLister) Get(name string) (*v1alpha1.Bus, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("bus"), name) - } - return obj.(*v1alpha1.Bus), nil -} diff --git a/pkg/client/listers/channels/v1alpha1/channel.go b/pkg/client/listers/channels/v1alpha1/channel.go deleted file mode 100644 index 3ea15686cfa..00000000000 --- a/pkg/client/listers/channels/v1alpha1/channel.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// ChannelLister helps list Channels. -type ChannelLister interface { - // List lists all Channels in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.Channel, err error) - // Channels returns an object that can list and get Channels. - Channels(namespace string) ChannelNamespaceLister - ChannelListerExpansion -} - -// channelLister implements the ChannelLister interface. -type channelLister struct { - indexer cache.Indexer -} - -// NewChannelLister returns a new ChannelLister. -func NewChannelLister(indexer cache.Indexer) ChannelLister { - return &channelLister{indexer: indexer} -} - -// List lists all Channels in the indexer. -func (s *channelLister) List(selector labels.Selector) (ret []*v1alpha1.Channel, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Channel)) - }) - return ret, err -} - -// Channels returns an object that can list and get Channels. -func (s *channelLister) Channels(namespace string) ChannelNamespaceLister { - return channelNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// ChannelNamespaceLister helps list and get Channels. -type ChannelNamespaceLister interface { - // List lists all Channels in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha1.Channel, err error) - // Get retrieves the Channel from the indexer for a given namespace and name. - Get(name string) (*v1alpha1.Channel, error) - ChannelNamespaceListerExpansion -} - -// channelNamespaceLister implements the ChannelNamespaceLister -// interface. -type channelNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Channels in the indexer for a given namespace. -func (s channelNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Channel, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Channel)) - }) - return ret, err -} - -// Get retrieves the Channel from the indexer for a given namespace and name. -func (s channelNamespaceLister) Get(name string) (*v1alpha1.Channel, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("channel"), name) - } - return obj.(*v1alpha1.Channel), nil -} diff --git a/pkg/client/listers/channels/v1alpha1/clusterbus.go b/pkg/client/listers/channels/v1alpha1/clusterbus.go deleted file mode 100644 index 5415692f299..00000000000 --- a/pkg/client/listers/channels/v1alpha1/clusterbus.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// ClusterBusLister helps list ClusterBuses. -type ClusterBusLister interface { - // List lists all ClusterBuses in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.ClusterBus, err error) - // Get retrieves the ClusterBus from the index for a given name. - Get(name string) (*v1alpha1.ClusterBus, error) - ClusterBusListerExpansion -} - -// clusterBusLister implements the ClusterBusLister interface. -type clusterBusLister struct { - indexer cache.Indexer -} - -// NewClusterBusLister returns a new ClusterBusLister. -func NewClusterBusLister(indexer cache.Indexer) ClusterBusLister { - return &clusterBusLister{indexer: indexer} -} - -// List lists all ClusterBuses in the indexer. -func (s *clusterBusLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterBus, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ClusterBus)) - }) - return ret, err -} - -// Get retrieves the ClusterBus from the index for a given name. -func (s *clusterBusLister) Get(name string) (*v1alpha1.ClusterBus, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("clusterbus"), name) - } - return obj.(*v1alpha1.ClusterBus), nil -} diff --git a/pkg/client/listers/channels/v1alpha1/expansion_generated.go b/pkg/client/listers/channels/v1alpha1/expansion_generated.go deleted file mode 100644 index 96d1fd82104..00000000000 --- a/pkg/client/listers/channels/v1alpha1/expansion_generated.go +++ /dev/null @@ -1,47 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -// BusListerExpansion allows custom methods to be added to -// BusLister. -type BusListerExpansion interface{} - -// BusNamespaceListerExpansion allows custom methods to be added to -// BusNamespaceLister. -type BusNamespaceListerExpansion interface{} - -// ChannelListerExpansion allows custom methods to be added to -// ChannelLister. -type ChannelListerExpansion interface{} - -// ChannelNamespaceListerExpansion allows custom methods to be added to -// ChannelNamespaceLister. -type ChannelNamespaceListerExpansion interface{} - -// ClusterBusListerExpansion allows custom methods to be added to -// ClusterBusLister. -type ClusterBusListerExpansion interface{} - -// SubscriptionListerExpansion allows custom methods to be added to -// SubscriptionLister. -type SubscriptionListerExpansion interface{} - -// SubscriptionNamespaceListerExpansion allows custom methods to be added to -// SubscriptionNamespaceLister. -type SubscriptionNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/channels/v1alpha1/subscription.go b/pkg/client/listers/channels/v1alpha1/subscription.go deleted file mode 100644 index b910f451ede..00000000000 --- a/pkg/client/listers/channels/v1alpha1/subscription.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// SubscriptionLister helps list Subscriptions. -type SubscriptionLister interface { - // List lists all Subscriptions in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.Subscription, err error) - // Subscriptions returns an object that can list and get Subscriptions. - Subscriptions(namespace string) SubscriptionNamespaceLister - SubscriptionListerExpansion -} - -// subscriptionLister implements the SubscriptionLister interface. -type subscriptionLister struct { - indexer cache.Indexer -} - -// NewSubscriptionLister returns a new SubscriptionLister. -func NewSubscriptionLister(indexer cache.Indexer) SubscriptionLister { - return &subscriptionLister{indexer: indexer} -} - -// List lists all Subscriptions in the indexer. -func (s *subscriptionLister) List(selector labels.Selector) (ret []*v1alpha1.Subscription, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Subscription)) - }) - return ret, err -} - -// Subscriptions returns an object that can list and get Subscriptions. -func (s *subscriptionLister) Subscriptions(namespace string) SubscriptionNamespaceLister { - return subscriptionNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// SubscriptionNamespaceLister helps list and get Subscriptions. -type SubscriptionNamespaceLister interface { - // List lists all Subscriptions in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha1.Subscription, err error) - // Get retrieves the Subscription from the indexer for a given namespace and name. - Get(name string) (*v1alpha1.Subscription, error) - SubscriptionNamespaceListerExpansion -} - -// subscriptionNamespaceLister implements the SubscriptionNamespaceLister -// interface. -type subscriptionNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Subscriptions in the indexer for a given namespace. -func (s subscriptionNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Subscription, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Subscription)) - }) - return ret, err -} - -// Get retrieves the Subscription from the indexer for a given namespace and name. -func (s subscriptionNamespaceLister) Get(name string) (*v1alpha1.Subscription, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("subscription"), name) - } - return obj.(*v1alpha1.Subscription), nil -} diff --git a/pkg/client/listers/feeds/v1alpha1/clustereventsource.go b/pkg/client/listers/feeds/v1alpha1/clustereventsource.go deleted file mode 100644 index 53211b2539c..00000000000 --- a/pkg/client/listers/feeds/v1alpha1/clustereventsource.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// ClusterEventSourceLister helps list ClusterEventSources. -type ClusterEventSourceLister interface { - // List lists all ClusterEventSources in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.ClusterEventSource, err error) - // Get retrieves the ClusterEventSource from the index for a given name. - Get(name string) (*v1alpha1.ClusterEventSource, error) - ClusterEventSourceListerExpansion -} - -// clusterEventSourceLister implements the ClusterEventSourceLister interface. -type clusterEventSourceLister struct { - indexer cache.Indexer -} - -// NewClusterEventSourceLister returns a new ClusterEventSourceLister. -func NewClusterEventSourceLister(indexer cache.Indexer) ClusterEventSourceLister { - return &clusterEventSourceLister{indexer: indexer} -} - -// List lists all ClusterEventSources in the indexer. -func (s *clusterEventSourceLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterEventSource, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ClusterEventSource)) - }) - return ret, err -} - -// Get retrieves the ClusterEventSource from the index for a given name. -func (s *clusterEventSourceLister) Get(name string) (*v1alpha1.ClusterEventSource, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("clustereventsource"), name) - } - return obj.(*v1alpha1.ClusterEventSource), nil -} diff --git a/pkg/client/listers/feeds/v1alpha1/clustereventtype.go b/pkg/client/listers/feeds/v1alpha1/clustereventtype.go deleted file mode 100644 index 1fd170b69af..00000000000 --- a/pkg/client/listers/feeds/v1alpha1/clustereventtype.go +++ /dev/null @@ -1,65 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// ClusterEventTypeLister helps list ClusterEventTypes. -type ClusterEventTypeLister interface { - // List lists all ClusterEventTypes in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.ClusterEventType, err error) - // Get retrieves the ClusterEventType from the index for a given name. - Get(name string) (*v1alpha1.ClusterEventType, error) - ClusterEventTypeListerExpansion -} - -// clusterEventTypeLister implements the ClusterEventTypeLister interface. -type clusterEventTypeLister struct { - indexer cache.Indexer -} - -// NewClusterEventTypeLister returns a new ClusterEventTypeLister. -func NewClusterEventTypeLister(indexer cache.Indexer) ClusterEventTypeLister { - return &clusterEventTypeLister{indexer: indexer} -} - -// List lists all ClusterEventTypes in the indexer. -func (s *clusterEventTypeLister) List(selector labels.Selector) (ret []*v1alpha1.ClusterEventType, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.ClusterEventType)) - }) - return ret, err -} - -// Get retrieves the ClusterEventType from the index for a given name. -func (s *clusterEventTypeLister) Get(name string) (*v1alpha1.ClusterEventType, error) { - obj, exists, err := s.indexer.GetByKey(name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("clustereventtype"), name) - } - return obj.(*v1alpha1.ClusterEventType), nil -} diff --git a/pkg/client/listers/feeds/v1alpha1/eventsource.go b/pkg/client/listers/feeds/v1alpha1/eventsource.go deleted file mode 100644 index 15ef8d8fc0b..00000000000 --- a/pkg/client/listers/feeds/v1alpha1/eventsource.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// EventSourceLister helps list EventSources. -type EventSourceLister interface { - // List lists all EventSources in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.EventSource, err error) - // EventSources returns an object that can list and get EventSources. - EventSources(namespace string) EventSourceNamespaceLister - EventSourceListerExpansion -} - -// eventSourceLister implements the EventSourceLister interface. -type eventSourceLister struct { - indexer cache.Indexer -} - -// NewEventSourceLister returns a new EventSourceLister. -func NewEventSourceLister(indexer cache.Indexer) EventSourceLister { - return &eventSourceLister{indexer: indexer} -} - -// List lists all EventSources in the indexer. -func (s *eventSourceLister) List(selector labels.Selector) (ret []*v1alpha1.EventSource, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.EventSource)) - }) - return ret, err -} - -// EventSources returns an object that can list and get EventSources. -func (s *eventSourceLister) EventSources(namespace string) EventSourceNamespaceLister { - return eventSourceNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// EventSourceNamespaceLister helps list and get EventSources. -type EventSourceNamespaceLister interface { - // List lists all EventSources in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha1.EventSource, err error) - // Get retrieves the EventSource from the indexer for a given namespace and name. - Get(name string) (*v1alpha1.EventSource, error) - EventSourceNamespaceListerExpansion -} - -// eventSourceNamespaceLister implements the EventSourceNamespaceLister -// interface. -type eventSourceNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all EventSources in the indexer for a given namespace. -func (s eventSourceNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.EventSource, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.EventSource)) - }) - return ret, err -} - -// Get retrieves the EventSource from the indexer for a given namespace and name. -func (s eventSourceNamespaceLister) Get(name string) (*v1alpha1.EventSource, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("eventsource"), name) - } - return obj.(*v1alpha1.EventSource), nil -} diff --git a/pkg/client/listers/feeds/v1alpha1/eventtype.go b/pkg/client/listers/feeds/v1alpha1/eventtype.go deleted file mode 100644 index 9dda5741959..00000000000 --- a/pkg/client/listers/feeds/v1alpha1/eventtype.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// EventTypeLister helps list EventTypes. -type EventTypeLister interface { - // List lists all EventTypes in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.EventType, err error) - // EventTypes returns an object that can list and get EventTypes. - EventTypes(namespace string) EventTypeNamespaceLister - EventTypeListerExpansion -} - -// eventTypeLister implements the EventTypeLister interface. -type eventTypeLister struct { - indexer cache.Indexer -} - -// NewEventTypeLister returns a new EventTypeLister. -func NewEventTypeLister(indexer cache.Indexer) EventTypeLister { - return &eventTypeLister{indexer: indexer} -} - -// List lists all EventTypes in the indexer. -func (s *eventTypeLister) List(selector labels.Selector) (ret []*v1alpha1.EventType, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.EventType)) - }) - return ret, err -} - -// EventTypes returns an object that can list and get EventTypes. -func (s *eventTypeLister) EventTypes(namespace string) EventTypeNamespaceLister { - return eventTypeNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// EventTypeNamespaceLister helps list and get EventTypes. -type EventTypeNamespaceLister interface { - // List lists all EventTypes in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha1.EventType, err error) - // Get retrieves the EventType from the indexer for a given namespace and name. - Get(name string) (*v1alpha1.EventType, error) - EventTypeNamespaceListerExpansion -} - -// eventTypeNamespaceLister implements the EventTypeNamespaceLister -// interface. -type eventTypeNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all EventTypes in the indexer for a given namespace. -func (s eventTypeNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.EventType, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.EventType)) - }) - return ret, err -} - -// Get retrieves the EventType from the indexer for a given namespace and name. -func (s eventTypeNamespaceLister) Get(name string) (*v1alpha1.EventType, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("eventtype"), name) - } - return obj.(*v1alpha1.EventType), nil -} diff --git a/pkg/client/listers/feeds/v1alpha1/expansion_generated.go b/pkg/client/listers/feeds/v1alpha1/expansion_generated.go deleted file mode 100644 index 6a7bb9bd92f..00000000000 --- a/pkg/client/listers/feeds/v1alpha1/expansion_generated.go +++ /dev/null @@ -1,51 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -// ClusterEventSourceListerExpansion allows custom methods to be added to -// ClusterEventSourceLister. -type ClusterEventSourceListerExpansion interface{} - -// ClusterEventTypeListerExpansion allows custom methods to be added to -// ClusterEventTypeLister. -type ClusterEventTypeListerExpansion interface{} - -// EventSourceListerExpansion allows custom methods to be added to -// EventSourceLister. -type EventSourceListerExpansion interface{} - -// EventSourceNamespaceListerExpansion allows custom methods to be added to -// EventSourceNamespaceLister. -type EventSourceNamespaceListerExpansion interface{} - -// EventTypeListerExpansion allows custom methods to be added to -// EventTypeLister. -type EventTypeListerExpansion interface{} - -// EventTypeNamespaceListerExpansion allows custom methods to be added to -// EventTypeNamespaceLister. -type EventTypeNamespaceListerExpansion interface{} - -// FeedListerExpansion allows custom methods to be added to -// FeedLister. -type FeedListerExpansion interface{} - -// FeedNamespaceListerExpansion allows custom methods to be added to -// FeedNamespaceLister. -type FeedNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/feeds/v1alpha1/feed.go b/pkg/client/listers/feeds/v1alpha1/feed.go deleted file mode 100644 index bb55a0740b9..00000000000 --- a/pkg/client/listers/feeds/v1alpha1/feed.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// FeedLister helps list Feeds. -type FeedLister interface { - // List lists all Feeds in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.Feed, err error) - // Feeds returns an object that can list and get Feeds. - Feeds(namespace string) FeedNamespaceLister - FeedListerExpansion -} - -// feedLister implements the FeedLister interface. -type feedLister struct { - indexer cache.Indexer -} - -// NewFeedLister returns a new FeedLister. -func NewFeedLister(indexer cache.Indexer) FeedLister { - return &feedLister{indexer: indexer} -} - -// List lists all Feeds in the indexer. -func (s *feedLister) List(selector labels.Selector) (ret []*v1alpha1.Feed, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Feed)) - }) - return ret, err -} - -// Feeds returns an object that can list and get Feeds. -func (s *feedLister) Feeds(namespace string) FeedNamespaceLister { - return feedNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// FeedNamespaceLister helps list and get Feeds. -type FeedNamespaceLister interface { - // List lists all Feeds in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha1.Feed, err error) - // Get retrieves the Feed from the indexer for a given namespace and name. - Get(name string) (*v1alpha1.Feed, error) - FeedNamespaceListerExpansion -} - -// feedNamespaceLister implements the FeedNamespaceLister -// interface. -type feedNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Feeds in the indexer for a given namespace. -func (s feedNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Feed, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Feed)) - }) - return ret, err -} - -// Get retrieves the Feed from the indexer for a given namespace and name. -func (s feedNamespaceLister) Get(name string) (*v1alpha1.Feed, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("feed"), name) - } - return obj.(*v1alpha1.Feed), nil -} diff --git a/pkg/client/listers/flows/v1alpha1/expansion_generated.go b/pkg/client/listers/flows/v1alpha1/expansion_generated.go deleted file mode 100644 index cd115a41fa4..00000000000 --- a/pkg/client/listers/flows/v1alpha1/expansion_generated.go +++ /dev/null @@ -1,27 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -// FlowListerExpansion allows custom methods to be added to -// FlowLister. -type FlowListerExpansion interface{} - -// FlowNamespaceListerExpansion allows custom methods to be added to -// FlowNamespaceLister. -type FlowNamespaceListerExpansion interface{} diff --git a/pkg/client/listers/flows/v1alpha1/flow.go b/pkg/client/listers/flows/v1alpha1/flow.go deleted file mode 100644 index 7358d8f0d5f..00000000000 --- a/pkg/client/listers/flows/v1alpha1/flow.go +++ /dev/null @@ -1,94 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// Code generated by lister-gen. DO NOT EDIT. - -package v1alpha1 - -import ( - v1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/client-go/tools/cache" -) - -// FlowLister helps list Flows. -type FlowLister interface { - // List lists all Flows in the indexer. - List(selector labels.Selector) (ret []*v1alpha1.Flow, err error) - // Flows returns an object that can list and get Flows. - Flows(namespace string) FlowNamespaceLister - FlowListerExpansion -} - -// flowLister implements the FlowLister interface. -type flowLister struct { - indexer cache.Indexer -} - -// NewFlowLister returns a new FlowLister. -func NewFlowLister(indexer cache.Indexer) FlowLister { - return &flowLister{indexer: indexer} -} - -// List lists all Flows in the indexer. -func (s *flowLister) List(selector labels.Selector) (ret []*v1alpha1.Flow, err error) { - err = cache.ListAll(s.indexer, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Flow)) - }) - return ret, err -} - -// Flows returns an object that can list and get Flows. -func (s *flowLister) Flows(namespace string) FlowNamespaceLister { - return flowNamespaceLister{indexer: s.indexer, namespace: namespace} -} - -// FlowNamespaceLister helps list and get Flows. -type FlowNamespaceLister interface { - // List lists all Flows in the indexer for a given namespace. - List(selector labels.Selector) (ret []*v1alpha1.Flow, err error) - // Get retrieves the Flow from the indexer for a given namespace and name. - Get(name string) (*v1alpha1.Flow, error) - FlowNamespaceListerExpansion -} - -// flowNamespaceLister implements the FlowNamespaceLister -// interface. -type flowNamespaceLister struct { - indexer cache.Indexer - namespace string -} - -// List lists all Flows in the indexer for a given namespace. -func (s flowNamespaceLister) List(selector labels.Selector) (ret []*v1alpha1.Flow, err error) { - err = cache.ListAllByNamespace(s.indexer, s.namespace, selector, func(m interface{}) { - ret = append(ret, m.(*v1alpha1.Flow)) - }) - return ret, err -} - -// Get retrieves the Flow from the indexer for a given namespace and name. -func (s flowNamespaceLister) Get(name string) (*v1alpha1.Flow, error) { - obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) - if err != nil { - return nil, err - } - if !exists { - return nil, errors.NewNotFound(v1alpha1.Resource("flow"), name) - } - return obj.(*v1alpha1.Flow), nil -} diff --git a/pkg/controller/bus/controller.go b/pkg/controller/bus/controller.go deleted file mode 100644 index fa5b80a77a7..00000000000 --- a/pkg/controller/bus/controller.go +++ /dev/null @@ -1,726 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package bus - -import ( - "fmt" - "reflect" - "time" - - "github.com/golang/glog" - "github.com/knative/eventing/pkg/buses" - "github.com/knative/eventing/pkg/controller" - "github.com/knative/eventing/pkg/system" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - kubeinformers "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" - appslisters "k8s.io/client-go/listers/apps/v1" - corelisters "k8s.io/client-go/listers/core/v1" - rbaclisters "k8s.io/client-go/listers/rbac/v1beta1" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/workqueue" - - clientset "github.com/knative/eventing/pkg/client/clientset/versioned" - channelscheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - informers "github.com/knative/eventing/pkg/client/informers/externalversions" - listers "github.com/knative/eventing/pkg/client/listers/channels/v1alpha1" - sharedclientset "github.com/knative/pkg/client/clientset/versioned" - sharedinformers "github.com/knative/pkg/client/informers/externalversions" - - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "github.com/knative/eventing/pkg/controller/util" -) - -const ( - controllerAgentName = "bus-controller" - serviceAccountName = "bus-operator" -) - -const ( - // SuccessSynced is used as part of the Event 'reason' when a Bus is synced - SuccessSynced = "Synced" - // ErrResourceExists is used as part of the Event 'reason' when a Bus fails - // to sync due to a Service of the same name already existing. - ErrResourceExists = "ErrResourceExists" - - // MessageResourceExists is the message used for Events when a resource - // fails to sync due to a Service already existing - MessageResourceExists = "Resource %q already exists and is not managed by Bus" - // MessageResourceSynced is the message used for an Event fired when a Bus - // is synced successfully - MessageResourceSynced = "Bus synced successfully" -) - -const ( - // ServiceSynced is used as part of the condition reason when the bus (k8s) service is successfully created. - ServiceSynced = "ServiceSynced" - // ServiceError is used as part of the condition reason when the bus (k8s) service creation failed. - ServiceError = "ServiceError" - // DeploymentSynced is used as part of the condition reason when a bus deployment is successfully created. - DeploymentSynced = "DeploymentSynced" - // DeploymentError is used as part of the condition reason when a bus deployment creation failed. - DeploymentError = "DeploymentError" -) - -// Controller is the controller implementation for Bus resources -type Controller struct { - // kubeclientset is a standard kubernetes clientset - kubeclientset kubernetes.Interface - // busclientset is a clientset for our own API group - busclientset clientset.Interface - - deploymentsLister appslisters.DeploymentLister - deploymentsSynced cache.InformerSynced - servicesLister corelisters.ServiceLister - servicesSynced cache.InformerSynced - serviceAccountsLister corelisters.ServiceAccountLister - serviceAccountsSynced cache.InformerSynced - clusterRoleBindingsLister rbaclisters.ClusterRoleBindingLister - clusterRoleBindingsSynced cache.InformerSynced - busesLister listers.BusLister - busesSynced cache.InformerSynced - - // workqueue is a rate limited work queue. This is used to queue work to be - // processed instead of performing it as soon as a change happens. This - // means we can ensure we only process a fixed amount of resources at a - // time, and makes it easy to ensure we are never processing the same item - // simultaneously in two different workers. - workqueue workqueue.RateLimitingInterface - // recorder is an event recorder for recording Event resources to the - // Kubernetes API. - recorder record.EventRecorder -} - -// NewController returns a new bus controller -func NewController( - kubeclientset kubernetes.Interface, - busclientset clientset.Interface, - sharedclientset sharedclientset.Interface, - restConfig *rest.Config, - kubeInformerFactory kubeinformers.SharedInformerFactory, - busInformerFactory informers.SharedInformerFactory, - istioInformerFactory sharedinformers.SharedInformerFactory, -) controller.Interface { - - // obtain references to shared index informers for the Bus, Deployment and Service - // types. - busInformer := busInformerFactory.Channels().V1alpha1().Buses() - deploymentInformer := kubeInformerFactory.Apps().V1().Deployments() - serviceInformer := kubeInformerFactory.Core().V1().Services() - serviceAccountInformer := kubeInformerFactory.Core().V1().ServiceAccounts() - clusterRoleBindingInformer := kubeInformerFactory.Rbac().V1beta1().ClusterRoleBindings() - - // Create event broadcaster - // Add bus-controller types to the default Kubernetes Scheme so Events can be - // logged for bus-controller types. - channelscheme.AddToScheme(scheme.Scheme) - glog.V(4).Info("Creating event broadcaster") - eventBroadcaster := record.NewBroadcaster() - eventBroadcaster.StartLogging(glog.Infof) - eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")}) - recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) - - controller := &Controller{ - kubeclientset: kubeclientset, - busclientset: busclientset, - deploymentsLister: deploymentInformer.Lister(), - deploymentsSynced: deploymentInformer.Informer().HasSynced, - servicesLister: serviceInformer.Lister(), - servicesSynced: serviceInformer.Informer().HasSynced, - serviceAccountsLister: serviceAccountInformer.Lister(), - serviceAccountsSynced: serviceAccountInformer.Informer().HasSynced, - clusterRoleBindingsLister: clusterRoleBindingInformer.Lister(), - clusterRoleBindingsSynced: clusterRoleBindingInformer.Informer().HasSynced, - busesLister: busInformer.Lister(), - busesSynced: busInformer.Informer().HasSynced, - workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Buses"), - recorder: recorder, - } - - glog.Info("Setting up event handlers") - // Set up an event handler for when Bus resources change - busInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: controller.enqueueBus, - UpdateFunc: func(old, new interface{}) { - controller.enqueueBus(new) - }, - }) - // Set up an event handler for when Service resources change. This - // handler will lookup the owner of the given Service, and if it is - // owned by a Bus resource will enqueue that Bus resource for - // processing. This way, we don't need to implement custom logic for - // handling Service resources. More info on this pattern: - // https://github.com/kubernetes/community/blob/8cafef897a22026d42f5e5bb3f104febe7e29830/contributors/devel/controllers.md - serviceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: controller.handleObject, - UpdateFunc: func(old, new interface{}) { - newService := new.(*corev1.Service) - oldService := old.(*corev1.Service) - if newService.ResourceVersion == oldService.ResourceVersion { - // Periodic resync will send update events for all known Services. - // Two different versions of the same Service will always have different RVs. - return - } - controller.handleObject(new) - }, - DeleteFunc: controller.handleObject, - }) - - return controller -} - -// Run will set up the event handlers for types we are interested in, as well -// as syncing informer caches and starting workers. It will block until stopCh -// is closed, at which point it will shutdown the workqueue and wait for -// workers to finish processing their current work items. -func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error { - defer runtime.HandleCrash() - defer c.workqueue.ShutDown() - - // Start the informer factories to begin populating the informer caches - glog.Info("Starting Bus controller") - - // Wait for the caches to be synced before starting workers - glog.Info("Waiting for informer caches to sync") - if ok := cache.WaitForCacheSync(stopCh, c.deploymentsSynced, c.servicesSynced, c.busesSynced); !ok { - return fmt.Errorf("failed to wait for caches to sync") - } - - glog.Info("Starting workers") - // Launch two workers to process Bus resources - for i := 0; i < threadiness; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) - } - - glog.Info("Started workers") - <-stopCh - glog.Info("Shutting down workers") - - return nil -} - -// runWorker is a long-running function that will continually call the -// processNextWorkItem function in order to read and process a message on the -// workqueue. -func (c *Controller) runWorker() { - for c.processNextWorkItem() { - } -} - -// processNextWorkItem will read a single work item off the workqueue and -// attempt to process it, by calling the syncHandler. -func (c *Controller) processNextWorkItem() bool { - obj, shutdown := c.workqueue.Get() - - if shutdown { - return false - } - - // We wrap this block in a func so we can defer c.workqueue.Done. - err := func(obj interface{}) error { - // We call Done here so the workqueue knows we have finished - // processing this item. We also must remember to call Forget if we - // do not want this work item being re-queued. For example, we do - // not call Forget if a transient error occurs, instead the item is - // put back on the workqueue and attempted again after a back-off - // period. - defer c.workqueue.Done(obj) - var key string - var ok bool - // We expect strings to come off the workqueue. These are of the - // form namespace/name. We do this as the delayed nature of the - // workqueue means the items in the informer cache may actually be - // more up to date that when the item was initially put onto the - // workqueue. - if key, ok = obj.(string); !ok { - // As the item in the workqueue is actually invalid, we call - // Forget here else we'd go into a loop of attempting to - // process a work item that is invalid. - c.workqueue.Forget(obj) - runtime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) - return nil - } - // Run the syncHandler, passing it the namespace/name string of the - // Bus resource to be synced. - if err := c.syncHandler(key); err != nil { - return fmt.Errorf("error syncing bus '%s': %v", key, err) - } - // Finally, if no error occurs we Forget this item so it does not - // get queued again until another change happens. - c.workqueue.Forget(obj) - glog.Infof("Successfully synced bus '%s'", key) - return nil - }(obj) - - if err != nil { - runtime.HandleError(err) - return true - } - - return true -} - -// syncHandler compares the actual state with the desired, and attempts to -// converge the two. It then updates the Status block of the Bus resource -// with the current status of the resource. -func (c *Controller) syncHandler(key string) error { - // Convert the namespace/name string into a distinct namespace and name - namespace, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - runtime.HandleError(fmt.Errorf("invalid resource key: %s", key)) - return nil - } - - // Get the Bus resource with this namespace/name - bus, err := c.busesLister.Buses(namespace).Get(name) - if err != nil { - // The Bus resource may no longer exist, in which case we stop - // processing. - if errors.IsNotFound(err) { - runtime.HandleError(fmt.Errorf("bus '%s' in work queue no longer exists", key)) - return nil - } - - return err - } - - // NEVER modify objects from the store. It's a read-only, local cache. - // You can use DeepCopy() to make a deep copy of original object and modify this copy - // Or create a copy manually for better performance - busCopy := bus.DeepCopy() - - // Sync Service derived from the Bus - dispatcherService, err := c.syncBusDispatcherService(busCopy) - - if err != nil { - busCopy.Status.Service = nil - serviceCondition := util.NewBusCondition(channelsv1alpha1.BusServiceable, corev1.ConditionFalse, ServiceError, err.Error()) - util.SetBusCondition(&busCopy.Status, *serviceCondition) - c.compareAndUpdateBusStatus(bus, busCopy) - return err - } - - busCopy.Status.Service = &corev1.LocalObjectReference{Name: dispatcherService.Name} - serviceCondition := util.NewBusCondition(channelsv1alpha1.BusServiceable, corev1.ConditionTrue, ServiceSynced, "service successfully synced") - util.SetBusCondition(&busCopy.Status, *serviceCondition) - - // Sync Deployment derived from the Bus - _, err = c.syncBusDispatcherDeployment(busCopy) - - if err != nil { - dispatchCondition := util.NewBusCondition(channelsv1alpha1.BusDispatching, corev1.ConditionFalse, DeploymentError, err.Error()) - util.SetBusCondition(&busCopy.Status, *dispatchCondition) - c.compareAndUpdateBusStatus(bus, busCopy) - return err - } - - dispatchCondition := util.NewBusCondition(channelsv1alpha1.BusDispatching, corev1.ConditionTrue, DeploymentSynced, "deployment successfully synced") - util.SetBusCondition(&busCopy.Status, *dispatchCondition) - - // Sync Deployment derived from the Bus - provisionerDeployment, err := c.syncBusProvisionerDeployment(busCopy) - - if err != nil { - provisionCondition := util.NewBusCondition(channelsv1alpha1.BusProvisioning, corev1.ConditionFalse, DeploymentError, err.Error()) - util.SetBusCondition(&busCopy.Status, *provisionCondition) - c.compareAndUpdateBusStatus(bus, busCopy) - return err - } - - if provisionerDeployment != nil { - provisionCondition := util.NewBusCondition(channelsv1alpha1.BusProvisioning, corev1.ConditionTrue, DeploymentSynced, "deployment successfully synced") - util.SetBusCondition(&busCopy.Status, *provisionCondition) - } else { - util.RemoveBusCondition(&busCopy.Status, channelsv1alpha1.BusProvisioning) - } - - // Finally, we update the status block of the Bus resource to reflect the - // current state of the world - err = c.compareAndUpdateBusStatus(bus, busCopy) - - if err != nil { - return err - } - - c.recorder.Event(bus, corev1.EventTypeNormal, SuccessSynced, MessageResourceSynced) - return nil -} - -func (c *Controller) syncBusDispatcherService(bus *channelsv1alpha1.Bus) (*corev1.Service, error) { - // Get the service with the specified service name - serviceName := controller.BusDispatcherServiceName(bus.Name, bus.Namespace) - service, err := c.servicesLister.Services(system.Namespace).Get(serviceName) - // If the resource doesn't exist, we'll create it - if errors.IsNotFound(err) { - service, err = c.kubeclientset.CoreV1().Services(system.Namespace).Create(newDispatcherService(bus)) - } - - // If an error occurs during Get/Create, we'll requeue the item so we can - // attempt processing again later. This could have been caused by a - // temporary network failure, or any other transient reason. - if err != nil { - return nil, err - } - - // If the Service is not controlled by this Bus resource, we should log - // a warning to the event recorder and return - if !metav1.IsControlledBy(service, bus) { - msg := fmt.Sprintf(MessageResourceExists, service.Name) - c.recorder.Event(bus, corev1.EventTypeWarning, ErrResourceExists, msg) - return nil, fmt.Errorf(msg) - } - - return service, nil -} - -func (c *Controller) syncBusDispatcherDeployment(bus *channelsv1alpha1.Bus) (*appsv1.Deployment, error) { - // Get the deployment with the specified deployment name - deploymentName := controller.BusDispatcherDeploymentName(bus.Name, bus.Namespace) - deployment, err := c.deploymentsLister.Deployments(system.Namespace).Get(deploymentName) - // If the resource doesn't exist, we'll create it - if errors.IsNotFound(err) { - deployment, err = c.kubeclientset.AppsV1().Deployments(system.Namespace).Create(newDispatcherDeployment(bus)) - } - - // If an error occurs during Get/Create, we'll requeue the item so we can - // attempt processing again later. This could have been caused by a - // temporary network failure, or any other transient reason. - if err != nil { - return nil, err - } - - // If the Deployment is not controlled by this Bus resource, we should log - // a warning to the event recorder and return - if !metav1.IsControlledBy(deployment, bus) { - msg := fmt.Sprintf(MessageResourceExists, deployment.Name) - c.recorder.Event(bus, corev1.EventTypeWarning, ErrResourceExists, msg) - return nil, fmt.Errorf(msg) - } - - // If the Deployment does not match the Bus's proposed Deployment we should update - // the Deployment resource. - proposedDeployment := newDispatcherDeployment(bus) - if !reflect.DeepEqual(proposedDeployment.Spec, deployment.Spec) { - glog.V(4).Infof("Bus %s dispatcher spec updated", bus.Name) - deployment, err = c.kubeclientset.AppsV1().Deployments(system.Namespace).Update(proposedDeployment) - - if err != nil { - return nil, err - } - } - - return deployment, nil -} - -func (c *Controller) syncBusProvisionerDeployment(bus *channelsv1alpha1.Bus) (*appsv1.Deployment, error) { - provisioner := bus.Spec.Provisioner - - // Get the deployment with the specified deployment name - deploymentName := controller.BusProvisionerDeploymentName(bus.Name, bus.Namespace) - deployment, err := c.deploymentsLister.Deployments(system.Namespace).Get(deploymentName) - - // If the resource shouldn't exists - if provisioner == nil { - // If the resource exists, we'll delete it - if deployment != nil { - err = c.kubeclientset.AppsV1().Deployments(system.Namespace).Delete(deploymentName, nil) - } - if errors.IsNotFound(err) { - return nil, nil - } - return nil, err - } - - // If the resource doesn't exist, we'll create it - if errors.IsNotFound(err) { - deployment, err = c.kubeclientset.AppsV1().Deployments(system.Namespace).Create(newProvisionerDeployment(bus)) - } - - // If an error occurs during Get/Create, we'll requeue the item so we can - // attempt processing again later. This could have been caused by a - // temporary network failure, or any other transient reason. - if err != nil { - return nil, err - } - - // If the Deployment is not controlled by this Bus resource, we should log - // a warning to the event recorder and return - if !metav1.IsControlledBy(deployment, bus) { - msg := fmt.Sprintf(MessageResourceExists, deployment.Name) - c.recorder.Event(bus, corev1.EventTypeWarning, ErrResourceExists, msg) - return nil, fmt.Errorf(msg) - } - - // If the Deployment does not match the Bus's proposed Deployment we should update - // the Deployment resource. - proposedDeployment := newProvisionerDeployment(bus) - if !reflect.DeepEqual(proposedDeployment.Spec, deployment.Spec) { - glog.V(4).Infof("Bus %s provisioner spec updated", bus.Name) - deployment, err = c.kubeclientset.AppsV1().Deployments(system.Namespace).Update(proposedDeployment) - - if err != nil { - return nil, err - } - } - - return deployment, nil -} - -func (c *Controller) compareAndUpdateBusStatus(bus *channelsv1alpha1.Bus, busCopy *channelsv1alpha1.Bus) error { - util.ConsolidateBusCondition(busCopy) - - // Only update if status has changed - if !equality.Semantic.DeepEqual(bus.Status, busCopy.Status) { - // If the CustomResourceSubresources feature gate is not enabled, - // we must use Update instead of UpdateStatus to update the Status block of the Bus resource. - // UpdateStatus will not allow changes to the Spec of the resource, - // which is ideal for ensuring nothing other than resource status has been updated. - _, err := c.busclientset.ChannelsV1alpha1().Buses(bus.Namespace).Update(busCopy) - return err - } - return nil -} - -// enqueueBus takes a Bus resource and converts it into a namespace/name -// string which is then put onto the work queue. This method should *not* be -// passed resources of any type other than Bus. -func (c *Controller) enqueueBus(obj interface{}) { - var key string - var err error - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - runtime.HandleError(err) - return - } - c.workqueue.AddRateLimited(key) -} - -// handleObject will take any resource implementing metav1.Object and attempt -// to find the Bus resource that 'owns' it. It does this by looking at the -// objects metadata.ownerReferences field for an appropriate OwnerReference. -// It then enqueues that Bus resource to be processed. If the object does not -// have an appropriate OwnerReference, it will simply be skipped. -func (c *Controller) handleObject(obj interface{}) { - var object metav1.Object - var ok bool - if object, ok = obj.(metav1.Object); !ok { - tombstone, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - runtime.HandleError(fmt.Errorf("error decoding object, invalid type")) - return - } - object, ok = tombstone.Obj.(metav1.Object) - if !ok { - runtime.HandleError(fmt.Errorf("error decoding object tombstone, invalid type")) - return - } - glog.V(4).Infof("Recovered deleted object '%s' from tombstone", object.GetName()) - } - glog.V(4).Infof("Processing object: %s", object.GetName()) - if ownerRef := metav1.GetControllerOf(object); ownerRef != nil { - // If this object is not owned by a Bus, we should not do anything more - // with it. - if ownerRef.Kind != "Bus" { - return - } - - bus, err := c.busesLister.Buses(object.GetNamespace()).Get(ownerRef.Name) - if err != nil { - glog.V(4).Infof("ignoring orphaned object '%s' of bus '%s'", object.GetSelfLink(), ownerRef.Name) - return - } - - c.enqueueBus(bus) - return - } -} - -// newDispatcherService creates a new Service for a Bus resource. It also sets -// the appropriate OwnerReferences on the resource so handleObject can discover -// the Bus resource that 'owns' it. -func newDispatcherService(bus *channelsv1alpha1.Bus) *corev1.Service { - labels := dispatcherLabels(bus.Name, bus.Namespace) - return &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: controller.BusDispatcherServiceName(bus.Name, bus.Namespace), - Namespace: system.Namespace, - Labels: labels, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(bus, schema.GroupVersionKind{ - Group: channelsv1alpha1.SchemeGroupVersion.Group, - Version: channelsv1alpha1.SchemeGroupVersion.Version, - Kind: "Bus", - }), - }, - }, - Spec: corev1.ServiceSpec{ - Selector: labels, - Ports: []corev1.ServicePort{ - { - Name: "http", - Port: 80, - TargetPort: intstr.FromInt(buses.MessageReceiverPort), - }, - }, - }, - } -} - -// newDispatcherDeployment creates a new Deployment for a Bus resource. It also sets -// the appropriate OwnerReferences on the resource so handleObject can discover -// the Bus resource that 'owns' it. -func newDispatcherDeployment(bus *channelsv1alpha1.Bus) *appsv1.Deployment { - labels := dispatcherLabels(bus.Name, bus.Namespace) - one := int32(1) - container := bus.Spec.Dispatcher.DeepCopy() - container.Env = append(container.Env, - corev1.EnvVar{ - Name: "PORT", - Value: "8080", - }, - corev1.EnvVar{ - Name: "BUS_NAMESPACE", - Value: bus.Namespace, - }, - corev1.EnvVar{ - Name: "BUS_NAME", - Value: bus.Name, - }, - ) - volumes := []corev1.Volume{} - if bus.Spec.Volumes != nil { - volumes = *bus.Spec.Volumes - } - return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: controller.BusDispatcherDeploymentName(bus.Name, bus.Namespace), - Namespace: system.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(bus, schema.GroupVersionKind{ - Group: channelsv1alpha1.SchemeGroupVersion.Group, - Version: channelsv1alpha1.SchemeGroupVersion.Version, - Kind: "Bus", - }), - }, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: &one, - Selector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - "sidecar.istio.io/inject": "true", - }, - Labels: labels, - }, - Spec: corev1.PodSpec{ - ServiceAccountName: serviceAccountName, - Containers: []corev1.Container{ - *container, - }, - Volumes: volumes, - }, - }, - }, - } -} - -// newProvisionerDeployment creates a new Deployment for a Bus resource. It also sets -// the appropriate OwnerReferences on the resource so handleObject can discover -// the Bus resource that 'owns' it. -func newProvisionerDeployment(bus *channelsv1alpha1.Bus) *appsv1.Deployment { - labels := provisionerLabels(bus.Name, bus.Namespace) - one := int32(1) - container := bus.Spec.Provisioner.DeepCopy() - container.Env = append(container.Env, - corev1.EnvVar{ - Name: "BUS_NAMESPACE", - Value: bus.Namespace, - }, - corev1.EnvVar{ - Name: "BUS_NAME", - Value: bus.Name, - }, - ) - volumes := []corev1.Volume{} - if bus.Spec.Volumes != nil { - volumes = *bus.Spec.Volumes - } - return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: controller.BusProvisionerDeploymentName(bus.Name, bus.Namespace), - Namespace: system.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(bus, schema.GroupVersionKind{ - Group: channelsv1alpha1.SchemeGroupVersion.Group, - Version: channelsv1alpha1.SchemeGroupVersion.Version, - Kind: "Bus", - }), - }, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: &one, - Selector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - "sidecar.istio.io/inject": "true", - }, - Labels: labels, - }, - Spec: corev1.PodSpec{ - ServiceAccountName: serviceAccountName, - Containers: []corev1.Container{ - *container, - }, - Volumes: volumes, - }, - }, - }, - } -} - -func dispatcherLabels(busName, namespace string) map[string]string { - return map[string]string{ - "bus": busName, - "namespace": namespace, - "role": "dispatcher", - } -} - -func provisionerLabels(busName, namespace string) map[string]string { - return map[string]string{ - "bus": busName, - "namespace": namespace, - "role": "provisioner", - } -} diff --git a/pkg/controller/bus/stub_test.go b/pkg/controller/bus/stub_test.go deleted file mode 100644 index 6979e8b37ce..00000000000 --- a/pkg/controller/bus/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package bus - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/controller/channel/controller.go b/pkg/controller/channel/controller.go deleted file mode 100644 index 158059927ef..00000000000 --- a/pkg/controller/channel/controller.go +++ /dev/null @@ -1,563 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package channel - -import ( - "fmt" - "time" - - "github.com/golang/glog" - "github.com/knative/eventing/pkg/controller" - istiolisters "github.com/knative/pkg/client/listers/istio/v1alpha3" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - kubeinformers "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" - corelisters "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/workqueue" - - clientset "github.com/knative/eventing/pkg/client/clientset/versioned" - channelscheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - informers "github.com/knative/eventing/pkg/client/informers/externalversions" - listers "github.com/knative/eventing/pkg/client/listers/channels/v1alpha1" - "github.com/knative/eventing/pkg/system" - sharedclientset "github.com/knative/pkg/client/clientset/versioned" - sharedinformers "github.com/knative/pkg/client/informers/externalversions" - - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "github.com/knative/eventing/pkg/controller/util" - istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" -) - -const controllerAgentName = "channel-controller" - -const ( - // SuccessSynced is used as part of the Event 'reason' when a Channel is synced - SuccessSynced = "Synced" - // ErrResourceExists is used as part of the Event 'reason' when a Channel fails - // to sync due to a Service of the same name already existing. - ErrResourceExists = "ErrResourceExists" - - // MessageResourceExists is the message used for Events when a resource - // fails to sync due to a Service already existing - MessageResourceExists = "Resource %q already exists and is not managed by Channel" - // MessageResourceSynced is the message used for an Event fired when a Channel - // is synced successfully - MessageResourceSynced = "Channel synced successfully" -) - -const ( - // ServiceSynced is used as part of the condition reason when the channel (k8s) service is successfully created. - ServiceSynced = "ServiceSynced" - // ServiceError is used as part of the condition reason when the channel (k8s) service creation failed. - ServiceError = "ServiceError" - // VirtualServiceSynced is used as part of the condition reason when the channel istio virtual service is successfully created. - VirtualServiceSynced = "VirtualServiceSynced" - // VirtualServiceError is used as part of the condition reason when the channel istio virtual service creation failed. - VirtualServiceError = "VirtualServiceError" -) - -const ( - PortNumber = 80 - PortName = "http" -) - -// Controller is the controller implementation for Channel resources -type Controller struct { - // kubeclientset is a standard kubernetes clientset - kubeclientset kubernetes.Interface - // channelclientset is a clientset for our own API group - channelclientset clientset.Interface - // sharedclientset is a clientset for shared API groups - sharedclientset sharedclientset.Interface - - virtualservicesLister istiolisters.VirtualServiceLister - virtualservicesSynced cache.InformerSynced - servicesLister corelisters.ServiceLister - servicesSynced cache.InformerSynced - channelsLister listers.ChannelLister - channelsSynced cache.InformerSynced - - // workqueue is a rate limited work queue. This is used to queue work to be - // processed instead of performing it as soon as a change happens. This - // means we can ensure we only process a fixed amount of resources at a - // time, and makes it easy to ensure we are never processing the same item - // simultaneously in two different workers. - workqueue workqueue.RateLimitingInterface - // recorder is an event recorder for recording Event resources to the - // Kubernetes API. - recorder record.EventRecorder -} - -// NewController returns a new channel controller -func NewController( - kubeclientset kubernetes.Interface, - channelclientset clientset.Interface, - sharedclientset sharedclientset.Interface, - restConfig *rest.Config, - kubeInformerFactory kubeinformers.SharedInformerFactory, - channelInformerFactory informers.SharedInformerFactory, - sharedInformerFactory sharedinformers.SharedInformerFactory, -) controller.Interface { - - // obtain references to shared index informers for the Service and Channel types. - virtualserviceInformer := sharedInformerFactory.Networking().V1alpha3().VirtualServices() - serviceInformer := kubeInformerFactory.Core().V1().Services() - channelInformer := channelInformerFactory.Channels().V1alpha1().Channels() - - // Create event broadcaster - // Add channel-controller types to the default Kubernetes Scheme so Events can be - // logged for channel-controller types. - channelscheme.AddToScheme(scheme.Scheme) - glog.V(4).Info("Creating event broadcaster") - eventBroadcaster := record.NewBroadcaster() - eventBroadcaster.StartLogging(glog.Infof) - eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")}) - recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) - - controller := &Controller{ - kubeclientset: kubeclientset, - channelclientset: channelclientset, - sharedclientset: sharedclientset, - virtualservicesLister: virtualserviceInformer.Lister(), - virtualservicesSynced: virtualserviceInformer.Informer().HasSynced, - servicesLister: serviceInformer.Lister(), - servicesSynced: serviceInformer.Informer().HasSynced, - channelsLister: channelInformer.Lister(), - channelsSynced: channelInformer.Informer().HasSynced, - workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "Channels"), - recorder: recorder, - } - - glog.Info("Setting up event handlers") - // Set up an event handler for when Channel resources change - channelInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: controller.enqueueChannel, - UpdateFunc: func(old, new interface{}) { - controller.enqueueChannel(new) - }, - }) - // Set up an event handler for when Service resources change. This - // handler will lookup the owner of the given Service, and if it is - // owned by a Channel resource will enqueue that Channel resource for - // processing. This way, we don't need to implement custom logic for - // handling Service resources. More info on this pattern: - // https://github.com/kubernetes/community/blob/8cafef897a22026d42f5e5bb3f104febe7e29830/contributors/devel/controllers.md - serviceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: controller.handleObject, - UpdateFunc: func(old, new interface{}) { - newService := new.(*corev1.Service) - oldService := old.(*corev1.Service) - if newService.ResourceVersion == oldService.ResourceVersion { - // Periodic resync will send update events for all known Services. - // Two different versions of the same Service will always have different RVs. - return - } - controller.handleObject(new) - }, - DeleteFunc: controller.handleObject, - }) - - return controller -} - -// Run will set up the event handlers for types we are interested in, as well -// as syncing informer caches and starting workers. It will block until stopCh -// is closed, at which point it will shutdown the workqueue and wait for -// workers to finish processing their current work items. -func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error { - defer runtime.HandleCrash() - defer c.workqueue.ShutDown() - - // Start the informer factories to begin populating the informer caches - glog.Info("Starting Channel controller") - - // Wait for the caches to be synced before starting workers - glog.Info("Waiting for informer caches to sync") - if ok := cache.WaitForCacheSync(stopCh, c.servicesSynced, c.channelsSynced); !ok { - return fmt.Errorf("failed to wait for caches to sync") - } - - glog.Info("Starting workers") - // Launch two workers to process Channel resources - for i := 0; i < threadiness; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) - } - - glog.Info("Started workers") - <-stopCh - glog.Info("Shutting down workers") - - return nil -} - -// runWorker is a long-running function that will continually call the -// processNextWorkItem function in order to read and process a message on the -// workqueue. -func (c *Controller) runWorker() { - for c.processNextWorkItem() { - } -} - -// processNextWorkItem will read a single work item off the workqueue and -// attempt to process it, by calling the syncHandler. -func (c *Controller) processNextWorkItem() bool { - obj, shutdown := c.workqueue.Get() - - if shutdown { - return false - } - - // We wrap this block in a func so we can defer c.workqueue.Done. - err := func(obj interface{}) error { - // We call Done here so the workqueue knows we have finished - // processing this item. We also must remember to call Forget if we - // do not want this work item being re-queued. For example, we do - // not call Forget if a transient error occurs, instead the item is - // put back on the workqueue and attempted again after a back-off - // period. - defer c.workqueue.Done(obj) - var key string - var ok bool - // We expect strings to come off the workqueue. These are of the - // form namespace/name. We do this as the delayed nature of the - // workqueue means the items in the informer cache may actually be - // more up to date that when the item was initially put onto the - // workqueue. - if key, ok = obj.(string); !ok { - // As the item in the workqueue is actually invalid, we call - // Forget here else we'd go into a loop of attempting to - // process a work item that is invalid. - c.workqueue.Forget(obj) - runtime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) - return nil - } - // Run the syncHandler, passing it the namespace/name string of the - // Channel resource to be synced. - if err := c.syncHandler(key); err != nil { - return fmt.Errorf("error syncing channel '%s': %v", key, err) - } - // Finally, if no error occurs we Forget this item so it does not - // get queued again until another change happens. - c.workqueue.Forget(obj) - glog.Infof("Successfully synced channel '%s'", key) - return nil - }(obj) - - if err != nil { - runtime.HandleError(err) - return true - } - - return true -} - -// syncHandler compares the actual state with the desired, and attempts to -// converge the two. It then updates the Status block of the Channel resource -// with the current status of the resource. -func (c *Controller) syncHandler(key string) error { - // Convert the namespace/name string into a distinct namespace and name - namespace, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - runtime.HandleError(fmt.Errorf("invalid resource key: %s", key)) - return nil - } - - // Get the Channel resource with this namespace/name - channel, err := c.channelsLister.Channels(namespace).Get(name) - if err != nil { - // The Channel resource may no longer exist, in which case we stop - // processing. - if errors.IsNotFound(err) { - runtime.HandleError(fmt.Errorf("channel '%s' in work queue no longer exists", key)) - return nil - } - - return err - } - - var service *corev1.Service - var virtualService *istiov1alpha3.VirtualService - var serviceErr, virtualServiceErr error - - // Sync Service derived from the Channel - service, serviceErr = c.syncChannelService(channel) - if serviceErr != nil { - c.updateChannelStatus(channel, service, serviceErr, virtualService, virtualServiceErr) - return serviceErr - } - - // Sync VirtualService derived from a Channel - virtualService, virtualServiceErr = c.syncChannelVirtualService(channel) - if virtualServiceErr != nil { - c.updateChannelStatus(channel, service, serviceErr, virtualService, virtualServiceErr) - return virtualServiceErr - } - - // Finally, we update the status block of the Channel resource to reflect the - // current state of the world - err = c.updateChannelStatus(channel, service, serviceErr, virtualService, virtualServiceErr) - if err != nil { - return err - } - - c.recorder.Event(channel, corev1.EventTypeNormal, SuccessSynced, MessageResourceSynced) - return nil -} - -func (c *Controller) syncChannelService(channel *channelsv1alpha1.Channel) (*corev1.Service, error) { - // Get the service with the specified service name - serviceName := controller.ChannelServiceName(channel.ObjectMeta.Name) - service, err := c.servicesLister.Services(channel.Namespace).Get(serviceName) - // If the resource doesn't exist, we'll create it - if errors.IsNotFound(err) { - service, err = c.kubeclientset.CoreV1().Services(channel.Namespace).Create(newService(channel)) - } - - // If an error occurs during Get/Create, we'll requeue the item so we can - // attempt processing again later. This could have been caused by a - // temporary network failure, or any other transient reason. - if err != nil { - return nil, err - } - - // If the Service is not controlled by this Channel resource, we should log - // a warning to the event recorder and return - if !metav1.IsControlledBy(service, channel) { - msg := fmt.Sprintf(MessageResourceExists, service.Name) - c.recorder.Event(channel, corev1.EventTypeWarning, ErrResourceExists, msg) - return nil, fmt.Errorf(msg) - } - - return service, nil -} - -func (c *Controller) syncChannelVirtualService(channel *channelsv1alpha1.Channel) (*istiov1alpha3.VirtualService, error) { - // Get the VirtualService with the specified Channel name - virtualserviceName := controller.ChannelVirtualServiceName(channel.ObjectMeta.Name) - virtualservice, err := c.virtualservicesLister.VirtualServices(channel.Namespace).Get(virtualserviceName) - - // If the resource doesn't exist, we'll create it - if errors.IsNotFound(err) { - virtualservice, err = c.sharedclientset.NetworkingV1alpha3().VirtualServices(channel.Namespace).Create(newVirtualService(channel)) - } - - // If an error occurs during Get/Create, we'll requeue the item so we can - // attempt processing again later. This could have been caused by a - // temporary network failure, or any other transient reason. - if err != nil { - return nil, err - } - - // If the Service is not controlled by this Channel resource, we should log - // a warning to the event recorder and return - if !metav1.IsControlledBy(virtualservice, channel) { - msg := fmt.Sprintf(MessageResourceExists, virtualservice.Name) - c.recorder.Event(channel, corev1.EventTypeWarning, ErrResourceExists, msg) - return nil, fmt.Errorf(msg) - } - - return virtualservice, nil -} - -func (c *Controller) updateChannelStatus(channel *channelsv1alpha1.Channel, - service *corev1.Service, serviceError error, - virtualService *istiov1alpha3.VirtualService, virtualServiceError error) error { - // NEVER modify objects from the store. It's a read-only, local cache. - // You can use DeepCopy() to make a deep copy of original object and modify this copy - // Or create a copy manually for better performance - channelCopy := channel.DeepCopy() - - // Update ChannelStatus - - if service != nil { - channelCopy.Status.Service = &corev1.LocalObjectReference{Name: service.Name} - serviceCondition := util.NewChannelCondition(channelsv1alpha1.ChannelServiceable, corev1.ConditionTrue, ServiceSynced, "service successfully synced") - util.SetChannelCondition(&channelCopy.Status, *serviceCondition) - } else { - channelCopy.Status.Service = nil - serviceCondition := util.NewChannelCondition(channelsv1alpha1.ChannelServiceable, corev1.ConditionFalse, ServiceError, serviceError.Error()) - util.SetChannelCondition(&channelCopy.Status, *serviceCondition) - } - - if virtualService != nil { - channelCopy.Status.VirtualService = &corev1.LocalObjectReference{Name: virtualService.Name} - serviceCondition := util.NewChannelCondition(channelsv1alpha1.ChannelRoutable, corev1.ConditionTrue, VirtualServiceSynced, "virtual service successfully synced") - util.SetChannelCondition(&channelCopy.Status, *serviceCondition) - } else { - channelCopy.Status.VirtualService = nil - serviceCondition := util.NewChannelCondition(channelsv1alpha1.ChannelRoutable, corev1.ConditionFalse, VirtualServiceError, virtualServiceError.Error()) - util.SetChannelCondition(&channelCopy.Status, *serviceCondition) - } - - util.ConsolidateChannelCondition(&channelCopy.Status) - - channelCopy.Status.DomainInternal = controller.ServiceHostName(service.Name, service.Namespace) - - // Only update if status has changed - if !equality.Semantic.DeepEqual(channel.Status, channelCopy.Status) { - // If the CustomResourceSubresources feature gate is not enabled, - // we must use Update instead of UpdateStatus to update the Status block of the Channel resource. - // UpdateStatus will not allow changes to the Spec of the resource, - // which is ideal for ensuring nothing other than resource status has been updated. - _, err := c.channelclientset.ChannelsV1alpha1().Channels(channel.Namespace).Update(channelCopy) - return err - } - return nil -} - -// enqueueChannel takes a Channel resource and converts it into a namespace/name -// string which is then put onto the work queue. This method should *not* be -// passed resources of any type other than Channel. -func (c *Controller) enqueueChannel(obj interface{}) { - var key string - var err error - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - runtime.HandleError(err) - return - } - c.workqueue.AddRateLimited(key) -} - -// handleObject will take any resource implementing metav1.Object and attempt -// to find the Channel resource that 'owns' it. It does this by looking at the -// objects metadata.ownerReferences field for an appropriate OwnerReference. -// It then enqueues that Channel resource to be processed. If the object does not -// have an appropriate OwnerReference, it will simply be skipped. -func (c *Controller) handleObject(obj interface{}) { - var object metav1.Object - var ok bool - if object, ok = obj.(metav1.Object); !ok { - tombstone, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - runtime.HandleError(fmt.Errorf("error decoding object, invalid type")) - return - } - object, ok = tombstone.Obj.(metav1.Object) - if !ok { - runtime.HandleError(fmt.Errorf("error decoding object tombstone, invalid type")) - return - } - glog.V(4).Infof("Recovered deleted object '%s' from tombstone", object.GetName()) - } - glog.V(4).Infof("Processing object: %s", object.GetName()) - if ownerRef := metav1.GetControllerOf(object); ownerRef != nil { - // If this object is not owned by a Channel, we should not do anything more - // with it. - if ownerRef.Kind != "Channel" { - return - } - - channel, err := c.channelsLister.Channels(object.GetNamespace()).Get(ownerRef.Name) - if err != nil { - glog.V(4).Infof("ignoring orphaned object '%s' of channel '%s'", object.GetSelfLink(), ownerRef.Name) - return - } - - c.enqueueChannel(channel) - return - } -} - -// newService creates a new Service for a Channel resource. It also sets -// the appropriate OwnerReferences on the resource so handleObject can discover -// the Channel resource that 'owns' it. -func newService(channel *channelsv1alpha1.Channel) *corev1.Service { - labels := map[string]string{ - "channel": channel.Name, - } - return &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: controller.ChannelServiceName(channel.ObjectMeta.Name), - Namespace: channel.Namespace, - Labels: labels, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(channel, schema.GroupVersionKind{ - Group: channelsv1alpha1.SchemeGroupVersion.Group, - Version: channelsv1alpha1.SchemeGroupVersion.Version, - Kind: "Channel", - }), - }, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - {Name: PortName, Port: PortNumber}, - }, - }, - } -} - -// newVirtualService creates a new VirtualService for a Channel resource. It also sets -// the appropriate OwnerReferences on the resource so handleObject can discover -// the Channel resource that 'owns' it. -func newVirtualService(channel *channelsv1alpha1.Channel) *istiov1alpha3.VirtualService { - labels := map[string]string{ - "channel": channel.Name, - } - var destinationHost string - if channel.Spec.Bus != "" { - labels["bus"] = channel.Spec.Bus - destinationHost = controller.ServiceHostName(controller.BusDispatcherServiceName(channel.Spec.Bus, channel.Namespace), system.Namespace) - } - if channel.Spec.ClusterBus != "" { - labels["clusterBus"] = channel.Spec.ClusterBus - destinationHost = controller.ServiceHostName(controller.ClusterBusDispatcherServiceName(channel.Spec.ClusterBus), system.Namespace) - } - return &istiov1alpha3.VirtualService{ - ObjectMeta: metav1.ObjectMeta{ - Name: controller.ChannelVirtualServiceName(channel.Name), - Namespace: channel.Namespace, - Labels: labels, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(channel, schema.GroupVersionKind{ - Group: channelsv1alpha1.SchemeGroupVersion.Group, - Version: channelsv1alpha1.SchemeGroupVersion.Version, - Kind: "Channel", - }), - }, - }, - Spec: istiov1alpha3.VirtualServiceSpec{ - Hosts: []string{ - controller.ServiceHostName(controller.ChannelServiceName(channel.Name), channel.Namespace), - controller.ChannelHostName(channel.Name, channel.Namespace), - }, - Http: []istiov1alpha3.HTTPRoute{{ - Rewrite: &istiov1alpha3.HTTPRewrite{ - Authority: controller.ChannelHostName(channel.Name, channel.Namespace), - }, - Route: []istiov1alpha3.DestinationWeight{{ - Destination: istiov1alpha3.Destination{ - Host: destinationHost, - Port: istiov1alpha3.PortSelector{ - Number: PortNumber, - }, - }}, - }}, - }, - }, - } -} diff --git a/pkg/controller/channel/stub_test.go b/pkg/controller/channel/stub_test.go deleted file mode 100644 index a2b614a1a3b..00000000000 --- a/pkg/controller/channel/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package channel - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/controller/clusterbus/controller.go b/pkg/controller/clusterbus/controller.go deleted file mode 100644 index db5418a9de7..00000000000 --- a/pkg/controller/clusterbus/controller.go +++ /dev/null @@ -1,703 +0,0 @@ -/* -Copyright 2017 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package clusterbus - -import ( - "fmt" - "reflect" - "time" - - "github.com/knative/eventing/pkg/buses" - - "github.com/golang/glog" - "github.com/knative/eventing/pkg/controller" - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/runtime" - "k8s.io/apimachinery/pkg/util/wait" - kubeinformers "k8s.io/client-go/informers" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/kubernetes/scheme" - typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1" - appslisters "k8s.io/client-go/listers/apps/v1" - corelisters "k8s.io/client-go/listers/core/v1" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/record" - "k8s.io/client-go/util/workqueue" - - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - clientset "github.com/knative/eventing/pkg/client/clientset/versioned" - channelscheme "github.com/knative/eventing/pkg/client/clientset/versioned/scheme" - informers "github.com/knative/eventing/pkg/client/informers/externalversions" - listers "github.com/knative/eventing/pkg/client/listers/channels/v1alpha1" - "github.com/knative/eventing/pkg/controller/util" - "github.com/knative/eventing/pkg/system" - sharedclientset "github.com/knative/pkg/client/clientset/versioned" - sharedinformers "github.com/knative/pkg/client/informers/externalversions" -) - -const ( - controllerAgentName = "clusterbus-controller" - serviceAccountName = "bus-operator" -) - -const ( - // SuccessSynced is used as part of the Event 'reason' when a ClusterBus is synced - SuccessSynced = "Synced" - // ErrResourceExists is used as part of the Event 'reason' when a ClusterBus fails - // to sync due to a Service of the same name already existing. - ErrResourceExists = "ErrResourceExists" - - // MessageResourceExists is the message used for Events when a resource - // fails to sync due to a Service already existing - MessageResourceExists = "Resource %q already exists and is not managed by ClusterBus" - // MessageResourceSynced is the message used for an Event fired when a ClusterBus - // is synced successfully - MessageResourceSynced = "ClusterBus synced successfully" -) - -const ( - // ServiceSynced is used as part of the condition reason when the bus (k8s) service is successfully created. - ServiceSynced = "ServiceSynced" - // ServiceError is used as part of the condition reason when the bus (k8s) service creation failed. - ServiceError = "ServiceError" - // DeploymentSynced is used as part of the condition reason when a bus deployment is successfully created. - DeploymentSynced = "DeploymentSynced" - // DeploymentError is used as part of the condition reason when a bus deployment creation failed. - DeploymentError = "DeploymentError" -) - -// Controller is the controller implementation for ClusterBus resources -type Controller struct { - // kubeclientset is a standard kubernetes clientset - kubeclientset kubernetes.Interface - // clusterbusclientset is a clientset for our own API group - clusterbusclientset clientset.Interface - - deploymentsLister appslisters.DeploymentLister - deploymentsSynced cache.InformerSynced - servicesLister corelisters.ServiceLister - servicesSynced cache.InformerSynced - clusterBusesLister listers.ClusterBusLister - clusterBusesSynced cache.InformerSynced - - // workqueue is a rate limited work queue. This is used to queue work to be - // processed instead of performing it as soon as a change happens. This - // means we can ensure we only process a fixed amount of resources at a - // time, and makes it easy to ensure we are never processing the same item - // simultaneously in two different workers. - workqueue workqueue.RateLimitingInterface - // recorder is an event recorder for recording Event resources to the - // Kubernetes API. - recorder record.EventRecorder -} - -// NewController returns a new clusterbus controller -func NewController( - kubeclientset kubernetes.Interface, - clusterbusclientset clientset.Interface, - sharedclientset sharedclientset.Interface, - restConfig *rest.Config, - kubeInformerFactory kubeinformers.SharedInformerFactory, - clusterBusInformerFactory informers.SharedInformerFactory, - sharedInformerFactory sharedinformers.SharedInformerFactory, -) controller.Interface { - - // obtain references to shared index informers for the ClusterBus, Deployment and Service - // types. - clusterBusInformer := clusterBusInformerFactory.Channels().V1alpha1().ClusterBuses() - deploymentInformer := kubeInformerFactory.Apps().V1().Deployments() - serviceInformer := kubeInformerFactory.Core().V1().Services() - - // Create event broadcaster - // Add clusterbus-controller types to the default Kubernetes Scheme so Events can be - // logged for clusterbus-controller types. - channelscheme.AddToScheme(scheme.Scheme) - glog.V(4).Info("Creating event broadcaster") - eventBroadcaster := record.NewBroadcaster() - eventBroadcaster.StartLogging(glog.Infof) - eventBroadcaster.StartRecordingToSink(&typedcorev1.EventSinkImpl{Interface: kubeclientset.CoreV1().Events("")}) - recorder := eventBroadcaster.NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) - - controller := &Controller{ - kubeclientset: kubeclientset, - clusterbusclientset: clusterbusclientset, - deploymentsLister: deploymentInformer.Lister(), - deploymentsSynced: deploymentInformer.Informer().HasSynced, - servicesLister: serviceInformer.Lister(), - servicesSynced: serviceInformer.Informer().HasSynced, - clusterBusesLister: clusterBusInformer.Lister(), - clusterBusesSynced: clusterBusInformer.Informer().HasSynced, - workqueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), "ClusterBuses"), - recorder: recorder, - } - - glog.Info("Setting up event handlers") - // Set up an event handler for when ClusterBus resources change - clusterBusInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: controller.enqueueClusterBus, - UpdateFunc: func(old, new interface{}) { - controller.enqueueClusterBus(new) - }, - }) - // Set up an event handler for when Service resources change. This - // handler will lookup the owner of the given Service, and if it is - // owned by a ClusterBus resource will enqueue that ClusterBus resource for - // processing. This way, we don't need to implement custom logic for - // handling Service resources. More info on this pattern: - // https://github.com/kubernetes/community/blob/8cafef897a22026d42f5e5bb3f104febe7e29830/contributors/devel/controllers.md - serviceInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: controller.handleObject, - UpdateFunc: func(old, new interface{}) { - newService := new.(*corev1.Service) - oldService := old.(*corev1.Service) - if newService.ResourceVersion == oldService.ResourceVersion { - // Periodic resync will send update events for all known Services. - // Two different versions of the same Service will always have different RVs. - return - } - controller.handleObject(new) - }, - DeleteFunc: controller.handleObject, - }) - - return controller -} - -// Run will set up the event handlers for types we are interested in, as well -// as syncing informer caches and starting workers. It will block until stopCh -// is closed, at which point it will shutdown the workqueue and wait for -// workers to finish processing their current work items. -func (c *Controller) Run(threadiness int, stopCh <-chan struct{}) error { - defer runtime.HandleCrash() - defer c.workqueue.ShutDown() - - // Start the informer factories to begin populating the informer caches - glog.Info("Starting ClusterBus controller") - - // Wait for the caches to be synced before starting workers - glog.Info("Waiting for informer caches to sync") - if ok := cache.WaitForCacheSync(stopCh, c.deploymentsSynced, c.servicesSynced, c.clusterBusesSynced); !ok { - return fmt.Errorf("failed to wait for caches to sync") - } - - glog.Info("Starting workers") - // Launch two workers to process ClusterBus resources - for i := 0; i < threadiness; i++ { - go wait.Until(c.runWorker, time.Second, stopCh) - } - - glog.Info("Started workers") - <-stopCh - glog.Info("Shutting down workers") - - return nil -} - -// runWorker is a long-running function that will continually call the -// processNextWorkItem function in order to read and process a message on the -// workqueue. -func (c *Controller) runWorker() { - for c.processNextWorkItem() { - } -} - -// processNextWorkItem will read a single work item off the workqueue and -// attempt to process it, by calling the syncHandler. -func (c *Controller) processNextWorkItem() bool { - obj, shutdown := c.workqueue.Get() - - if shutdown { - return false - } - - // We wrap this block in a func so we can defer c.workqueue.Done. - err := func(obj interface{}) error { - // We call Done here so the workqueue knows we have finished - // processing this item. We also must remember to call Forget if we - // do not want this work item being re-queued. For example, we do - // not call Forget if a transient error occurs, instead the item is - // put back on the workqueue and attempted again after a back-off - // period. - defer c.workqueue.Done(obj) - var key string - var ok bool - // We expect strings to come off the workqueue. These are of the - // form namespace/name. We do this as the delayed nature of the - // workqueue means the items in the informer cache may actually be - // more up to date that when the item was initially put onto the - // workqueue. - if key, ok = obj.(string); !ok { - // As the item in the workqueue is actually invalid, we call - // Forget here else we'd go into a loop of attempting to - // process a work item that is invalid. - c.workqueue.Forget(obj) - runtime.HandleError(fmt.Errorf("expected string in workqueue but got %#v", obj)) - return nil - } - // Run the syncHandler, passing it the namespace/name string of the - // ClusterBus resource to be synced. - if err := c.syncHandler(key); err != nil { - return fmt.Errorf("error syncing clusterbus '%s': %v", key, err) - } - // Finally, if no error occurs we Forget this item so it does not - // get queued again until another change happens. - c.workqueue.Forget(obj) - glog.Infof("Successfully synced clusterbus '%s'", key) - return nil - }(obj) - - if err != nil { - runtime.HandleError(err) - return true - } - - return true -} - -// syncHandler compares the actual state with the desired, and attempts to -// converge the two. It then updates the Status block of the ClusterBus resource -// with the current status of the resource. -func (c *Controller) syncHandler(key string) error { - // Convert the namespace/name string into a distinct namespace and name - _, name, err := cache.SplitMetaNamespaceKey(key) - if err != nil { - runtime.HandleError(fmt.Errorf("invalid resource key: %s", key)) - return nil - } - - // Get the ClusterBus resource with this name - clusterBus, err := c.clusterBusesLister.Get(name) - if err != nil { - // The ClusterBus resource may no longer exist, in which case we stop - // processing. - if errors.IsNotFound(err) { - runtime.HandleError(fmt.Errorf("clusterbus '%s' in work queue no longer exists", key)) - return nil - } - - return err - } - - // NEVER modify objects from the store. It's a read-only, local cache. - // You can use DeepCopy() to make a deep copy of original object and modify this copy - // Or create a copy manually for better performance - clusterBusCopy := clusterBus.DeepCopy() - - // Sync Service derived from the ClusterBus - dispatcherService, err := c.syncClusterBusDispatcherService(clusterBusCopy) - - if err != nil { - clusterBusCopy.Status.Service = nil - serviceCondition := util.NewBusCondition(channelsv1alpha1.BusServiceable, corev1.ConditionFalse, ServiceError, err.Error()) - util.SetBusCondition(&clusterBusCopy.Status, *serviceCondition) - c.compareAndUpdateBusStatus(clusterBus, clusterBusCopy) - return err - } - - clusterBusCopy.Status.Service = &corev1.LocalObjectReference{Name: dispatcherService.Name} - serviceCondition := util.NewBusCondition(channelsv1alpha1.BusServiceable, corev1.ConditionTrue, ServiceSynced, "service successfully synced") - util.SetBusCondition(&clusterBusCopy.Status, *serviceCondition) - - // Sync Deployment derived from the ClusterBus - _, err = c.syncClusterBusDispatcherDeployment(clusterBusCopy) - - if err != nil { - dispatchCondition := util.NewBusCondition(channelsv1alpha1.BusDispatching, corev1.ConditionFalse, DeploymentError, err.Error()) - util.SetBusCondition(&clusterBusCopy.Status, *dispatchCondition) - c.compareAndUpdateBusStatus(clusterBus, clusterBusCopy) - return err - } - - dispatchCondition := util.NewBusCondition(channelsv1alpha1.BusDispatching, corev1.ConditionTrue, DeploymentSynced, "deployment successfully synced") - util.SetBusCondition(&clusterBusCopy.Status, *dispatchCondition) - - // Sync Deployment derived from the ClusterBus - provisionerDeployment, err := c.syncClusterBusProvisionerDeployment(clusterBusCopy) - - if err != nil { - provisionCondition := util.NewBusCondition(channelsv1alpha1.BusProvisioning, corev1.ConditionFalse, DeploymentError, err.Error()) - util.SetBusCondition(&clusterBusCopy.Status, *provisionCondition) - c.compareAndUpdateBusStatus(clusterBus, clusterBusCopy) - return err - } - - if provisionerDeployment != nil { - provisionCondition := util.NewBusCondition(channelsv1alpha1.BusProvisioning, corev1.ConditionTrue, DeploymentSynced, "deployment successfully synced") - util.SetBusCondition(&clusterBusCopy.Status, *provisionCondition) - } else { - util.RemoveBusCondition(&clusterBusCopy.Status, channelsv1alpha1.BusProvisioning) - } - - // Finally, we update the status block of the ClusterBus resource to reflect the - // current state of the world - err = c.compareAndUpdateBusStatus(clusterBus, clusterBusCopy) - if err != nil { - return err - } - - c.recorder.Event(clusterBus, corev1.EventTypeNormal, SuccessSynced, MessageResourceSynced) - return nil -} - -func (c *Controller) syncClusterBusDispatcherService(clusterBus *channelsv1alpha1.ClusterBus) (*corev1.Service, error) { - // Get the service with the specified service name - serviceName := controller.ClusterBusDispatcherServiceName(clusterBus.ObjectMeta.Name) - service, err := c.servicesLister.Services(system.Namespace).Get(serviceName) - // If the resource doesn't exist, we'll create it - if errors.IsNotFound(err) { - service, err = c.kubeclientset.CoreV1().Services(system.Namespace).Create(newDispatcherService(clusterBus)) - } - - // If an error occurs during Get/Create, we'll requeue the item so we can - // attempt processing again later. This could have been caused by a - // temporary network failure, or any other transient reason. - if err != nil { - return nil, err - } - - // If the Service is not controlled by this ClusterBus resource, we should log - // a warning to the event recorder and return - if !metav1.IsControlledBy(service, clusterBus) { - msg := fmt.Sprintf(MessageResourceExists, service.Name) - c.recorder.Event(clusterBus, corev1.EventTypeWarning, ErrResourceExists, msg) - return nil, fmt.Errorf(msg) - } - - return service, nil -} - -func (c *Controller) syncClusterBusDispatcherDeployment(clusterBus *channelsv1alpha1.ClusterBus) (*appsv1.Deployment, error) { - // Get the deployment with the specified deployment name - deploymentName := controller.ClusterBusDispatcherDeploymentName(clusterBus.ObjectMeta.Name) - deployment, err := c.deploymentsLister.Deployments(system.Namespace).Get(deploymentName) - // If the resource doesn't exist, we'll create it - if errors.IsNotFound(err) { - deployment, err = c.kubeclientset.AppsV1().Deployments(system.Namespace).Create(newDispatcherDeployment(clusterBus)) - } - - // If an error occurs during Get/Create, we'll requeue the item so we can - // attempt processing again later. This could have been caused by a - // temporary network failure, or any other transient reason. - if err != nil { - return nil, err - } - - // If the Deployment is not controlled by this ClusterBus resource, we should log - // a warning to the event recorder and return - if !metav1.IsControlledBy(deployment, clusterBus) { - msg := fmt.Sprintf(MessageResourceExists, deployment.Name) - c.recorder.Event(clusterBus, corev1.EventTypeWarning, ErrResourceExists, msg) - return nil, fmt.Errorf(msg) - } - - // If the Deployment does not match the ClusterBus's proposed Deployment we should update - // the Deployment resource. - proposedDeployment := newDispatcherDeployment(clusterBus) - if !reflect.DeepEqual(proposedDeployment.Spec, deployment.Spec) { - glog.V(4).Infof("ClusterBus %s dispatcher spec updated", clusterBus.Name) - deployment, err = c.kubeclientset.AppsV1().Deployments(system.Namespace).Update(proposedDeployment) - - if err != nil { - return nil, err - } - } - - return deployment, nil -} - -func (c *Controller) syncClusterBusProvisionerDeployment(clusterBus *channelsv1alpha1.ClusterBus) (*appsv1.Deployment, error) { - provisioner := clusterBus.Spec.Provisioner - - // Get the deployment with the specified deployment name - deploymentName := controller.ClusterBusProvisionerDeploymentName(clusterBus.ObjectMeta.Name) - deployment, err := c.deploymentsLister.Deployments(system.Namespace).Get(deploymentName) - - // If the resource shouldn't exists - if provisioner == nil { - // If the resource exists, we'll delete it - if deployment != nil { - err = c.kubeclientset.AppsV1().Deployments(system.Namespace).Delete(deploymentName, nil) - } - if errors.IsNotFound(err) { - return nil, nil - } - return nil, err - } - - // If the resource doesn't exist, we'll create it - if errors.IsNotFound(err) { - deployment, err = c.kubeclientset.AppsV1().Deployments(system.Namespace).Create(newProvisionerDeployment(clusterBus)) - } - - // If an error occurs during Get/Create, we'll requeue the item so we can - // attempt processing again later. This could have been caused by a - // temporary network failure, or any other transient reason. - if err != nil { - return nil, err - } - - // If the Deployment is not controlled by this ClusterBus resource, we should log - // a warning to the event recorder and return - if !metav1.IsControlledBy(deployment, clusterBus) { - msg := fmt.Sprintf(MessageResourceExists, deployment.Name) - c.recorder.Event(clusterBus, corev1.EventTypeWarning, ErrResourceExists, msg) - return nil, fmt.Errorf(msg) - } - - // If the Deployment does not match the ClusterBus's proposed Deployment we should update - // the Deployment resource. - proposedDeployment := newProvisionerDeployment(clusterBus) - if !reflect.DeepEqual(proposedDeployment.Spec, deployment.Spec) { - glog.V(4).Infof("ClusterBus %s provisioner spec updated", clusterBus.Name) - deployment, err = c.kubeclientset.AppsV1().Deployments(system.Namespace).Update(proposedDeployment) - - if err != nil { - return nil, err - } - } - - return deployment, nil -} - -func (c *Controller) compareAndUpdateBusStatus(clusterBus *channelsv1alpha1.ClusterBus, clusterBusCopy *channelsv1alpha1.ClusterBus) error { - util.ConsolidateBusCondition(clusterBusCopy) - // Only update if status has changed - if !equality.Semantic.DeepEqual(clusterBus.Status, clusterBusCopy.Status) { - // If the CustomResourceSubresources feature gate is not enabled, - // we must use Update instead of UpdateStatus to update the Status block of the ClusterBus resource. - // UpdateStatus will not allow changes to the Spec of the resource, - // which is ideal for ensuring nothing other than resource status has been updated. - _, err := c.clusterbusclientset.ChannelsV1alpha1().ClusterBuses().Update(clusterBusCopy) - return err - } - return nil -} - -// enqueueClusterBus takes a ClusterBus resource and converts it into a namespace/name -// string which is then put onto the work queue. This method should *not* be -// passed resources of any type other than ClusterBus. -func (c *Controller) enqueueClusterBus(obj interface{}) { - var key string - var err error - if key, err = cache.MetaNamespaceKeyFunc(obj); err != nil { - runtime.HandleError(err) - return - } - c.workqueue.AddRateLimited(key) -} - -// handleObject will take any resource implementing metav1.Object and attempt -// to find the ClusterBus resource that 'owns' it. It does this by looking at the -// objects metadata.ownerReferences field for an appropriate OwnerReference. -// It then enqueues that ClusterBus resource to be processed. If the object does not -// have an appropriate OwnerReference, it will simply be skipped. -func (c *Controller) handleObject(obj interface{}) { - var object metav1.Object - var ok bool - if object, ok = obj.(metav1.Object); !ok { - tombstone, ok := obj.(cache.DeletedFinalStateUnknown) - if !ok { - runtime.HandleError(fmt.Errorf("error decoding object, invalid type")) - return - } - object, ok = tombstone.Obj.(metav1.Object) - if !ok { - runtime.HandleError(fmt.Errorf("error decoding object tombstone, invalid type")) - return - } - glog.V(4).Infof("Recovered deleted object '%s' from tombstone", object.GetName()) - } - glog.V(4).Infof("Processing object: %s", object.GetName()) - if ownerRef := metav1.GetControllerOf(object); ownerRef != nil { - // If this object is not owned by a ClusterBus, we should not do anything more - // with it. - if ownerRef.Kind != "ClusterBus" { - return - } - - clusterBus, err := c.clusterBusesLister.Get(ownerRef.Name) - if err != nil { - glog.V(4).Infof("ignoring orphaned object '%s' of clusterbus '%s'", object.GetSelfLink(), ownerRef.Name) - return - } - - c.enqueueClusterBus(clusterBus) - return - } -} - -// newDispatcherService creates a new Service for a ClusterBus resource. It also sets -// the appropriate OwnerReferences on the resource so handleObject can discover -// the ClusterBus resource that 'owns' it. -func newDispatcherService(clusterBus *channelsv1alpha1.ClusterBus) *corev1.Service { - labels := dispatcherLabels(clusterBus.Name) - return &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: controller.ClusterBusDispatcherServiceName(clusterBus.ObjectMeta.Name), - Namespace: system.Namespace, - Labels: labels, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(clusterBus, schema.GroupVersionKind{ - Group: channelsv1alpha1.SchemeGroupVersion.Group, - Version: channelsv1alpha1.SchemeGroupVersion.Version, - Kind: "ClusterBus", - }), - }, - }, - Spec: corev1.ServiceSpec{ - Selector: labels, - Ports: []corev1.ServicePort{ - { - Name: "http", - Port: 80, - TargetPort: intstr.FromInt(buses.MessageReceiverPort), - }, - }, - }, - } -} - -// newDispatcherDeployment creates a new Deployment for a ClusterBus resource. It also sets -// the appropriate OwnerReferences on the resource so handleObject can discover -// the ClusterBus resource that 'owns' it. -func newDispatcherDeployment(clusterBus *channelsv1alpha1.ClusterBus) *appsv1.Deployment { - labels := dispatcherLabels(clusterBus.Name) - one := int32(1) - container := clusterBus.Spec.Dispatcher.DeepCopy() - container.Env = append(container.Env, - corev1.EnvVar{ - Name: "PORT", - Value: "8080", - }, - corev1.EnvVar{ - Name: "BUS_NAME", - Value: clusterBus.Name, - }, - ) - volumes := []corev1.Volume{} - if clusterBus.Spec.Volumes != nil { - volumes = *clusterBus.Spec.Volumes - } - return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: controller.ClusterBusDispatcherDeploymentName(clusterBus.ObjectMeta.Name), - Namespace: system.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(clusterBus, schema.GroupVersionKind{ - Group: channelsv1alpha1.SchemeGroupVersion.Group, - Version: channelsv1alpha1.SchemeGroupVersion.Version, - Kind: "ClusterBus", - }), - }, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: &one, - Selector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - "sidecar.istio.io/inject": "true", - }, - Labels: labels, - }, - Spec: corev1.PodSpec{ - ServiceAccountName: serviceAccountName, - Containers: []corev1.Container{ - *container, - }, - Volumes: volumes, - }, - }, - }, - } -} - -// newProvisionerDeployment creates a new Deployment for a ClusterBus resource. It also sets -// the appropriate OwnerReferences on the resource so handleObject can discover -// the ClusterBus resource that 'owns' it. -func newProvisionerDeployment(clusterBus *channelsv1alpha1.ClusterBus) *appsv1.Deployment { - labels := provisionerLabels(clusterBus.Name) - one := int32(1) - container := clusterBus.Spec.Provisioner.DeepCopy() - container.Env = append(container.Env, - corev1.EnvVar{ - Name: "BUS_NAME", - Value: clusterBus.Name, - }, - ) - volumes := []corev1.Volume{} - if clusterBus.Spec.Volumes != nil { - volumes = *clusterBus.Spec.Volumes - } - return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: controller.ClusterBusProvisionerDeploymentName(clusterBus.ObjectMeta.Name), - Namespace: system.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(clusterBus, schema.GroupVersionKind{ - Group: channelsv1alpha1.SchemeGroupVersion.Group, - Version: channelsv1alpha1.SchemeGroupVersion.Version, - Kind: "ClusterBus", - }), - }, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: &one, - Selector: &metav1.LabelSelector{ - MatchLabels: labels, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{ - "sidecar.istio.io/inject": "true", - }, - Labels: labels, - }, - Spec: corev1.PodSpec{ - ServiceAccountName: serviceAccountName, - Containers: []corev1.Container{ - *container, - }, - Volumes: volumes, - }, - }, - }, - } -} - -func dispatcherLabels(busName string) map[string]string { - return map[string]string{ - "clusterBus": busName, - "role": "dispatcher", - } -} - -func provisionerLabels(busName string) map[string]string { - return map[string]string{ - "clusterBus": busName, - "role": "provisioner", - } -} diff --git a/pkg/controller/clusterbus/stub_test.go b/pkg/controller/clusterbus/stub_test.go deleted file mode 100644 index a6d484c77fe..00000000000 --- a/pkg/controller/clusterbus/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package clusterbus - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/controller/eventtype/provider.go b/pkg/controller/eventtype/provider.go deleted file mode 100644 index 04adfdfca2b..00000000000 --- a/pkg/controller/eventtype/provider.go +++ /dev/null @@ -1,108 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package eventtype - -import ( - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "go.uber.org/zap" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/record" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" -) - -const ( - // controllerAgentName is the string used by this controller to identify - // itself when creating events. - controllerAgentName = "event-type-controller" -) - -type reconciler struct { - client client.Client - restConfig *rest.Config - recorder record.EventRecorder - logger *zap.Logger -} - -// Verify the struct implements reconcile.Reconciler -var _ reconcile.Reconciler = &reconciler{} - -// ProvideController returns a flow controller. -func ProvideController(mgr manager.Manager) (controller.Controller, error) { - logger, err := zap.NewProduction() - if err != nil { - return nil, err - } - logger.With(zap.String("controller", controllerAgentName)) - - // Setup a new controller to Reconcile Flows. - c, err := controller.New(controllerAgentName, mgr, controller.Options{ - Reconciler: &reconciler{ - recorder: mgr.GetRecorder(controllerAgentName), - logger: logger, - }, - }) - if err != nil { - return nil, err - } - - // Watch EventType events and enqueue EventType object key. - if err := c.Watch(&source.Kind{ - Type: &feedsv1alpha1.EventType{}}, - &handler.EnqueueRequestForObject{}); err != nil { - return nil, err - } - - // In addition to watching EventType objects, watch for Feeds, which use and 'pin' EventTypes. - err = c.Watch(&source.Kind{Type: &feedsv1alpha1.Feed{}}, - &handler.EnqueueRequestsFromMapFunc{ToRequests: feedToEventType{}}) - if err != nil { - return nil, err - } - - return c, nil -} - -func (r *reconciler) InjectClient(c client.Client) error { - r.client = c - return nil -} - -type feedToEventType struct{} - -func (_ feedToEventType) Map(obj handler.MapObject) []reconcile.Request { - feed, ok := obj.Object.(*feedsv1alpha1.Feed) - if !ok { - // This wasn't a Feed. - return []reconcile.Request{} - } - etName := feed.Spec.Trigger.EventType - - return []reconcile.Request{ - { - NamespacedName: types.NamespacedName{ - Namespace: obj.Meta.GetNamespace(), - Name: etName, - }, - }, - } -} diff --git a/pkg/controller/eventtype/provider_test.go b/pkg/controller/eventtype/provider_test.go deleted file mode 100644 index f205632afe4..00000000000 --- a/pkg/controller/eventtype/provider_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package eventtype - -import ( - "github.com/google/go-cmp/cmp" - "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "testing" -) - -const ( - feedName = "feed-name" -) - -type mapTestCase struct { - name string - obj handler.MapObject - rr []reconcile.Request -} - -func TestMap(t *testing.T) { - testCases := []mapTestCase{ - { - name: "not a Feed", - obj: handler.MapObject{ - Object: getPod(), - }, - rr: []reconcile.Request{}, - }, - { - name: "Feed", - obj: handler.MapObject{ - Meta: getFeed(etNamespace, feedName, etName), - Object: getFeed(etNamespace, feedName, etName), - }, - rr: []reconcile.Request{ - { - NamespacedName: types.NamespacedName{ - Namespace: etNamespace, - Name: etName, - }, - }, - }, - }, - } - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - feedToEventType := feedToEventType{} - actualRR := feedToEventType.Map(tc.obj) - if diff := cmp.Diff(tc.rr, actualRR); diff != "" { - t.Errorf("Reconcile request (-want, +got) = %v", diff) - } - }) - } -} - -func getPod() *v1.Pod { - return &v1.Pod{ - ObjectMeta: objectMeta(etNamespace, "pod-name"), - TypeMeta: metav1.TypeMeta{ - APIVersion: v1.SchemeGroupVersion.String(), - Kind: "Pod", - }, - } -} diff --git a/pkg/controller/eventtype/reconcile.go b/pkg/controller/eventtype/reconcile.go deleted file mode 100644 index a599a10a62c..00000000000 --- a/pkg/controller/eventtype/reconcile.go +++ /dev/null @@ -1,230 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package eventtype - -import ( - "context" - "fmt" - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/util/sets" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "strings" -) - -const ( - finalizerName = controllerAgentName -) - -// Reconcile compares the actual state of a Feed with the desired, and attempts -// to converge the two. It then updates the Status block of the Feed with -// its current state. -// If Reconcile returns a non-nil error, the request will be retried. -func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) { - //TODO use this to store the logger and set a deadline - ctx := context.TODO() - - et := &feedsv1alpha1.EventType{} - err := r.client.Get(ctx, request.NamespacedName, et) - - // The EventType may have been deleted since it was added to the workqueue. If so - // there's nothing to be done. - if errors.IsNotFound(err) { - r.logger.Error("could not find EventType", zap.Any("request", request)) - return reconcile.Result{}, nil - } - - // If the EventType exists but could not be retrieved, then we should retry. - if err != nil { - r.logger.Error("could not fetch EventType", - zap.Any("request", request), - zap.Error(err)) - return reconcile.Result{}, err - } - - // Now that we know the EventType exists, we can reconcile it. An error returned - // here means the reconcile did not complete and the EventType should be requeued - // for another attempt. - // A successful reconcile does not necessarily mean the EventType is in the desired - // state, it means no more can be done for now. In this case the EventType will - // not be reconciled again until the resync period or a watched resource changes. - if err = r.reconcile(ctx, et); err != nil { - r.logger.Error("error reconciling EventType", - zap.Any("EventType", et), - zap.Error(err)) - // Note that we do not return the error here. That is because we rely on r.updateEventType() - // to write any updated status to the API server. After updating the API server, then we - // should return this error. - } - - // Since the reconcile is a sequence of steps, earlier steps may complete - // successfully while later steps fail. The EventType is updated on failure to - // preserve any useful status or metadata changes the non-failing steps made. - if updateErr := r.updateEventType(ctx, et); updateErr != nil { - r.logger.Error("failed to update EventType", - zap.Any("EventType", et), - zap.Error(updateErr)) - // An error here means the EventType should be reconciled again, regardless of - // whether the reconcile was successful or not. - return reconcile.Result{}, updateErr - } - return reconcile.Result{}, err -} - -// reconcile tries to converge the current state of the given EventType to the desired state. This -// function should not update the EventType in the API server. This method will update 'et'. The -// calling method is responsible for writing back to the API server. -func (r *reconciler) reconcile(ctx context.Context, et *feedsv1alpha1.EventType) error { - if et.GetDeletionTimestamp() == nil { - r.addFinalizer(et) - } else { - err := r.handleDeletion(ctx, et) - if err != nil { - r.logger.Error("Error deleting the EventType", - zap.Any("EventType", et), - zap.Error(err)) - return err - } - } - return nil -} - -func (r *reconciler) addFinalizer(et *feedsv1alpha1.EventType) { - finalizers := sets.NewString(et.Finalizers...) - finalizers.Insert(finalizerName) - et.Finalizers = finalizers.List() -} - -func (r *reconciler) removeFinalizer(et *feedsv1alpha1.EventType) { - finalizers := sets.NewString(et.Finalizers...) - finalizers.Delete(finalizerName) - et.Finalizers = finalizers.List() -} - -// handleDeletion checks the finalizer conditions of an EventType marked for deletion. If the -// conditions are met, then it removes the finalizer. Otherwise it adds a Status saying why it -// can't. -func (r *reconciler) handleDeletion(ctx context.Context, et *feedsv1alpha1.EventType) error { - feeds, err := r.findFeedsUsingEventType(ctx, et) - if err != nil { - r.logger.Info("Unable to find Feeds using EventType", - zap.String("EventType.Name", et.Name), - zap.Error(err)) - return err - } - if len(feeds) == 0 { - r.removeFinalizer(et) - } else { - r.logger.Info("Cannot remove finalizer from EventType, Feed(s) still use it.", - zap.String("EventType.Name", et.Name), - zap.Int("feedsUsingEventType", len(feeds))) - } - r.updateInUseStatus(ctx, et, feeds) - return nil -} - -// findFeedsUsingEventTypes finds all the Feeds in the same namespace as et that use et. -func (r *reconciler) findFeedsUsingEventType(ctx context.Context, et *feedsv1alpha1.EventType) ( - []feedsv1alpha1.Feed, error) { - allFeeds := &feedsv1alpha1.FeedList{} - listOptions := client.InNamespace(et.Namespace) - - //TODO this is here because the fake client needs it. Remove this when it's - // no longer needed. - listOptions.Raw = &metav1.ListOptions{ - TypeMeta: metav1.TypeMeta{ - APIVersion: feedsv1alpha1.SchemeGroupVersion.String(), - Kind: "Feed", - }, - } - err := r.client.List(ctx, listOptions, allFeeds) - if err != nil { - r.logger.Error("Unable to list feeds", zap.Error(err)) - return nil, err - } - feeds := make([]feedsv1alpha1.Feed, 0, len(allFeeds.Items)) - - for _, feed := range allFeeds.Items { - if et.Name == feed.Spec.Trigger.EventType { - feeds = append(feeds, feed) - } - } - return feeds, nil -} - -func (r *reconciler) updateInUseStatus(ctx context.Context, et *feedsv1alpha1.EventType, feedsStillUsingEventType []feedsv1alpha1.Feed) { - // Filter out the existing InUse condition, if present. - var newConditions []feedsv1alpha1.CommonEventTypeCondition - for _, condition := range et.Status.Conditions { - if condition.Type != feedsv1alpha1.EventTypeInUse { - newConditions = append(newConditions, condition) - } - } - - if len(feedsStillUsingEventType) > 0 { - // Add the up-to-date InUse condition. - newConditions = append(newConditions, feedsv1alpha1.CommonEventTypeCondition{ - Type: feedsv1alpha1.EventTypeInUse, - Status: corev1.ConditionTrue, - Message: fmt.Sprintf("Still in use by the Feeds: %s", getFeedNames(feedsStillUsingEventType)), - }) - } - - et.Status.Conditions = newConditions -} - -// getFeedNames generates a single string with the names of all the feeds. -func getFeedNames(feeds []feedsv1alpha1.Feed) string { - feedNames := make([]string, 0, len(feeds)) - for _, feed := range feeds { - feedNames = append(feedNames, feed.Name) - } - return strings.Join(feedNames, ", ") -} - -func (r *reconciler) updateEventType(ctx context.Context, u *feedsv1alpha1.EventType) error { - et := &feedsv1alpha1.EventType{} - err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, et) - if err != nil { - return err - } - - updated := false - if !equality.Semantic.DeepEqual(et.Finalizers, u.Finalizers) { - et.SetFinalizers(u.ObjectMeta.Finalizers) - updated = true - } - - if !equality.Semantic.DeepEqual(et.Status, u.Status) { - et.Status = u.Status - updated = true - } - - if updated == false { - return nil - } - // Until #38113 is merged, we must use Update instead of UpdateStatus to - // update the Status block of the Feed resource. UpdateStatus will not - // allow changes to the Spec of the resource, which is ideal for ensuring - // nothing other than resource status has been updated. - return r.client.Update(ctx, et) -} diff --git a/pkg/controller/eventtype/reconcile_test.go b/pkg/controller/eventtype/reconcile_test.go deleted file mode 100644 index c0d1447758a..00000000000 --- a/pkg/controller/eventtype/reconcile_test.go +++ /dev/null @@ -1,299 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package eventtype - -import ( - "context" - "errors" - "fmt" - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - controllertesting "github.com/knative/eventing/pkg/controller/testing" - "go.uber.org/zap" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/tools/record" - "sigs.k8s.io/controller-runtime/pkg/client" - "testing" -) - -var ( - // deletionTime is used when objects are marked as deleted. Rfc3339Copy() - // truncates to seconds to match the loss of precision during serialization. - deletionTime = metav1.Now().Rfc3339Copy() -) - -const ( - eventSourceName = "event-source" - etNamespace = "test-namespace" - etName = "test-event-type" - reconcileKey = etNamespace + "/" + etName - - fn1 = "fake-feed-name-1" - fn2 = "fake-feed-name-2" - - getError = "test induced error getting" - listError = "test induced error listing" - updateError = "test induced error updating" -) - -func init() { - // Add types to scheme - feedsv1alpha1.AddToScheme(scheme.Scheme) -} - -func TestAllCases(t *testing.T) { - testCases := []controllertesting.TestCase{ - { - Name: "missing EventType", - ReconcileKey: reconcileKey, - }, - { - Name: "Internal error getting EventType", - ReconcileKey: reconcileKey, - WantErrMsg: getError, - Mocks: controllertesting.Mocks{ - MockGets: []controllertesting.MockGet{ - func(_ client.Client, _ context.Context, _ client.ObjectKey, _ runtime.Object) (controllertesting.MockHandled, error) { - return controllertesting.Handled, errors.New(getError) - }, - }, - }, - }, - { - Name: "new EventType: adds Finalizer", - InitialState: []runtime.Object{ - getEventType(false), - }, - ReconcileKey: reconcileKey, - WantPresent: []runtime.Object{ - getEventType(true), - }, - }, - { - Name: "tries to add finalizer -- second EventType.Get() errors", - InitialState: []runtime.Object{ - getEventType(false), - }, - WantPresent: []runtime.Object{ - // Unable to update the EventType, it should still be in the initial state. - getEventType(false), - }, - ReconcileKey: reconcileKey, - WantErrMsg: getError, - Mocks: controllertesting.Mocks{ - MockGets: []controllertesting.MockGet{ - // The first Get should pass through to the inner client. - func(innerClient client.Client, ctx context.Context, key client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { - realResponse := innerClient.Get(ctx, key, obj) - return controllertesting.Handled, realResponse - }, - // The second Get, which runs inside the Update logic, should error. - func(_ client.Client, _ context.Context, _ client.ObjectKey, _ runtime.Object) (controllertesting.MockHandled, error) { - return controllertesting.Handled, errors.New(getError) - }, - }, - }, - }, - { - Name: "tries to add finalizer -- EventType.Update() errors", - InitialState: []runtime.Object{ - getEventType(false), - }, - WantPresent: []runtime.Object{ - // Unable to update the EventType, it should still be in the initial state. - getEventType(false), - }, - ReconcileKey: reconcileKey, - WantErrMsg: updateError, - Mocks: controllertesting.Mocks{ - MockUpdates: []controllertesting.MockUpdate{ - func(_ client.Client, _ context.Context, _ runtime.Object) (controllertesting.MockHandled, error) { - return controllertesting.Handled, errors.New(updateError) - }, - }, - }, - }, - { - Name: "old EventType: already has Finalizer", - InitialState: []runtime.Object{ - getEventType(true), - }, - ReconcileKey: reconcileKey, - WantPresent: []runtime.Object{ - getEventType(true), - }, - }, - { - Name: "deleting EventType: no Feeds", - InitialState: []runtime.Object{ - getDeletingEventType(true), - }, - ReconcileKey: reconcileKey, - WantPresent: []runtime.Object{ - // The Finalizer should have been removed. - getDeletingEventType(false), - }, - }, - { - Name: "deleting EventType: Feeds not using EventType", - InitialState: []runtime.Object{ - getDeletingEventType(true), - getFeedUsingOtherEventType(), - }, - ReconcileKey: reconcileKey, - WantPresent: []runtime.Object{ - // The Finalizer should have been removed. - getDeletingEventType(false), - }, - }, { - Name: "deleting EventType: Feeds using EventType in different namespace", - InitialState: []runtime.Object{ - getDeletingEventType(true), - getFeedUsingEventTypeInDifferentNamespace(), - }, - ReconcileKey: reconcileKey, - WantPresent: []runtime.Object{ - // The Finalizer should have been removed. - getDeletingEventType(false), - }, - }, - { - Name: "deleting EventType: Feeds using EventType", - InitialState: []runtime.Object{ - getDeletingEventTypeWithCompleteStatus(), - getFeedUsingEventType(fn1), - getFeedUsingEventType(fn2), - }, - ReconcileKey: reconcileKey, - WantPresent: []runtime.Object{ - // There are Feeds still using the EventType, it should still have its Finalizer and a - // new Status added stating which Feeds are blocking its deletion. - getDeletingEventTypeWithCompleteAndInUseStatus(fn1 + ", " + fn2), - }, - }, - { - Name: "deleting EventType: can't list Feeds", - InitialState: []runtime.Object{ - getDeletingEventType(true), - }, - ReconcileKey: reconcileKey, - WantPresent: []runtime.Object{ - // Unable to update the EventType, it should still be in the initial state. - getDeletingEventType(true), - }, - WantErrMsg: listError, - Mocks: controllertesting.Mocks{ - MockLists: []controllertesting.MockList{ - func(_ client.Client, _ context.Context, _ *client.ListOptions, _ runtime.Object) (controllertesting.MockHandled, error) { - return controllertesting.Handled, errors.New(listError) - }, - }, - }, - }, - } - recorder := record.NewBroadcaster().NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) - - for _, tc := range testCases { - c := tc.GetClient() - r := &reconciler{ - client: c, - recorder: recorder, - logger: zap.NewNop(), - } - t.Run(tc.Name, tc.Runner(t, r, c)) - } -} - -func objectMeta(namespace, name string) metav1.ObjectMeta { - return metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - SelfLink: fmt.Sprintf("/apis/eventing/v1alpha1/namespaces/%s/object/%s", namespace, name), - } -} - -func getEventType(finalizer bool) *feedsv1alpha1.EventType { - et := &feedsv1alpha1.EventType{ - TypeMeta: metav1.TypeMeta{ - APIVersion: feedsv1alpha1.SchemeGroupVersion.String(), - Kind: "EventType", - }, - ObjectMeta: objectMeta(etNamespace, etName), - Spec: feedsv1alpha1.EventTypeSpec{ - EventSource: eventSourceName, - }, - } - if finalizer { - et.Finalizers = []string{finalizerName} - } - return et -} - -func getDeletingEventType(finalizer bool) *feedsv1alpha1.EventType { - et := getEventType(finalizer) - et.ObjectMeta.DeletionTimestamp = &deletionTime - return et -} - -func getDeletingEventTypeWithCompleteStatus() *feedsv1alpha1.EventType { - et := getDeletingEventType(true) - et.Status.Conditions = append(et.Status.Conditions, feedsv1alpha1.CommonEventTypeCondition{ - Type: feedsv1alpha1.EventTypeComplete, - Status: corev1.ConditionTrue, - }) - return et -} - -func getDeletingEventTypeWithCompleteAndInUseStatus(feedNames string) *feedsv1alpha1.EventType { - et := getDeletingEventTypeWithCompleteStatus() - et.Status.Conditions = append(et.Status.Conditions, feedsv1alpha1.CommonEventTypeCondition{ - Type: feedsv1alpha1.EventTypeInUse, - Status: corev1.ConditionTrue, - Message: "Still in use by the Feeds: " + feedNames, - }) - return et -} - -func getFeed(namespace, name, eventType string) *feedsv1alpha1.Feed { - feed := &feedsv1alpha1.Feed{ - TypeMeta: metav1.TypeMeta{ - APIVersion: feedsv1alpha1.SchemeGroupVersion.String(), - Kind: "Feed", - }, - ObjectMeta: objectMeta(namespace, name), - Spec: feedsv1alpha1.FeedSpec{ - Trigger: feedsv1alpha1.EventTrigger{ - EventType: eventType, - }, - }, - } - return feed -} - -func getFeedUsingOtherEventType() *feedsv1alpha1.Feed { - return getFeed(etNamespace, "feed-not-using-event-type", "some-other-event-type") -} - -func getFeedUsingEventType(feedName string) *feedsv1alpha1.Feed { - return getFeed(etNamespace, feedName, etName) -} - -func getFeedUsingEventTypeInDifferentNamespace() *feedsv1alpha1.Feed { - return getFeed("some-other-namespace", "feed-in-different-namespace", etName) -} diff --git a/pkg/controller/feed/errors.go b/pkg/controller/feed/errors.go deleted file mode 100644 index db4a451d14f..00000000000 --- a/pkg/controller/feed/errors.go +++ /dev/null @@ -1,69 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Veroute.on 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 feed - -const ( - // EventSourceDoesNotExist is a condition reason used when an EventSource - // referenced by a Feed does not exist. - EventSourceDoesNotExist string = "EventSourceDoesNotExist" - - // EventSourceDeleting is a condition reason used when an EventSource - // referenced by a feed has been marked for deletion. - EventSourceDeleting string = "EventSourceDeleting" - - // EventTypeDoesNotExist is a condition reason used when an EventType - // referenced by a Feed does not exist. - EventTypeDoesNotExist string = "EventTypeDoesNotExist" - - // EventTypeDeleting is a condition reason used when an EventType - // referenced by a feed has been marked for deletion. - EventTypeDeleting string = "EventTypeDeleting" -) - -// StatusError can be returned from lower level functions and used as the input -// for setting conditions on the Feed. -type StatusError struct { - // Reason is a one-word CamelCase reason for the failure. - Reason string - // Message is a human-readable message describing the error. - Message string -} - -// Error implements the error interface. -func (se *StatusError) Error() string { - return se.Message -} - -// EventSourceError is a StatusError specifically for EventSource errors. -type EventSourceError struct { - StatusError -} - -// Error implements the error interface. -func (es *EventSourceError) Error() string { - return es.StatusError.Error() -} - -// EventTypeError is a StatusError specifically for EventType errors. -type EventTypeError struct { - StatusError -} - -// Error implements the error interface. -func (es *EventTypeError) Error() string { - return es.StatusError.Error() -} diff --git a/pkg/controller/feed/provider.go b/pkg/controller/feed/provider.go deleted file mode 100644 index 75f075f3701..00000000000 --- a/pkg/controller/feed/provider.go +++ /dev/null @@ -1,71 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package feed - -import ( - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - batchv1 "k8s.io/api/batch/v1" - "k8s.io/client-go/tools/record" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" -) - -const controllerAgentName = "feed-controller" - -type reconciler struct { - client client.Client - recorder record.EventRecorder -} - -// Verify the struct implements reconcile.Reconciler -var _ reconcile.Reconciler = &reconciler{} - -// ProvideController returns a Feed controller and adds it to the given Manager. -func ProvideController(mgr manager.Manager) (controller.Controller, error) { - // Setup a new controller to Reconcile Feeds. - c, err := controller.New(controllerAgentName, mgr, controller.Options{ - Reconciler: &reconciler{ - recorder: mgr.GetRecorder(controllerAgentName), - }, - }) - if err != nil { - return nil, err - } - - // Watch Feed events and enqueue Feed object key. - if err := c.Watch(&source.Kind{Type: &feedsv1alpha1.Feed{}}, &handler.EnqueueRequestForObject{}); err != nil { - return nil, err - } - - // Watch Jobs and enqueue owning Feed key. - if err := c.Watch(&source.Kind{Type: &batchv1.Job{}}, - &handler.EnqueueRequestForOwner{OwnerType: &feedsv1alpha1.Feed{}, IsController: true}); err != nil { - return nil, err - } - - return c, nil -} - -// InjectClient is called by the injector to set the reconciler's client field. -func (r *reconciler) InjectClient(c client.Client) error { - r.client = c - return nil -} diff --git a/pkg/controller/feed/reconcile.go b/pkg/controller/feed/reconcile.go deleted file mode 100644 index 1f9048168cd..00000000000 --- a/pkg/controller/feed/reconcile.go +++ /dev/null @@ -1,565 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Veroute.on 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 feed - -import ( - "context" - "encoding/base64" - "encoding/json" - "fmt" - - "github.com/golang/glog" - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "github.com/knative/eventing/pkg/controller/feed/resources" - "github.com/knative/eventing/pkg/sources" - "gopkg.in/yaml.v2" - batchv1 "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -const ( - finalizerName = controllerAgentName -) - -// Reconcile compares the actual state of a Feed with the desired, and attempts -// to converge the two. It then updates the Status block of the Feed with -// its current state. -// If Reconcile returns a non-nil error, the request will be retried. -func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) { - //TODO use this to store the logger and set a deadline - ctx := context.TODO() - - feed := &feedsv1alpha1.Feed{} - err := r.client.Get(ctx, request.NamespacedName, feed) - - // The feed may have been deleted since it was added to the workqueue. If so - // there's nothing to be done. - if errors.IsNotFound(err) { - glog.Errorf("could not find Feed %v\n", request) - return reconcile.Result{}, nil - } - - // If the feed exists but could not be retrieved, then we should retry. - if err != nil { - glog.Errorf("could not fetch Feed %v for %+v\n", err, request) - return reconcile.Result{}, err - } - - // Now that we know the feed exists, we can reconcile it. An error returned - // here means the reconcile did not complete and the Feed should be requeued - // for another attempt. - // A successful reconcile does not necessarily mean the feed is in the desired - // state, it means no more can be done for now. In this case the feed will - // not be reconciled again until the resync period or a watched resource - // changes. - if err = r.reconcile(ctx, feed); err != nil { - glog.Errorf("error reconciling Feed: %v", err) - } - - // Since the reconcile is a sequence of steps, earlier steps may complete - // successfully while later steps fail. The Feed is updated on failure to - // preserve any useful status or metadata changes the non-failing steps made. - if updateErr := r.updateFeed(ctx, feed); updateErr != nil { - glog.Errorf("failed to update Feed: %v", updateErr) - // An error here means the feed should be reconciled again, regardless of - // whether the reconcile was successful or not. - return reconcile.Result{}, updateErr - } - return reconcile.Result{}, err -} - -// reconcile tries to converge the current state of the given Feed to the -// desired state. This function should not update the Feed; the calling method -// should do that. -func (r *reconciler) reconcile(ctx context.Context, feed *feedsv1alpha1.Feed) error { - feed.Status.InitializeConditions() - // Fetch the EventSource and EventType that is being asked for - // and if they don't exist update the status to reflect this back - // to the user. - eventSource, eventType, err := r.getFeedSource(ctx, feed) - if err != nil { - switch t := err.(type) { - case *EventSourceError: - glog.Errorf("eventsource can not be used as a target : %s", err) - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionDependenciesSatisfied, - Status: corev1.ConditionFalse, - Reason: t.Reason, - Message: t.Message, - }) - case *EventTypeError: - glog.Errorf("eventtype can not be used as a target : %s", err) - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionDependenciesSatisfied, - Status: corev1.ConditionFalse, - Reason: t.Reason, - Message: t.Message, - }) - default: - // This is unreachable unless getFeedSource is refactored. - // Assume this is a non-terminal state and return the error. - return fmt.Errorf("error getting feed source: %v", err) - } - // This is a terminal state, and we've noted it in the status, so return - // nil to signal that no further reconciling is required. - return nil - } - - // TODO: Set the FeedConditionDependenciesSatisfied to true here? Or, after - // job finishes? For now, the above conveys enough information to the user - // to make sure if they make a typo they will get that relayed back to them. - // IF we make it here, clear the condition in case they actually fixed the problem - // say, by installing an event provider. - feed.Status.RemoveCondition(feedsv1alpha1.FeedConditionDependenciesSatisfied) - - // Once we found the actual type, set the owner reference for it. - // TODO: Remove this and use finalizers in the EventTypes / EventSources - // to do this properly. - // TODO: Add issue link here. can't look up right now, no wifi - r.setEventTypeOwnerReference(ctx, feed) - - if feed.GetDeletionTimestamp() == nil { - err = r.reconcileStartJob(ctx, feed, eventSource, eventType) - if err != nil { - glog.Errorf("error reconciling start Job: %v", err) - } - } else { - err = r.reconcileStopJob(ctx, feed, eventSource, eventType) - if err != nil { - glog.Errorf("error reconciling stop Job: %v", err) - } - } - return nil -} - -// reconcileStartJob creates a start Job if one doesn't exist, checks the status -// of the start Job, and updates the Feed status accordingly. -func (r *reconciler) reconcileStartJob(ctx context.Context, feed *feedsv1alpha1.Feed, es *feedsv1alpha1.EventSource, et *feedsv1alpha1.EventType) error { - bc := feed.Status.GetCondition(feedsv1alpha1.FeedConditionReady) - switch bc.Status { - case corev1.ConditionUnknown: - - job := &batchv1.Job{} - jobName := resources.JobName(feed) - - if err := r.client.Get(ctx, client.ObjectKey{Namespace: feed.Namespace, Name: jobName}, job); err != nil { - if errors.IsNotFound(err) { - job, err = r.createJob(ctx, feed, es, et) - if err != nil { - return err - } - r.recorder.Eventf(feed, corev1.EventTypeNormal, "StartJobCreated", "Created start job %q", job.Name) - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionUnknown, - Reason: "StartJob", - Message: "start job in progress", - }) - } - } - feed.AddFinalizer(finalizerName) - - if resources.IsJobComplete(job) { - r.recorder.Eventf(feed, corev1.EventTypeNormal, "StartJobCompleted", "Start job %q completed", job.Name) - if err := r.setFeedContext(ctx, feed, job); err != nil { - return err - } - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionTrue, - Reason: "FeedSuccess", - Message: "start job succeeded", - }) - } else if resources.IsJobFailed(job) { - r.recorder.Eventf(feed, corev1.EventTypeWarning, "StartJobFailed", "Start job %q failed: %q", job.Name, resources.JobFailedMessage(job)) - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionFalse, - Reason: "FeedFailed", - Message: fmt.Sprintf("Job failed with %s", resources.JobFailedMessage(job)), - }) - } - - case corev1.ConditionTrue: - //TODO delete job - } - return nil -} - -// reconcileStopJob deletes the start Job if it exists, creates a stop Job if -// one doesn't exist, checks the status of the stop Job, and updates the Feed -// status accordingly. -func (r *reconciler) reconcileStopJob(ctx context.Context, feed *feedsv1alpha1.Feed, es *feedsv1alpha1.EventSource, et *feedsv1alpha1.EventType) error { - if feed.HasFinalizer(finalizerName) { - - // check for an existing start Job - job := &batchv1.Job{} - jobName := resources.StartJobName(feed) - - err := r.client.Get(ctx, client.ObjectKey{Namespace: feed.Namespace, Name: jobName}, job) - if err != nil && !errors.IsNotFound(err) { - return err - } - if err == nil { - // Delete the existing job and return. When it's deleted, this Feed - // will be reconciled again. - glog.Infof("Found existing start job: %s/%s", job.Namespace, job.Name) - - // Need to delete pods first to workaround the client's lack of support - // for cascading deletes. TODO remove this when client support allows. - if err = r.deleteJobPods(ctx, job); err != nil { - return err - } - r.client.Delete(ctx, job) - glog.Infof("Deleted start job: %s/%s", job.Namespace, job.Name) - return nil - } - - jobName = resources.JobName(feed) - - if err := r.client.Get(ctx, client.ObjectKey{Namespace: feed.Namespace, Name: jobName}, job); err != nil { - if errors.IsNotFound(err) { - job, err = r.createJob(ctx, feed, es, et) - if err != nil { - return err - } - r.recorder.Eventf(feed, corev1.EventTypeNormal, "StopJobCreated", "Created stop job %q", job.Name) - //TODO check for event source not found and remove finalizer - - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionUnknown, - Reason: "StopJob", - Message: "stop job in progress", - }) - } - } - - if resources.IsJobComplete(job) { - r.recorder.Eventf(feed, corev1.EventTypeNormal, "StopJobCompleted", "Stop job %q completed", job.Name) - feed.RemoveFinalizer(finalizerName) - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionTrue, - Reason: "FeedSuccess", - Message: "stop job succeeded", - }) - } else if resources.IsJobFailed(job) { - r.recorder.Eventf(feed, corev1.EventTypeWarning, "StopJobFailed", "Stop job %q failed: %q", job.Name, resources.JobFailedMessage(job)) - glog.Warningf("Stop job %q failed, removing finalizer on feed %q anyway.", job.Name, feed.Name) - feed.RemoveFinalizer(finalizerName) - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionFalse, - Reason: "FeedFailed", - Message: fmt.Sprintf("Job failed with %s", resources.JobFailedMessage(job)), - }) - } - } - return nil -} - -// updateFeed updates the given Feed's owner references, finalizers, and status -// to the given values. It skips the update if none of those values would change. -func (r *reconciler) updateFeed(ctx context.Context, u *feedsv1alpha1.Feed) error { - feed := &feedsv1alpha1.Feed{} - err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, feed) - if err != nil { - return err - } - - updated := false - if !equality.Semantic.DeepEqual(feed.OwnerReferences, u.OwnerReferences) { - feed.SetOwnerReferences(u.ObjectMeta.OwnerReferences) - updated = true - } - - if !equality.Semantic.DeepEqual(feed.Finalizers, u.Finalizers) { - feed.SetFinalizers(u.ObjectMeta.Finalizers) - updated = true - } - - if !equality.Semantic.DeepEqual(feed.Status, u.Status) { - feed.Status = u.Status - updated = true - } - - if updated == false { - return nil - } - // Until #38113 is merged, we must use Update instead of UpdateStatus to - // update the Status block of the Feed resource. UpdateStatus will not - // allow changes to the Spec of the resource, which is ideal for ensuring - // nothing other than resource status has been updated. - return r.client.Update(ctx, feed) -} - -// getEventTypeName returns the name of the Feed's referenced EventType or -// ClusterEventType. -func (r *reconciler) getEventTypeName(feed *feedsv1alpha1.Feed) string { - if len(feed.Spec.Trigger.EventType) > 0 { - return feed.Spec.Trigger.EventType - } else if len(feed.Spec.Trigger.ClusterEventType) > 0 { - return feed.Spec.Trigger.ClusterEventType - } - return "" -} - -// setEventTypeOwnerReference makes the given Feed's referenced ClusterEventType a non-controlling -// owner of the Feed. -func (r *reconciler) setEventTypeOwnerReference(ctx context.Context, feed *feedsv1alpha1.Feed) error { - // Namespaced EventTypes use a finalizer and do not need an Owners reference. - if len(feed.Spec.Trigger.ClusterEventType) > 0 { - return r.setClusterEventTypeOwnerReference(ctx, feed) - } - return nil -} - -// setEventTypeOwnerReference makes the given Feed's referenced ClusterEventType -// a non-controlling owner of the Feed. -func (r *reconciler) setClusterEventTypeOwnerReference(ctx context.Context, feed *feedsv1alpha1.Feed) error { - et := &feedsv1alpha1.ClusterEventType{} - if err := r.client.Get(ctx, client.ObjectKey{Name: feed.Spec.Trigger.ClusterEventType}, et); err != nil { - if errors.IsNotFound(err) { - glog.Errorf("Feed ClusterEventType not found, will not set finalizer") - return nil - } - return err - } - - blockOwnerDeletion := true - isController := false - ref := metav1.NewControllerRef(et, feedsv1alpha1.SchemeGroupVersion.WithKind("ClusterEventType")) - ref.BlockOwnerDeletion = &blockOwnerDeletion - ref.Controller = &isController - - feed.SetOwnerReference(ref) - return nil -} - -// resolveTrigger extracts the trigger from the Feed, reifies the parameters, -// and turns it all into an EventTrigger. -func (r *reconciler) resolveTrigger(ctx context.Context, feed *feedsv1alpha1.Feed) (sources.EventTrigger, error) { - trigger := feed.Spec.Trigger - resolved := sources.EventTrigger{ - Resource: trigger.Resource, - EventType: r.getEventTypeName(feed), - Parameters: make(map[string]interface{}), - } - - if trigger.Parameters != nil && trigger.Parameters.Raw != nil && len(trigger.Parameters.Raw) > 0 { - p := make(map[string]interface{}) - if err := yaml.Unmarshal(trigger.Parameters.Raw, &p); err != nil { - return resolved, err - } - for k, v := range p { - resolved.Parameters[k] = v - } - } - if trigger.ParametersFrom != nil { - glog.Infof("fetching from source %+v", trigger.ParametersFrom) - for _, p := range trigger.ParametersFrom { - pfs, err := r.fetchParametersFromSource(ctx, feed.Namespace, &p) - if err != nil { - return resolved, err - } - for k, v := range pfs { - resolved.Parameters[k] = v - } - } - } - return resolved, nil -} - -// fetchParametersFromSource gets the secret value referenced by the given -// ParametersFrom and returns it as a string-keyed map. -func (r *reconciler) fetchParametersFromSource(ctx context.Context, namespace string, parametersFrom *feedsv1alpha1.ParametersFromSource) (map[string]interface{}, error) { - var params map[string]interface{} - if parametersFrom.SecretKeyRef != nil { - glog.Infof("fetching secret %+v", parametersFrom.SecretKeyRef) - data, err := r.fetchSecretKeyValue(ctx, namespace, parametersFrom.SecretKeyRef) - if err != nil { - return nil, err - } - - if err := json.Unmarshal(data, ¶ms); err != nil { - return nil, fmt.Errorf("failed to unmarshal parameters as JSON object: %v", err) - } - } - return params, nil -} - -// fetchSecretKeyValue gets the Secret referenced and returns the data in the -// referenced key. -func (r *reconciler) fetchSecretKeyValue(ctx context.Context, namespace string, secretKeyRef *feedsv1alpha1.SecretKeyReference) ([]byte, error) { - secret := &corev1.Secret{} - err := r.client.Get(ctx, client.ObjectKey{Namespace: namespace, Name: secretKeyRef.Name}, secret) - if err != nil { - return nil, err - } - return secret.Data[secretKeyRef.Key], nil -} - -// createJob creates a Job for the given Feed based on its current state, -// returning the created Job. -func (r *reconciler) createJob(ctx context.Context, feed *feedsv1alpha1.Feed, es *feedsv1alpha1.EventSource, et *feedsv1alpha1.EventType) (*batchv1.Job, error) { - trigger, err := r.resolveTrigger(ctx, feed) - if err != nil { - return nil, err - } - - job, err := resources.MakeJob(feed, es, trigger, feed.Spec.Action.DNSName) - if err != nil { - return nil, err - } - - if err := r.client.Create(ctx, job); err != nil { - return nil, err - } - return job, nil -} - -// setFeedContext sets the Feed's context from the context emitted by the given -// Job. -func (r *reconciler) setFeedContext(ctx context.Context, feed *feedsv1alpha1.Feed, job *batchv1.Job) error { - feedContext, err := r.getJobContext(ctx, job) - if err != nil { - return err - } - - marshalledFeedContext, err := json.Marshal(&feedContext.Context) - if err != nil { - return err - } - feed.Status.FeedContext = &runtime.RawExtension{ - Raw: marshalledFeedContext, - } - - return nil -} - -// getJobContext returns the FeedContext emitted by the first successful pod -// owned by this job. The feed context is extracted from the termination -// message of the first container in the pod. -func (r *reconciler) getJobContext(ctx context.Context, job *batchv1.Job) (*sources.FeedContext, error) { - pods, err := r.getJobPods(ctx, job) - if err != nil { - return nil, err - } - - for _, p := range pods { - if p.Status.Phase == corev1.PodSucceeded { - glog.Infof("Pod succeeded: %s", p.Name) - if msg := resources.GetFirstTerminationMessage(&p); msg != "" { - decodedContext, _ := base64.StdEncoding.DecodeString(msg) - glog.Infof("decoded to %q", decodedContext) - var ret sources.FeedContext - err = json.Unmarshal(decodedContext, &ret) - if err != nil { - glog.Errorf("failed to unmarshal context: %s", err) - return nil, err - } - return &ret, nil - } - } - } - return &sources.FeedContext{}, nil -} - -// getJobPods returns the array of Pods owned by the given Job. -func (r *reconciler) getJobPods(ctx context.Context, job *batchv1.Job) ([]corev1.Pod, error) { - podList := &corev1.PodList{} - listOptions := client. - InNamespace(job.Namespace). - MatchingLabels(job.Spec.Selector.MatchLabels) - - //TODO this is here because the fake client needs it. Remove this when it's - // no longer needed. - listOptions.Raw = &metav1.ListOptions{ - TypeMeta: metav1.TypeMeta{ - APIVersion: corev1.SchemeGroupVersion.String(), - Kind: "Pod", - }, - } - - if err := r.client.List(ctx, listOptions, podList); err != nil { - return nil, err - } - - return podList.Items, nil -} - -func (r *reconciler) deleteJobPods(ctx context.Context, job *batchv1.Job) error { - pods, err := r.getJobPods(ctx, job) - if err != nil { - return err - } - - for _, pod := range pods { - if err := r.client.Delete(ctx, &pod); err != nil { - return err - } - glog.Infof("Deleted start job pod: %s/%s", pod.Namespace, pod.Name) - } - return nil -} - -// getFeedSource gets the EventSource and EventType that the trigger is targeting and -// returns them. If either the source or type is not found or is in the deleting state -// returns an error of the proper type. -func (r *reconciler) getFeedSource(ctx context.Context, feed *feedsv1alpha1.Feed) (*feedsv1alpha1.EventSource, *feedsv1alpha1.EventType, error) { - es := &feedsv1alpha1.EventSource{} - if err := r.client.Get(ctx, client.ObjectKey{Namespace: feed.Namespace, Name: feed.Spec.Trigger.Service}, es); err != nil { - if errors.IsNotFound(err) { - msg := fmt.Sprintf("EventSource %s/%s does not exist", feed.Namespace, feed.Spec.Trigger.Service) - glog.Info(msg) - return nil, nil, &EventSourceError{StatusError{EventSourceDoesNotExist, msg}} - } - return nil, nil, err - } - - if feed.GetDeletionTimestamp() == nil && es.GetDeletionTimestamp() != nil { - // EventSource is being deleted so don't allow non-deleting Feeds to use it. - msg := fmt.Sprintf("EventSource %s/%s is being deleted", feed.Namespace, feed.Spec.Trigger.Service) - glog.Info(msg) - return nil, nil, &EventSourceError{StatusError{EventSourceDeleting, msg}} - } - - et := &feedsv1alpha1.EventType{} - if err := r.client.Get(ctx, client.ObjectKey{Namespace: feed.Namespace, Name: feed.Spec.Trigger.EventType}, et); err != nil { - if errors.IsNotFound(err) { - msg := fmt.Sprintf("EventType %s/%s does not exist", feed.Namespace, feed.Spec.Trigger.EventType) - glog.Info(msg) - return nil, nil, &EventTypeError{StatusError{EventTypeDoesNotExist, msg}} - } - return nil, nil, err - } - - if feed.GetDeletionTimestamp() == nil && et.GetDeletionTimestamp() != nil { - // EventType is being deleted so don't allow non-deleting Feeds to use it. - msg := fmt.Sprintf("EventType %s/%s is being deleted", feed.Namespace, feed.Spec.Trigger.EventType) - glog.Info(msg) - return nil, nil, &EventTypeError{StatusError{EventTypeDeleting, msg}} - } - return es, et, nil -} diff --git a/pkg/controller/feed/reconcile_test.go b/pkg/controller/feed/reconcile_test.go deleted file mode 100644 index 0ced4c73d86..00000000000 --- a/pkg/controller/feed/reconcile_test.go +++ /dev/null @@ -1,903 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Veroute.on 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 feed - -import ( - "context" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "testing" - - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "github.com/knative/eventing/pkg/controller/feed/resources" - controllertesting "github.com/knative/eventing/pkg/controller/testing" - "github.com/knative/eventing/pkg/sources" - batchv1 "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/tools/record" -) - -/* -TODO -- initial: feed with job deadline exceeded - reconciled: feed failure, job exists, finalizer -*/ - -var ( - trueVal = true - // deletionTime is used when objects are marked as deleted. Rfc3339Copy() - // truncates to seconds to match the loss of precision during serialization. - deletionTime = metav1.Now().Rfc3339Copy() -) - -const ( - targetDNS = "myservice.mynamespace.svc.cluster.local" - - getError = "test induced error getting" - createError = "test induced error creating" - - jobFailedReason = "test induced job failure reason" - jobFailedMessage = "test induced job failure message" -) - -func init() { - // Add types to scheme - feedsv1alpha1.AddToScheme(scheme.Scheme) -} - -func TestAllCases(t *testing.T) { - testCases := []controllertesting.TestCase{ - { - Name: "Feed not found", - // This should return a successful reconcile result immediately. - WantErr: false, - WantResult: reconcile.Result{}, - }, - { - Name: "Internal error getting Feed", - WantErrMsg: getError, - Mocks: controllertesting.Mocks{ - MockGets: []controllertesting.MockGet{ - func(_ client.Client, _ context.Context, _ client.ObjectKey, _ runtime.Object) (controllertesting.MockHandled, error) { - return controllertesting.Handled, errors.New(getError) - }, - }, - }, - }, - { - Name: "new feed: adds status, finalizer, creates job", - InitialState: []runtime.Object{ - getEventSource(), - getEventType(), - getNewFeed(), - }, - WantPresent: []runtime.Object{ - getStartInProgressFeed(), - getNewStartJob(), - //TODO job created event - }, - }, { - Name: "new feed: adds status, finalizer, create job fails", - InitialState: []runtime.Object{ - getEventSource(), - getEventType(), - getNewFeed(), - }, - WantPresent: []runtime.Object{ - getReadyUnknownFeed(), - //TODO job created event - }, - Mocks: controllertesting.Mocks{ - MockCreates: []controllertesting.MockCreate{ - func(_ client.Client, _ context.Context, obj runtime.Object) (controllertesting.MockHandled, error) { - // Return an error when creating a Job. - if _, ok := obj.(*batchv1.Job); ok { - return controllertesting.Handled, errors.New(createError) - } - return controllertesting.Unhandled, nil - }, - }, - }, - }, - { - Name: "new feed with secret ref: secret gets to job", - InitialState: []runtime.Object{ - getEventSource(), - getEventType(), - getFeedSecret(), - getNewSecretFeed(), - }, - WantPresent: []runtime.Object{ - getSecretStartInProgressFeed(), - getNewSecretStartJob(), - //TODO job created event - }, - }, - { - Name: "in progress feed with existing job: both unchanged", - InitialState: []runtime.Object{ - getEventSource(), - getEventType(), - getStartInProgressFeed(), - getNewStartJob(), - }, - WantPresent: []runtime.Object{ - getStartInProgressFeed(), - getNewStartJob(), - }, - }, - { - Name: "in progress feed with completed job: updated status, context, job exists", - InitialState: []runtime.Object{ - getEventSource(), - getEventType(), - getStartInProgressFeed(), - getCompletedStartFeedJob(), - getCompletedStartFeedJobPod(), - }, - WantPresent: []runtime.Object{ - getStartedFeed(), - getCompletedStartFeedJob(), - //TODO job completed event - }, - }, - { - Name: "in progress feed with failed start job: updated status, job exists", - InitialState: []runtime.Object{ - getEventSource(), - getEventType(), - getStartInProgressFeed(), - getFailedStartFeedJob(), - getCompletedStartFeedJobPod(), - }, - WantPresent: []runtime.Object{ - getStartFailedFeed(), - getFailedStartFeedJob(), - //TODO job failed event - }, - }, - { - Name: "non-existing event type: updated status", - InitialState: []runtime.Object{ - getNewFeed(), - getEventSource(), - }, - WantPresent: []runtime.Object{ - getEventTypeMissing(), - }, - }, - { - Name: "Internal Error getting EventSource", - InitialState: []runtime.Object{ - getNewFeed(), - }, - WantPresent: []runtime.Object{ - getReadyUnknownFeed(), - }, - WantErrMsg: "error getting feed source: " + getError, - Mocks: controllertesting.Mocks{ - MockGets: []controllertesting.MockGet{ - func(_ client.Client, _ context.Context, _ client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { - if _, ok := obj.(*feedsv1alpha1.EventSource); ok { - return controllertesting.Handled, errors.New(getError) - } - return controllertesting.Unhandled, nil - }, - }, - }, - }, - { - Name: "deleting event type: updated status", - InitialState: []runtime.Object{ - getNewFeed(), - getEventSource(), - getDeletingEventType(), - }, - WantPresent: []runtime.Object{ - getEventTypeDeleting(), - }, - }, - { - Name: "non-existing event source: updated status", - InitialState: []runtime.Object{ - getNewFeed(), - }, - WantPresent: []runtime.Object{ - getEventSourceMissing(), - }, - }, - { - Name: "deleting event source: updated status", - InitialState: []runtime.Object{ - getNewFeed(), - getDeletingEventSource(), - }, - WantPresent: []runtime.Object{ - getEventSourceDeleting(), - }, - }, - { - Name: "deleting Feed with deleting EventSource", - InitialState: []runtime.Object{ - getDeletedStartedFeed(), - getDeletingEventSource(), - getEventType(), - }, - WantPresent: []runtime.Object{ - getDeletedStopInProgressFeed(), - }, - }, - { - Name: "deleting Feed with deleting EventType", - InitialState: []runtime.Object{ - getDeletedStartedFeed(), - getEventSource(), - getDeletingEventType(), - }, - WantPresent: []runtime.Object{ - getDeletedStopInProgressFeed(), - }, - }, - { - Name: "in progress feed with failed stop job: finalizer removed", - InitialState: []runtime.Object{ - getEventSource(), - getEventType(), - getDeletedStopInProgressFeed(), - getFailedStopJob(), - }, - WantPresent: []runtime.Object{ - getFinalizerRemovedStopJobFailedFeed(), - //TODO job failed event - }, - }, - { - Name: "failed because missing event source, now present", - InitialState: []runtime.Object{ - getFeedFailingWithMissingEventSource(), - getEventSource(), - getEventType(), - }, - WantPresent: []runtime.Object{ - getStartInProgressFeed(), - getNewStartJob(), - }, - }, - { - Name: "Deleted feed with finalizer, previously completed, feed job exists: feed job deleted", - InitialState: []runtime.Object{ - getEventSource(), - getEventType(), - getDeletedStartedFeed(), - getCompletedStartFeedJob(), - }, - WantPresent: []runtime.Object{ - getDeletedStartedFeed(), - }, - WantAbsent: []runtime.Object{ - getCompletedStartFeedJob(), - }, - }, - { - Name: "Deleted feed with finalizer, previously completed, feed job missing: stop feed job created, status updated", - InitialState: []runtime.Object{ - getEventSource(), - getEventType(), - getDeletedStartedFeed(), - }, - WantPresent: []runtime.Object{ - getDeletedStopInProgressFeed(), - getNewStopJob(), - //TODO job created event - }, - }, - { - Name: "Deleted in-progress feed with finalizer, stop feed job exists: unchanged", - InitialState: []runtime.Object{ - getEventSource(), - getEventType(), - getDeletedStopInProgressFeed(), - getInProgressStopJob(), - }, - WantPresent: []runtime.Object{ - getDeletedStopInProgressFeed(), - getInProgressStopJob(), - }, - }, - { - Name: "Deleted feed with completed stop feed job: no finalizers, update status", - InitialState: []runtime.Object{ - getEventSource(), - getEventType(), - getDeletedStopInProgressFeed(), - getCompletedStopJob(), - }, - WantPresent: []runtime.Object{ - getDeletedStoppedFeed(), - //TODO job completed event - }, - }, - } - reconcileKey := "test/test-feed" - - recorder := record.NewBroadcaster().NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) - - for _, tc := range testCases { - c := tc.GetClient() - r := &reconciler{ - client: c, - recorder: recorder, - } - tc.ReconcileKey = reconcileKey - t.Run(tc.Name, tc.Runner(t, r, c)) - } -} - -func getEventSource() *feedsv1alpha1.EventSource { - return &feedsv1alpha1.EventSource{ - ObjectMeta: om("test", "test-es"), - Spec: feedsv1alpha1.EventSourceSpec{ - CommonEventSourceSpec: feedsv1alpha1.CommonEventSourceSpec{ - Source: "github", - Image: "example.com/test-es-feeder", - Parameters: nil, - }, - }, - } -} - -func getDeletingEventSource() *feedsv1alpha1.EventSource { - return &feedsv1alpha1.EventSource{ - ObjectMeta: omDeleting("test", "test-es"), - Spec: feedsv1alpha1.EventSourceSpec{ - CommonEventSourceSpec: feedsv1alpha1.CommonEventSourceSpec{ - Source: "github", - Image: "example.com/test-es-feeder", - Parameters: nil, - }, - }, - } -} - -func getEventType() *feedsv1alpha1.EventType { - return &feedsv1alpha1.EventType{ - ObjectMeta: om("test", "test-et"), - Spec: feedsv1alpha1.EventTypeSpec{ - EventSource: getEventSource().Name, - }, - } -} - -func getDeletingEventType() *feedsv1alpha1.EventType { - return &feedsv1alpha1.EventType{ - ObjectMeta: omDeleting("test", "test-et"), - Spec: feedsv1alpha1.EventTypeSpec{ - EventSource: getEventSource().Name, - }, - } -} - -func getFeedContext() *sources.FeedContext { - return &sources.FeedContext{ - Context: map[string]interface{}{ - "foo": "bar", - }, - } -} - -func getNewFeed() *feedsv1alpha1.Feed { - return &feedsv1alpha1.Feed{ - TypeMeta: feedType(), - ObjectMeta: om("test", "test-feed"), - Spec: feedsv1alpha1.FeedSpec{ - Action: feedsv1alpha1.FeedAction{ - DNSName: targetDNS, - }, - Trigger: feedsv1alpha1.EventTrigger{ - EventType: getEventType().Name, - Resource: "", - Service: "", - Parameters: nil, - ParametersFrom: nil, - }, - }, - } -} - -func getFeedSecret() *corev1.Secret { - secretMap := map[string]interface{}{"foo": "bar"} - data, err := json.Marshal(secretMap) - if err != nil { - panic(err) - } - return &corev1.Secret{ - ObjectMeta: om("test", "feed-secret"), - Data: map[string][]byte{ - "test-secret-key": data, - }, - } - -} - -func getNewSecretFeed() *feedsv1alpha1.Feed { - feed := getNewFeed() - feed.Spec.Trigger.ParametersFrom = []feedsv1alpha1.ParametersFromSource{{ - SecretKeyRef: &feedsv1alpha1.SecretKeyReference{ - Name: getFeedSecret().Name, - Key: "test-secret-key", - }, - }} - return feed -} - -func getFeedFailingWithMissingEventSource() *feedsv1alpha1.Feed { - feed := getNewFeed() - feed.Status.InitializeConditions() - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionDependenciesSatisfied, - Status: corev1.ConditionFalse, - Reason: "TestGenerated", - Message: "test generated", - }) - return feed -} - -func getReadyUnknownFeed() *feedsv1alpha1.Feed { - feed := getNewFeed() - - feed.Status.InitializeConditions() - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionUnknown, - }) - - return feed -} - -func getNewFeedWithFinalizer() *feedsv1alpha1.Feed { - feed := getNewFeed() - feed.AddFinalizer(finalizerName) - return feed -} - -func getStartInProgressFeed() *feedsv1alpha1.Feed { - feed := getNewFeedWithFinalizer() - - feed.Status.InitializeConditions() - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionUnknown, - Reason: "StartJob", - Message: "start job in progress", - }) - - return feed -} - -func getSecretStartInProgressFeed() *feedsv1alpha1.Feed { - feed := getStartInProgressFeed() - feed.Spec.Trigger.ParametersFrom = []feedsv1alpha1.ParametersFromSource{{ - SecretKeyRef: &feedsv1alpha1.SecretKeyReference{ - Name: getFeedSecret().Name, - Key: "test-secret-key", - }, - }} - return feed -} - -func getStartedFeed() *feedsv1alpha1.Feed { - feed := getStartInProgressFeed() - marshalledContext, err := json.Marshal(getFeedContext().Context) - if err != nil { - panic(err) - } - feed.Status.FeedContext = &runtime.RawExtension{ - Raw: marshalledContext, - } - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionTrue, - Reason: "FeedSuccess", - Message: "start job succeeded", - }) - return feed -} - -func getStartFailedFeed() *feedsv1alpha1.Feed { - feed := getStartInProgressFeed() - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionFalse, - Reason: "FeedFailed", - Message: "Job failed with [] ", - }) - return feed -} - -func getDeletedStartedFeed() *feedsv1alpha1.Feed { - feed := getStartedFeed() - feed.SetDeletionTimestamp(&deletionTime) - return feed -} - -func getDeletedStopInProgressFeed() *feedsv1alpha1.Feed { - feed := getDeletedStartedFeed() - - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionUnknown, - Reason: "StopJob", - Message: "stop job in progress", - }) - return feed -} - -func getDeletedStoppedFeed() *feedsv1alpha1.Feed { - feed := getDeletedStopInProgressFeed() - feed.RemoveFinalizer(finalizerName) - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionTrue, - Reason: "FeedSuccess", - Message: "stop job succeeded", - }) - return feed -} - -func getFinalizerRemovedStopJobFailedFeed() *feedsv1alpha1.Feed { - feed := getDeletedStopInProgressFeed() - feed.RemoveFinalizer(finalizerName) - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionReady, - Status: corev1.ConditionFalse, - Reason: "FeedFailed", - Message: fmt.Sprintf("Job failed with [%s] %s", jobFailedReason, jobFailedMessage), - }) - return feed -} - -func getNewStartJob() *batchv1.Job { - jobName := resources.StartJobName(getNewFeed()) - return &batchv1.Job{ - TypeMeta: jobType(), - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: jobName, - Labels: map[string]string{"app": "feedpod"}, - OwnerReferences: []metav1.OwnerReference{{ - APIVersion: feedsv1alpha1.SchemeGroupVersion.String(), - Kind: "Feed", - Name: getNewFeed().Name, - Controller: &trueVal, - BlockOwnerDeletion: &trueVal, - }}, - }, - Spec: batchv1.JobSpec{ - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{"sidecar.istio.io/inject": "false"}, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: "feedlet", - Image: "example.com/test-es-feeder", - Env: []corev1.EnvVar{{ - Name: string(resources.EnvVarOperation), - Value: string(resources.OperationStartFeed), - }, { - Name: string(resources.EnvVarTarget), - Value: targetDNS, - }, { - Name: string(resources.EnvVarTrigger), - Value: base64.StdEncoding.EncodeToString(bytesOrDie(json.Marshal( - sources.EventTrigger{ - EventType: "test-et", - Parameters: map[string]interface{}{}, - }, - ))), - }, { - Name: string(resources.EnvVarContext), - Value: base64.StdEncoding.EncodeToString(bytesOrDie(json.Marshal( - sources.FeedContext{}, - ))), - }, { - Name: string(resources.EnvVarEventSourceParameters), - Value: "", - }, { - Name: string(resources.EnvVarNamespace), - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.namespace", - }, - }, - }, { - Name: string(resources.EnvVarServiceAccount), - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.serviceAccountName", - }, - }, - }}, - ImagePullPolicy: corev1.PullIfNotPresent, - }}, - RestartPolicy: corev1.RestartPolicyNever, - }, - }, - BackoffLimit: &resources.DefaultBackoffLimit, - ActiveDeadlineSeconds: &resources.DefaultActiveDeadlineSeconds, - }, - Status: batchv1.JobStatus{}, - } -} - -func getNewSecretStartJob() *batchv1.Job { - job := getNewStartJob() - envVars := job.Spec.Template.Spec.Containers[0].Env - var newEnvVars []corev1.EnvVar - for _, envVar := range envVars { - if envVar.Name == string(resources.EnvVarTrigger) { - newEnvVars = append(newEnvVars, corev1.EnvVar{ - Name: string(resources.EnvVarTrigger), - Value: base64.StdEncoding.EncodeToString(bytesOrDie(json.Marshal( - sources.EventTrigger{ - EventType: "test-et", - Parameters: map[string]interface{}{"foo": "bar"}, - }, - ))), - }) - } else { - newEnvVars = append(newEnvVars, envVar) - } - } - job.Spec.Template.Spec.Containers[0].Env = newEnvVars - return job -} - -func getInProgressStartFeedJob() *batchv1.Job { - job := getNewStartJob() - // This is normally set by a webhook. Set it here - // to simulate. TODO use a reactor when that's - // supported. - job.Spec.Selector = &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "job": job.Name, - }, - } - return job -} - -func getCompletedStartFeedJob() *batchv1.Job { - job := getInProgressStartFeedJob() - job.Status = batchv1.JobStatus{ - Conditions: []batchv1.JobCondition{{ - Type: batchv1.JobComplete, - Status: corev1.ConditionTrue, - }}, - } - return job -} - -func getFailedStartFeedJob() *batchv1.Job { - job := getInProgressStartFeedJob() - job.Status = batchv1.JobStatus{ - Conditions: []batchv1.JobCondition{{ - Type: batchv1.JobFailed, - Status: corev1.ConditionTrue, - }}, - } - return job -} - -func getCompletedStartFeedJobPod() *corev1.Pod { - job := getCompletedStartFeedJob() - outputContext, err := json.Marshal(getFeedContext()) - if err != nil { - panic(err) - } - - return &corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Namespace: "test", - Name: job.Name, - Labels: map[string]string{"job": job.Name}, - }, - Status: corev1.PodStatus{ - Phase: corev1.PodSucceeded, - ContainerStatuses: []corev1.ContainerStatus{{ - State: corev1.ContainerState{ - Terminated: &corev1.ContainerStateTerminated{ - Message: base64.StdEncoding.EncodeToString(outputContext), - }, - }, - }}, - }, - } -} - -func getNewStopJob() *batchv1.Job { - job := getNewStartJob() - job.Name = resources.StopJobName(getDeletedStartedFeed()) - - job.Spec.Template.Spec.Containers[0].Env = []corev1.EnvVar{{ - Name: string(resources.EnvVarOperation), - Value: string(resources.OperationStopFeed), - }, { - Name: string(resources.EnvVarTarget), - Value: targetDNS, - }, { - Name: string(resources.EnvVarTrigger), - Value: base64.StdEncoding.EncodeToString(bytesOrDie(json.Marshal( - sources.EventTrigger{ - EventType: "test-et", - Parameters: map[string]interface{}{}, - }, - ))), - }, { - Name: string(resources.EnvVarContext), - Value: base64.StdEncoding.EncodeToString(bytesOrDie(json.Marshal(getFeedContext()))), - }, { - Name: string(resources.EnvVarEventSourceParameters), - Value: "", - }, { - Name: string(resources.EnvVarNamespace), - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.namespace", - }, - }, - }, { - Name: string(resources.EnvVarServiceAccount), - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.serviceAccountName", - }, - }, - }} - return job -} - -func getInProgressStopJob() *batchv1.Job { - job := getNewStopJob() - // This is normally set by a webhook. Set it here - // to simulate. TODO use a reactor when that's - // supported. - job.Spec.Selector = &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "job": job.Name, - }, - } - return job -} - -func getCompletedStopJob() *batchv1.Job { - job := getInProgressStopJob() - job.Status = batchv1.JobStatus{ - Conditions: []batchv1.JobCondition{{ - Type: batchv1.JobComplete, - Status: corev1.ConditionTrue, - }}, - } - return job -} - -func getFailedStopJob() *batchv1.Job { - job := getInProgressStopJob() - job.Status.Conditions = append(job.Status.Conditions, batchv1.JobCondition{ - Type: batchv1.JobFailed, - Status: corev1.ConditionTrue, - Reason: jobFailedReason, - Message: jobFailedMessage, - }) - return job -} - -func getEventTypeMissing() *feedsv1alpha1.Feed { - feed := getNewFeed() - feed.Status.InitializeConditions() - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionDependenciesSatisfied, - Status: corev1.ConditionFalse, - Reason: EventTypeDoesNotExist, - Message: "EventType test/test-et does not exist", - }) - - return feed -} - -func getEventTypeDeleting() *feedsv1alpha1.Feed { - feed := getNewFeed() - feed.Status.InitializeConditions() - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionDependenciesSatisfied, - Status: corev1.ConditionFalse, - Reason: EventTypeDeleting, - Message: "EventType test/test-et is being deleted", - }) - - return feed -} - -func getEventSourceMissing() *feedsv1alpha1.Feed { - feed := getNewFeed() - feed.Status.InitializeConditions() - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionDependenciesSatisfied, - Status: corev1.ConditionFalse, - Reason: EventSourceDoesNotExist, - Message: "EventSource test/ does not exist", - }) - - return feed -} - -func getEventSourceDeleting() *feedsv1alpha1.Feed { - feed := getNewFeed() - feed.Status.InitializeConditions() - feed.Status.SetCondition(&feedsv1alpha1.FeedCondition{ - Type: feedsv1alpha1.FeedConditionDependenciesSatisfied, - Status: corev1.ConditionFalse, - Reason: EventSourceDeleting, - Message: "EventSource test/ is being deleted", - }) - - return feed -} - -func om(namespace, name string) metav1.ObjectMeta { - return metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - SelfLink: fmt.Sprintf("/apis/eventing/v1alpha1/namespaces/%s/object/%s", namespace, name), - } -} - -func omDeleting(namespace, name string) metav1.ObjectMeta { - return metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - SelfLink: fmt.Sprintf("/apis/eventing/v1alpha1/namespaces/%s/object/%s", namespace, name), - DeletionTimestamp: &deletionTime, - } -} - -func bytesOrDie(v []byte, err error) []byte { - if err != nil { - panic(err) - } - return v -} - -func feedType() metav1.TypeMeta { - return metav1.TypeMeta{ - APIVersion: feedsv1alpha1.SchemeGroupVersion.String(), - Kind: "Feed", - } -} - -func jobType() metav1.TypeMeta { - return metav1.TypeMeta{ - APIVersion: batchv1.SchemeGroupVersion.String(), - Kind: "Job", - } -} diff --git a/pkg/controller/feed/resources/job.go b/pkg/controller/feed/resources/job.go deleted file mode 100644 index ec127956cbd..00000000000 --- a/pkg/controller/feed/resources/job.go +++ /dev/null @@ -1,254 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resources - -import ( - "encoding/base64" - "encoding/json" - - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "github.com/knative/eventing/pkg/sources" - - "fmt" - - batchv1 "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// Operation specifies the operation for the feed container to perform. -type Operation string - -const ( - // OperationStartFeed specifies a feed should be started - OperationStartFeed Operation = "START" - // OperationStopFeed specifies a feed should be stopped - OperationStopFeed = "STOP" - // istio side car injection annotation - sidecarIstioInjectAnnotation = "sidecar.istio.io/inject" -) - -// EnvVar specifies the names of the environment variables passed to the -// feed container. -type EnvVar string - -const ( - // EnvVarOperation is the Env variable that gets set to requested Operation - EnvVarOperation EnvVar = "FEED_OPERATION" - // EnvVarTrigger is the Env variable that gets set to serialized trigger configuration - EnvVarTrigger = "FEED_TRIGGER" - // EnvVarTarget is the Env variable that gets set to target of the feed operation - EnvVarTarget = "FEED_TARGET" - // EnvVarContext is the Env variable that gets set to serialized FeedContext if stopping - EnvVarContext = "FEED_CONTEXT" - // EnvVarEventSourceParameters is the Env variable that gets set to serialized EventSourceSpec - EnvVarEventSourceParameters = "EVENT_SOURCE_PARAMETERS" - // EnvVarNamespace is the Env variable that gets set to namespace of the container doing - // the Feed (aka, namespace of the feed). Uses downward api - EnvVarNamespace = "FEED_NAMESPACE" - // EnvVarServiceAccount is the Env variable that gets set to serviceaccount of the - // container doing the feed. Uses downward api - //TODO is this useful? Wouldn't this already be the implicit service Account - // for the container? - EnvVarServiceAccount = "FEED_SERVICE_ACCOUNT" -) - -var ( - // DefaultBackoffLimit is the default BackoffLimit value for feedlet jobs. - // No more than this number of retry pods will be created before the job is - // considered failed. The total number of tries is this number + 1. - DefaultBackoffLimit int32 = 2 - // DefaultActiveDeadlineSeconds is the default ActiveDeadlineSeconds value for - // feedlet jobs. The job cannot be active for more than this number of - // seconds before it is considered failed. - DefaultActiveDeadlineSeconds int64 = 30 -) - -// MakeJob creates a Job to start or stop a Feed. -func MakeJob(feed *feedsv1alpha1.Feed, source *feedsv1alpha1.EventSource, trigger sources.EventTrigger, target string) (*batchv1.Job, error) { - labels := map[string]string{ - "app": "feedpod", - } - - podTemplate, err := makePodTemplate(feed, source, trigger, target) - if err != nil { - return nil, err - } - - return &batchv1.Job{ - ObjectMeta: metav1.ObjectMeta{ - Name: JobName(feed), - Namespace: feed.Namespace, - Labels: labels, - OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(feed, feedsv1alpha1.SchemeGroupVersion.WithKind("Feed"))}, - }, - Spec: batchv1.JobSpec{ - Template: *podTemplate, - BackoffLimit: &DefaultBackoffLimit, - ActiveDeadlineSeconds: &DefaultActiveDeadlineSeconds, - }, - }, nil -} - -// IsJobComplete returns true if the Job has completed successfully, or false if -// the Job is in progress or failed. -func IsJobComplete(job *batchv1.Job) bool { - for _, c := range job.Status.Conditions { - if c.Type == batchv1.JobComplete && c.Status == corev1.ConditionTrue { - return true - } - } - return false -} - -// IsJobFailed returns true if the Job has failed, or false if -// the Job is in progress or completed successfully. -func IsJobFailed(job *batchv1.Job) bool { - for _, c := range job.Status.Conditions { - if c.Type == batchv1.JobFailed && c.Status == corev1.ConditionTrue { - return true - } - } - return false -} - -// JobFailedMessage returns a string containing the job's failure reason -// and message for use in a Condition. -func JobFailedMessage(job *batchv1.Job) string { - for _, c := range job.Status.Conditions { - if c.Type == batchv1.JobFailed && c.Status == corev1.ConditionTrue { - return fmt.Sprintf("[%s] %s", c.Reason, c.Message) - } - } - return "" -} - -// makePodTemplate creates a pod template for a feed stop or start Job. -func makePodTemplate(feed *feedsv1alpha1.Feed, source *feedsv1alpha1.EventSource, trigger sources.EventTrigger, target string) (*corev1.PodTemplateSpec, error) { - var op Operation - if feed.GetDeletionTimestamp() == nil { - op = OperationStartFeed - } else { - op = OperationStopFeed - } - - var marshaledFeedContext []byte - var err error - // Check for an existing RawExtension object in the Feed status - if rawExt := feed.Status.FeedContext; rawExt != nil && rawExt.Raw != nil && len(rawExt.Raw) > 0 { - var ctx sources.FeedContext - if err = json.Unmarshal(rawExt.Raw, &ctx.Context); err != nil { - return nil, err - } - marshaledFeedContext, err = json.Marshal(ctx) - if err != nil { - return nil, err - } - } - // If no context was present, marshal an empty context because the event - // source wrapper expects it to be valid json. - if len(marshaledFeedContext) == 0 { - marshaledFeedContext, err = json.Marshal(sources.FeedContext{}) - if err != nil { - return nil, err - } - } - encodedFeedContext := base64.StdEncoding.EncodeToString(marshaledFeedContext) - - marshalledTrigger, err := json.Marshal(trigger) - if err != nil { - return nil, err - } - encodedTrigger := base64.StdEncoding.EncodeToString(marshalledTrigger) - - var encodedSourceParameters string - if source.Spec.Parameters != nil { - marshalledSourceParameters, err := json.Marshal(source.Spec.Parameters) - if err != nil { - return nil, err - } - encodedSourceParameters = base64.StdEncoding.EncodeToString(marshalledSourceParameters) - } - - return &corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - // don't inject Istio sidecar - // so if the feed can to access external services during StartFeed/StopFeed - Annotations: map[string]string{sidecarIstioInjectAnnotation: "false"}, - }, - Spec: corev1.PodSpec{ - ServiceAccountName: feed.Spec.ServiceAccountName, - RestartPolicy: corev1.RestartPolicyNever, - Containers: []corev1.Container{ - { - Name: "feedlet", - Image: source.Spec.Image, - ImagePullPolicy: corev1.PullIfNotPresent, - Env: []corev1.EnvVar{ - { - Name: string(EnvVarOperation), - Value: string(op), - }, - { - Name: string(EnvVarTarget), - Value: target, - }, - { - Name: string(EnvVarTrigger), - Value: encodedTrigger, - }, - { - Name: string(EnvVarContext), - Value: encodedFeedContext, - }, - { - Name: string(EnvVarEventSourceParameters), - Value: encodedSourceParameters, - }, - { - Name: string(EnvVarNamespace), - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.namespace", - }, - }, - }, - { - Name: string(EnvVarServiceAccount), - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.serviceAccountName", - }, - }, - }, - }, - }, - }, - }, - }, nil -} - -// GetFirstTerminationMessage returns the termination message of the first -// terminated container in the given Pod. -func GetFirstTerminationMessage(pod *corev1.Pod) string { - for _, cs := range pod.Status.ContainerStatuses { - if cs.State.Terminated != nil && cs.State.Terminated.Message != "" { - return cs.State.Terminated.Message - } - } - return "" -} diff --git a/pkg/controller/feed/resources/job_test.go b/pkg/controller/feed/resources/job_test.go deleted file mode 100644 index 4b3ad41d2ea..00000000000 --- a/pkg/controller/feed/resources/job_test.go +++ /dev/null @@ -1,430 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resources - -import ( - "fmt" - "github.com/google/go-cmp/cmp" - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "github.com/knative/eventing/pkg/sources" - batchv1 "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "strings" - "testing" -) - -const ( - feedName = "feed-name" - feedNamespace = "feed-namespace" - feedUid = "feed-uid" - feedTarget = "feed-target" -) - -func TestIsJobComplete(t *testing.T) { - testCases := []struct { - name string - conditions []batchv1.JobCondition - want bool - }{ - { - name: "no conditions: false", - want: false, - }, - { - name: "no job complete status: false", - conditions: []batchv1.JobCondition{ - { - Type: batchv1.JobFailed, - Status: corev1.ConditionTrue, - }, - }, - want: false, - }, - { - name: "job complete false: false", - conditions: []batchv1.JobCondition{ - { - Type: batchv1.JobComplete, - Status: corev1.ConditionFalse, - }, - }, - want: false, - }, - { - name: "job complete true: true", - conditions: []batchv1.JobCondition{ - { - Type: batchv1.JobComplete, - Status: corev1.ConditionTrue, - }, - }, - want: true, - }, - { - name: "multiple conditions, job complete true: true", - conditions: []batchv1.JobCondition{ - { - Type: batchv1.JobFailed, - Status: corev1.ConditionFalse, - }, - { - Type: batchv1.JobComplete, - Status: corev1.ConditionTrue, - }, - }, - want: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - job := makeJobWithConditions(tc.conditions) - actual := IsJobComplete(job) - if actual != tc.want { - t.Errorf("Expected %v, actual %v", tc.want, actual) - } - }) - } -} - -func TestIsJobFailed(t *testing.T) { - testCases := []struct { - name string - conditions []batchv1.JobCondition - want bool - }{ - { - name: "no conditions: false", - want: false, - }, - { - name: "no job failed status: false", - conditions: []batchv1.JobCondition{ - { - Type: batchv1.JobComplete, - Status: corev1.ConditionTrue, - }, - }, - want: false, - }, - { - name: "job failed false: false", - conditions: []batchv1.JobCondition{ - { - Type: batchv1.JobFailed, - Status: corev1.ConditionFalse, - }, - }, - want: false, - }, - { - name: "job failed true: true", - conditions: []batchv1.JobCondition{ - { - Type: batchv1.JobFailed, - Status: corev1.ConditionTrue, - }, - }, - want: true, - }, - { - name: "multiple conditions, job failed true: true", - conditions: []batchv1.JobCondition{ - { - Type: batchv1.JobComplete, - Status: corev1.ConditionFalse, - }, - { - Type: batchv1.JobFailed, - Status: corev1.ConditionTrue, - }, - }, - want: true, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - job := makeJobWithConditions(tc.conditions) - actual := IsJobFailed(job) - if actual != tc.want { - t.Errorf("Expected %v, actual %v", tc.want, actual) - } - }) - } -} - -func TestJobFailedMessage(t *testing.T) { - jobFailedReason := "test induced job failed reason" - jobFailedMessage := "test induced job failed message" - - testCases := []struct { - name string - conditions []batchv1.JobCondition - want string - }{ - { - name: "no conditions: empty string", - want: "", - }, - { - name: "no job failed status: empty string", - conditions: []batchv1.JobCondition{ - { - Type: batchv1.JobComplete, - Status: corev1.ConditionTrue, - }, - }, - want: "", - }, - { - name: "job failed false: empty string", - conditions: []batchv1.JobCondition{ - { - Type: batchv1.JobFailed, - Status: corev1.ConditionFalse, - }, - }, - want: "", - }, - { - name: "job failed true: message", - conditions: []batchv1.JobCondition{ - { - Type: batchv1.JobFailed, - Status: corev1.ConditionTrue, - Reason: jobFailedReason, - Message: jobFailedMessage, - }, - }, - want: fmt.Sprintf("[%s] %s", jobFailedReason, jobFailedMessage), - }, - { - name: "multiple conditions, job failed true: message", - conditions: []batchv1.JobCondition{ - { - Type: batchv1.JobComplete, - Status: corev1.ConditionFalse, - }, - { - Type: batchv1.JobFailed, - Status: corev1.ConditionTrue, - Reason: jobFailedReason, - Message: jobFailedMessage, - }, - }, - want: fmt.Sprintf("[%s] %s", jobFailedReason, jobFailedMessage), - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - job := makeJobWithConditions(tc.conditions) - actual := JobFailedMessage(job) - if actual != tc.want { - t.Errorf("Expected '%v', actual '%v'", tc.want, actual) - } - }) - } -} - -func TestGetFirstTerminationMessage(t *testing.T) { - message := "test induced terminal status message" - testCases := []struct { - name string - status []corev1.ContainerStatus - want string - }{ - { - name: "no status: empty string", - want: "", - }, - { - name: "no terminated status: empty string", - status: []corev1.ContainerStatus{ - { - State: corev1.ContainerState{ - Running: &corev1.ContainerStateRunning{ - StartedAt: metav1.Now(), - }, - }, - }, - }, - want: "", - }, - { - name: "no terminated status message: empty string", - status: []corev1.ContainerStatus{ - { - State: corev1.ContainerState{ - Terminated: &corev1.ContainerStateTerminated{}, - }, - }, - }, - want: "", - }, - { - name: "terminated status message: message", - status: []corev1.ContainerStatus{ - { - State: corev1.ContainerState{ - Terminated: &corev1.ContainerStateTerminated{ - Message: message, - }, - }, - }, - }, - want: message, - }, - { - name: "multiple terminated status message: first message", - status: []corev1.ContainerStatus{ - { - State: corev1.ContainerState{ - Terminated: &corev1.ContainerStateTerminated{ - Message: message, - }, - }, - }, - { - State: corev1.ContainerState{ - Terminated: &corev1.ContainerStateTerminated{ - Message: "some other message that won't be used", - }, - }, - }, - }, - want: message, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - pod := makePodWithStatus(tc.status) - actual := GetFirstTerminationMessage(pod) - if actual != tc.want { - t.Errorf("Expected '%v', actual '%v'", tc.want, actual) - } - }) - } -} - -func TestMakeJob(t *testing.T) { - type jobVerification func(t *testing.T, job *batchv1.Job) - - testCases := []struct { - name string - feed *feedsv1alpha1.Feed - source *feedsv1alpha1.EventSource - trigger sources.EventTrigger - target string - wantErr error - jobVerification []jobVerification - }{ - { - name: "", - feed: &feedsv1alpha1.Feed{ - ObjectMeta: metav1.ObjectMeta{ - Name: feedName, - Namespace: feedNamespace, - UID: feedUid, - }, - Spec: feedsv1alpha1.FeedSpec{ - ServiceAccountName: "feed-service-account", - }, - }, - source: &feedsv1alpha1.EventSource{}, - trigger: sources.EventTrigger{ - EventType: "test-event-type", - }, - target: feedTarget, - jobVerification: []jobVerification{ - jobObjectMetaVerification, - jobObjectSpecVerification, - }, - }, - } - - for _, tc := range testCases { - t.Run(tc.name, func(t *testing.T) { - actual, actualErr := MakeJob(tc.feed, tc.source, tc.trigger, tc.target) - if cmp.Diff(tc.wantErr, actualErr) != "" { - t.Errorf("Incorrect error (-want, +got): %v", cmp.Diff(tc.wantErr, actualErr)) - } - for _, jv := range tc.jobVerification { - jv(t, actual) - } - }) - } -} - -func makeJobWithConditions(conditions []batchv1.JobCondition) *batchv1.Job { - return &batchv1.Job{ - Status: batchv1.JobStatus{ - Conditions: conditions, - }, - } -} - -func makePodWithStatus(status []corev1.ContainerStatus) *corev1.Pod { - return &corev1.Pod{ - Status: corev1.PodStatus{ - ContainerStatuses: status, - }, - } -} - -func jobObjectMetaVerification(t *testing.T, job *batchv1.Job) { - if job.Namespace != feedNamespace { - t.Errorf("Expected namespace '%v', actual '%v'", feedNamespace, job.Namespace) - } - if !strings.Contains(job.Name, feedName) { - t.Errorf("Expected job name to include the Feed's name, '%v'. Actual: '%v'", feedName, job.Name) - } - if job.Labels["app"] != "feedpod" { - t.Errorf("Missing app=feedpod label. Actual: %v", job.Labels) - } - // TODO: Verify the entire owner reference. - if len(job.OwnerReferences) != 1 || job.OwnerReferences[0].UID != feedUid { - t.Errorf("Expected exactly one owner reference that points to the Feed. Actual: %+v", job.OwnerReferences) - } -} - -func jobObjectSpecVerification(t *testing.T, job *batchv1.Job) { - pt := job.Spec.Template - if pt.Annotations[sidecarIstioInjectAnnotation] != "false" { - t.Errorf("Expected Istio sidecar injection disabled. Actually: %v", pt.Labels[sidecarIstioInjectAnnotation]) - } - if len(pt.Spec.Containers) != 1 { - t.Errorf("Expected exactly one container, the feedlet. Actually: %+v", pt.Spec.Containers) - } - c := pt.Spec.Containers[0] - if getEnvVar(c.Env, "FEED_TARGET") != feedTarget { - t.Errorf("Expected FEED_TARGET to be '%v'. Actually: %+v", feedTarget, getEnvVar(c.Env, "FEED_TARGET")) - } - // TODO: Verify the other fields. -} - -func getEnvVar(env []corev1.EnvVar, name string) string { - for _, ev := range env { - if ev.Name == name { - return ev.Value - } - } - return "" -} diff --git a/pkg/controller/feed/resources/names.go b/pkg/controller/feed/resources/names.go deleted file mode 100644 index 111e09ec604..00000000000 --- a/pkg/controller/feed/resources/names.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resources - -import ( - "fmt" - - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" -) - -// JobName returns the name of the job for the feed, taking into account whether -// it's been deleted. -func JobName(feed *feedsv1alpha1.Feed) string { - if feed.GetDeletionTimestamp() != nil { - return StopJobName(feed) - } - return StartJobName(feed) -} - -// StartJobName returns the name of the job for the start operation. -func StartJobName(feed *feedsv1alpha1.Feed) string { - return fmt.Sprintf("%s-start", feed.Name) -} - -// StopJobName returns the name of the job for the stop operation. -func StopJobName(feed *feedsv1alpha1.Feed) string { - return fmt.Sprintf("%s-stop", feed.Name) -} diff --git a/pkg/controller/flow/dyn_client.go b/pkg/controller/flow/dyn_client.go deleted file mode 100644 index aa3dcf3aaee..00000000000 --- a/pkg/controller/flow/dyn_client.go +++ /dev/null @@ -1,74 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package flow - -import ( - "fmt" - "strings" - - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" - "k8s.io/client-go/rest" -) - -// CreateDynamicClient creates a dynamic client for the Group Version for the -// ObjectReference. It can only be used for that APIVersion / Group -func CreateDynamicClient(config *rest.Config, ref *corev1.ObjectReference) (dynamic.Interface, error) { - // We need to tweak the configuration so that it points to the right - // resources under the ThirdPartyResources that Istio uses. - gvk := ref.GroupVersionKind() - - config.ContentConfig.GroupVersion = &schema.GroupVersion{ - Group: gvk.Group, - Version: gvk.Version, - } - config.APIPath = "apis" - return dynamic.NewForConfig(config) -} - -func CreateResourceInterface(config *rest.Config, ref *corev1.ObjectReference, namespace string) (dynamic.ResourceInterface, error) { - c, err := CreateDynamicClient(config, ref) - if err != nil { - return nil, err - } - - gvk := ref.GroupVersionKind() - - r := c.Resource(schema.GroupVersionResource{ - Group: gvk.Group, - Version: gvk.Version, - Resource: pluralizeKind(gvk.Kind), - }) - if r == nil { - return nil, fmt.Errorf("failed to create dynamic client resource") - } - return r.Namespace(namespace), nil -} - -// takes a kind and pluralizes it. This is super terrible, but I am -// not aware of a generic way to do this. -// I am not alone in thinking this and I haven't found a better solution: -// This seems relevant: -// https://github.com/kubernetes/kubernetes/issues/18622 -func pluralizeKind(kind string) string { - ret := strings.ToLower(kind) - if strings.HasSuffix(ret, "s") { - return fmt.Sprintf("%ses", ret) - } - return fmt.Sprintf("%ss", ret) -} diff --git a/pkg/controller/flow/provider.go b/pkg/controller/flow/provider.go deleted file mode 100644 index b916006f7aa..00000000000 --- a/pkg/controller/flow/provider.go +++ /dev/null @@ -1,91 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package flow - -import ( - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/record" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller" - "sigs.k8s.io/controller-runtime/pkg/handler" - "sigs.k8s.io/controller-runtime/pkg/manager" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - "sigs.k8s.io/controller-runtime/pkg/source" -) - -const ( - // controllerAgentName is the string used by this controller to identify - // itself when creating events. - controllerAgentName = "flow-controller" -) - -type reconciler struct { - client client.Client - restConfig *rest.Config - recorder record.EventRecorder -} - -// Verify the struct implements reconcile.Reconciler -var _ reconcile.Reconciler = &reconciler{} - -// ProvideController returns a flow controller. -func ProvideController(mgr manager.Manager) (controller.Controller, error) { - // Setup a new controller to Reconcile Flows. - c, err := controller.New(controllerAgentName, mgr, controller.Options{ - Reconciler: &reconciler{ - recorder: mgr.GetRecorder(controllerAgentName), - }, - }) - if err != nil { - return nil, err - } - - // Watch Flow events and enqueue Flow object key. - if err := c.Watch(&source.Kind{Type: &v1alpha1.Flow{}}, &handler.EnqueueRequestForObject{}); err != nil { - return nil, err - } - - // In addition to watching Flow objects, watch for objects that a Flow creates and own and when changes - // are made to them, enqueue owning Flow object for reconcile loop. - if err := c.Watch(&source.Kind{Type: &channelsv1alpha1.Channel{}}, - &handler.EnqueueRequestForOwner{OwnerType: &v1alpha1.Flow{}, IsController: true}); err != nil { - return nil, err - } - if err := c.Watch(&source.Kind{Type: &channelsv1alpha1.Subscription{}}, - &handler.EnqueueRequestForOwner{OwnerType: &v1alpha1.Flow{}, IsController: true}); err != nil { - return nil, err - } - if err := c.Watch(&source.Kind{Type: &feedsv1alpha1.Feed{}}, - &handler.EnqueueRequestForOwner{OwnerType: &v1alpha1.Flow{}, IsController: true}); err != nil { - return nil, err - } - - return c, nil -} - -func (r *reconciler) InjectClient(c client.Client) error { - r.client = c - return nil -} - -func (r *reconciler) InjectConfig(c *rest.Config) error { - r.restConfig = c - return nil -} diff --git a/pkg/controller/flow/reconcile.go b/pkg/controller/flow/reconcile.go deleted file mode 100644 index cb4c3696bed..00000000000 --- a/pkg/controller/flow/reconcile.go +++ /dev/null @@ -1,355 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package flow - -import ( - "context" - "fmt" - "log" - - "github.com/golang/glog" - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - "github.com/knative/eventing/pkg/controller/flow/resources" - "github.com/knative/eventing/pkg/system" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/equality" - "k8s.io/apimachinery/pkg/api/errors" - "k8s.io/apimachinery/pkg/api/meta" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime/schema" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/reconcile" -) - -// What field do we assume Object Reference exports as a resolvable target -const targetFieldName = "domainInternal" - -// Reconcile compares the actual state with the desired, and attempts to -// converge the two. It then updates the Status block of the Flow resource -// with the current status of the resource. -func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) { - glog.Infof("Reconciling flow %v", request) - flow := &v1alpha1.Flow{} - err := r.client.Get(context.TODO(), request.NamespacedName, flow) - - if errors.IsNotFound(err) { - glog.Errorf("could not find flow %v\n", request) - return reconcile.Result{}, nil - } - - if err != nil { - glog.Errorf("could not fetch Flow %v for %+v\n", err, request) - return reconcile.Result{}, err - } - - original := flow.DeepCopy() - - flow.Status.InitializeConditions() - - // Reconcile this copy of the Flow and then write back any status - // updates regardless of whether the reconcile error out. - err = r.reconcile(flow) - if equality.Semantic.DeepEqual(original.Status, flow.Status) { - // If we didn't change anything then don't call updateStatus. - // This is important because the copy we loaded from the informer's - // cache may be stale and we don't want to overwrite a prior update - // to status with this stale state. - } else if _, err := r.updateStatus(flow); err != nil { - glog.Warningf("Failed to update flow status: %v", err) - return reconcile.Result{}, err - } - - // Requeue if the resource is not ready: - return reconcile.Result{}, err -} - -func (r *reconciler) reconcile(flow *v1alpha1.Flow) error { - // See if the flow has been deleted - accessor, err := meta.Accessor(flow) - if err != nil { - log.Fatalf("Failed to get metadata: %s", err) - } - deletionTimestamp := accessor.GetDeletionTimestamp() - glog.Infof("DeletionTimestamp: %v", deletionTimestamp) - - target, err := r.resolveActionTarget(flow.Namespace, flow.Spec.Action) - if err != nil { - glog.Warningf("Failed to resolve target %v : %v", flow.Spec.Action, err) - flow.Status.PropagateActionTargetResolved(corev1.ConditionFalse, "ActionTargetNotResolved", err.Error()) - return err - } - - flow.Status.PropagateActionTargetResolved(corev1.ConditionTrue, "ActionTargetResolved", fmt.Sprintf("Resolved to: %q", target)) - - // Ok, so target is the underlying k8s service (or URI if so specified) that we want to target - glog.Infof("Resolved Target to: %q", target) - - // Reconcile the Channel. Creates a channel that is the target that the Feed will use. - // TODO: We should reuse channels possibly. By this I mean that instead of creating a - // channel for each subscription, we could look at existing channels and reuse one - // and only create a subscription to a channel instead. - channel, err := r.reconcileChannel(flow) - if err != nil { - glog.Warningf("Failed to reconcile channel : %v", err) - return err - } - flow.Status.PropagateChannelStatus(channel.Status) - - subscription, err := r.reconcileSubscription(channel.Name, target, flow) - if err != nil { - glog.Warningf("Failed to reconcile subscription : %v", err) - return err - } - flow.Status.PropagateSubscriptionStatus(subscription.Status) - - channelDNS := channel.Status.DomainInternal - if channelDNS != "" { - glog.Infof("Reconciling feed for flow %q targeting %q", flow.Name, channelDNS) - feed, err := r.reconcileFeed(channelDNS, flow) - if err != nil { - glog.Warningf("Failed to reconcile feed: %v", err) - return err - } - glog.Infof("Reconciled feed status for flow %q targeting %q : %+v", flow.Name, channelDNS, feed.Status) - flow.Status.PropagateFeedStatus(feed.Status) - } - return nil -} - -func (r *reconciler) updateStatus(flow *v1alpha1.Flow) (*v1alpha1.Flow, error) { - newFlow := &v1alpha1.Flow{} - err := r.client.Get(context.TODO(), client.ObjectKey{Namespace: flow.Namespace, Name: flow.Name}, newFlow) - - if err != nil { - return nil, err - } - newFlow.Status = flow.Status - - // Until #38113 is merged, we must use Update instead of UpdateStatus to - // update the Status block of the Flow resource. UpdateStatus will not - // allow changes to the Spec of the resource, which is ideal for ensuring - // nothing other than resource status has been updated. - if err = r.client.Update(context.TODO(), newFlow); err != nil { - return nil, err - } - return newFlow, nil -} - -// resolveActionTarget resolves the Action.Target. If it's an ObjectReference -// will resolve it, and if it's an TargetURI will just return it. -func (r *reconciler) resolveActionTarget(namespace string, action v1alpha1.FlowAction) (string, error) { - glog.Infof("Resolving target: %+v", action) - - if action.Target != nil { - return r.resolveObjectReference(namespace, action.Target) - } - if action.TargetURI != nil { - return *action.TargetURI, nil - } - - return "", fmt.Errorf("no resolvable action target: %+v", action) -} - -// resolveObjectReference fetches an object based on ObjectReference. It assumes the -// object has a status["domainInternal"] string in it and returns it. -func (r *reconciler) resolveObjectReference(namespace string, ref *corev1.ObjectReference) (string, error) { - resourceClient, err := CreateResourceInterface(r.restConfig, ref, namespace) - if err != nil { - glog.Warningf("failed to create dynamic client resource: %v", err) - return "", err - } - - obj, err := resourceClient.Get(ref.Name, metav1.GetOptions{}) - if err != nil { - glog.Warningf("failed to get object: %v", err) - return "", err - } - status, ok := obj.Object["status"] - if !ok { - return "", fmt.Errorf("%q does not contain status", ref.Name) - } - statusMap := status.(map[string]interface{}) - serviceName, ok := statusMap[targetFieldName] - if !ok { - return "", fmt.Errorf("%q does not contain field %q in status", targetFieldName, ref.Name) - } - serviceNameStr, ok := serviceName.(string) - if !ok { - return "", fmt.Errorf("%q status field %q is not a string", targetFieldName, ref.Name) - } - return serviceNameStr, nil -} - -func (r *reconciler) reconcileChannel(flow *v1alpha1.Flow) (*channelsv1alpha1.Channel, error) { - channelName := flow.Name - - channel := &channelsv1alpha1.Channel{} - err := r.client.Get(context.TODO(), client.ObjectKey{Namespace: flow.Namespace, Name: channelName}, channel) - if errors.IsNotFound(err) { - channel, err = r.createChannel(flow) - if err != nil { - glog.Errorf("Failed to create channel %q : %v", channelName, err) - return nil, err - } - } else if err != nil { - glog.Errorf("Failed to reconcile channel %q failed to get channels : %v", channelName, err) - return nil, err - } - - // TODO: Make sure channel is what it should be. For now, just assume it's fine - // if it exists. - return channel, nil -} - -func (r *reconciler) createChannel(flow *v1alpha1.Flow) (*channelsv1alpha1.Channel, error) { - clusterBusName, err := r.getDefaultClusterBusName() - if err != nil { - return nil, err - } - - channel := resources.MakeChannel(clusterBusName, flow) - if err := r.client.Create(context.TODO(), channel); err != nil { - return nil, err - } - return channel, nil -} - -func (r *reconciler) reconcileSubscription(channelName string, target string, flow *v1alpha1.Flow) (*channelsv1alpha1.Subscription, error) { - subscriptionName := flow.Name - - subscription := &channelsv1alpha1.Subscription{} - err := r.client.Get(context.TODO(), client.ObjectKey{Namespace: flow.Namespace, Name: subscriptionName}, subscription) - if errors.IsNotFound(err) { - subscription, err = r.createSubscription(channelName, target, flow) - if err != nil { - glog.Errorf("Failed to create subscription %q : %v", subscriptionName, err) - return nil, err - } - } else if err != nil { - glog.Errorf("Failed to reconcile subscription %q failed to get subscriptions : %v", subscriptionName, err) - return nil, err - } - - // TODO: Make sure subscription is what it should be. For now, just assume it's fine - // if it exists. - return subscription, nil -} - -func (r *reconciler) createSubscription(channelName string, target string, flow *v1alpha1.Flow) (*channelsv1alpha1.Subscription, error) { - subscription := resources.MakeSubscription(channelName, target, flow) - if err := r.client.Create(context.TODO(), subscription); err != nil { - return nil, err - } - return subscription, nil -} - -func (r *reconciler) reconcileFeed(channelDNS string, flow *v1alpha1.Flow) (*feedsv1alpha1.Feed, error) { - feed, err := r.getFeed(context.TODO(), flow) - if errors.IsNotFound(err) { - feed, err = r.createFeed(channelDNS, flow) - if err != nil { - glog.Errorf("Failed to create feed for flow %q: %v", flow.Name, err) - return nil, err - } - } else if err != nil { - glog.Errorf("Failed to reconcile feed for flow %q failed to get feeds : %v", flow.Name, err) - return nil, err - } - - glog.Infof("Reconciled feed: %+v", feed) - // TODO: Make sure feed is what it should be. For now, just assume it's fine - // if it exists. - return feed, nil -} - -func (r *reconciler) createFeed(channelDNS string, flow *v1alpha1.Flow) (*feedsv1alpha1.Feed, error) { - feed := resources.MakeFeed(channelDNS, flow) - if err := r.client.Create(context.TODO(), feed); err != nil { - return nil, err - } - return feed, nil -} - -const ( - // controllerConfigMapName is the name of the configmap in the eventing - // namespace that holds the configuration for this controller. - controllerConfigMapName = "flow-controller-config" - - // defaultClusterBusConfigMapKey is the name of the key in this controller's - // ConfigMap that contains the name of the default cluster bus for the flow - // controller to use. - defaultClusterBusConfigMapKey = "default-cluster-bus" - - // fallbackClusterBusName is the name of the cluster bus that will be used - // for flows if the controller's configmap does not exist or does not - // contain the 'default-cluster-bus' key. - fallbackClusterBusName = "stub" -) - -// getDefaultClusterBusName returns the value of the 'default-cluster-bus' key in -// the knative-eventing/flow-controller-config configmap or an error. If the -// 'default-cluster-bus' key is not set, it returns the default value "stub". -func (r *reconciler) getDefaultClusterBusName() (string, error) { - configMapKey := client.ObjectKey{ - Namespace: system.Namespace, - Name: controllerConfigMapName, - } - - configMap := &corev1.ConfigMap{} - if err := r.client.Get(context.TODO(), configMapKey, configMap); err != nil { - // return the fallback value if there's an error loading the configmap - return fallbackClusterBusName, nil - } - - if value, ok := configMap.Data[defaultClusterBusConfigMapKey]; ok { - return value, nil - } - - return fallbackClusterBusName, nil // return the fallback value -} - -func (r *reconciler) getFeed(ctx context.Context, flow *v1alpha1.Flow) (*feedsv1alpha1.Feed, error) { - feedList := &feedsv1alpha1.FeedList{} - err := r.client.List( - ctx, - &client.ListOptions{ - Namespace: flow.Namespace, - LabelSelector: labels.Everything(), - // TODO this is here because the fake client needs it. Remove this when it's no longer - // needed. - Raw: &metav1.ListOptions{ - TypeMeta: metav1.TypeMeta{ - APIVersion: feedsv1alpha1.SchemeGroupVersion.String(), - Kind: "Feed", - }, - }, - }, - feedList) - if err != nil { - glog.Errorf("Unable to list feeds: %v", err) - return nil, err - } - for _, feed := range feedList.Items { - if metav1.IsControlledBy(&feed, flow) { - return &feed, nil - } - } - return nil, errors.NewNotFound(schema.GroupResource{}, "") -} diff --git a/pkg/controller/flow/reconcile_test.go b/pkg/controller/flow/reconcile_test.go deleted file mode 100644 index cb7feefc6dd..00000000000 --- a/pkg/controller/flow/reconcile_test.go +++ /dev/null @@ -1,270 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Veroute.on 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 flow - -import ( - "context" - "fmt" - "sigs.k8s.io/controller-runtime/pkg/client" - "testing" - - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - flowsv1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - controllertesting "github.com/knative/eventing/pkg/controller/testing" - "github.com/knative/eventing/pkg/system" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/reconcile" - - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/tools/record" -) - -var ( - trueVal = true - targetURI = "http://target.example.com" -) - -const ( - targetDNS = "myservice.mynamespace.svc.cluster.local" - eventType = "myeventtype" - flowName = "test-flow" -) - -func init() { - // Add types to scheme - feedsv1alpha1.AddToScheme(scheme.Scheme) - flowsv1alpha1.AddToScheme(scheme.Scheme) - channelsv1alpha1.AddToScheme(scheme.Scheme) -} - -var injectDomainInternalMocks = controllertesting.Mocks{ - MockCreates: []controllertesting.MockCreate{ - func(innerClient client.Client, ctx context.Context, obj runtime.Object) (controllertesting.MockHandled, error) { - // If we are creating a Channel, then fill in the status, in particular the DomainInternal as - // it used to control whether the Feed is created. - if channel, ok := obj.(*channelsv1alpha1.Channel); ok { - err := innerClient.Create(ctx, obj) - channel.Status.DomainInternal = targetDNS - return controllertesting.Handled, err - } - return controllertesting.Unhandled, nil - }, - }, -} - -var testCases = []controllertesting.TestCase{ - { - Name: "new flow: adds status, action target resolved", - InitialState: []runtime.Object{ - getNewFlow(), - getFlowControllerConfigMap(), - }, - ReconcileKey: "test/test-flow", - WantResult: reconcile.Result{}, - WantPresent: []runtime.Object{ - getActionTargetResolvedFlow(), - func() *channelsv1alpha1.Channel { - c := getNewChannel() - c.Spec.ClusterBus = "special-bus" - return c - }(), - getNewSubscription(), - getNewFeed(), - }, - Mocks: injectDomainInternalMocks, - }, - { - Name: "new flow: adds status, action target resolved, no flow controller config map, use default 'stub' bus", - InitialState: []runtime.Object{ - getNewFlow(), - }, - ReconcileKey: "test/test-flow", - WantResult: reconcile.Result{}, - WantPresent: []runtime.Object{ - getActionTargetResolvedFlow(), - getNewChannel(), - getNewSubscription(), - getNewFeed(), - }, - Mocks: injectDomainInternalMocks, - }, -} - -func TestAllCases(t *testing.T) { - recorder := record.NewBroadcaster().NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) - - for _, tc := range testCases { - c := tc.GetClient() - r := &reconciler{ - client: c, - recorder: recorder, - } - t.Run(tc.Name, tc.Runner(t, r, c)) - } -} - -func getActionTargetResolvedFlow() *flowsv1alpha1.Flow { - newFlow := getNewFlow() - newFlow.Status = flowsv1alpha1.FlowStatus{ - Conditions: []flowsv1alpha1.FlowCondition{{ - Type: flowsv1alpha1.FlowConditionReady, - Status: corev1.ConditionUnknown, - }, { - Type: flowsv1alpha1.FlowConditionActionTargetResolved, - Status: corev1.ConditionTrue, - Reason: "ActionTargetResolved", - Message: fmt.Sprintf("Resolved to: %q", targetURI), - }}, - } - return newFlow -} - -func getNewFlow() *flowsv1alpha1.Flow { - return &flowsv1alpha1.Flow{ - TypeMeta: flowType(), - ObjectMeta: om("test", flowName), - Spec: flowsv1alpha1.FlowSpec{ - Action: flowsv1alpha1.FlowAction{ - TargetURI: &targetURI, - }, - Trigger: flowsv1alpha1.EventTrigger{ - EventType: eventType, - Resource: "myresource", - Service: "", - Parameters: nil, - ParametersFrom: nil, - }, - }, - } -} - -func getNewChannel() *channelsv1alpha1.Channel { - channel := &channelsv1alpha1.Channel{ - TypeMeta: channelType(), - ObjectMeta: om("test", flowName), - Spec: channelsv1alpha1.ChannelSpec{ - ClusterBus: "stub", - }, - } - channel.ObjectMeta.OwnerReferences = append(channel.ObjectMeta.OwnerReferences, getOwnerReference(false)) - - // selflink is not filled in when we create the object, so clear it - channel.ObjectMeta.SelfLink = "" - return channel -} - -func getNewSubscription() *channelsv1alpha1.Subscription { - subscription := &channelsv1alpha1.Subscription{ - TypeMeta: subscriptionType(), - ObjectMeta: om("test", flowName), - Spec: channelsv1alpha1.SubscriptionSpec{ - Channel: flowName, - Subscriber: targetURI, - }, - } - subscription.ObjectMeta.OwnerReferences = append(subscription.ObjectMeta.OwnerReferences, getOwnerReference(false)) - - // selflink is not filled in when we create the object, so clear it - subscription.ObjectMeta.SelfLink = "" - return subscription -} - -func getNewFeed() *feedsv1alpha1.Feed { - return &feedsv1alpha1.Feed{ - TypeMeta: feedType(), - ObjectMeta: feedObjectMeta("test", "test-flow-"), - Spec: feedsv1alpha1.FeedSpec{ - Action: feedsv1alpha1.FeedAction{ - DNSName: targetDNS, - }, - Trigger: feedsv1alpha1.EventTrigger{ - EventType: eventType, - Resource: "myresource", - Service: "", - Parameters: nil, - ParametersFrom: nil, - }, - }, - } -} - -func getFlowControllerConfigMap() *corev1.ConfigMap { - return &corev1.ConfigMap{ - ObjectMeta: om(system.Namespace, controllerConfigMapName), - Data: map[string]string{ - defaultClusterBusConfigMapKey: "special-bus", - }, - } -} - -func flowType() metav1.TypeMeta { - return metav1.TypeMeta{ - APIVersion: flowsv1alpha1.SchemeGroupVersion.String(), - Kind: "Flow", - } -} - -func feedType() metav1.TypeMeta { - return metav1.TypeMeta{ - APIVersion: feedsv1alpha1.SchemeGroupVersion.String(), - Kind: "Feed", - } -} - -func channelType() metav1.TypeMeta { - return metav1.TypeMeta{ - APIVersion: channelsv1alpha1.SchemeGroupVersion.String(), - Kind: "Channel", - } -} - -func subscriptionType() metav1.TypeMeta { - return metav1.TypeMeta{ - APIVersion: channelsv1alpha1.SchemeGroupVersion.String(), - Kind: "Subscription", - } -} - -func om(namespace, name string) metav1.ObjectMeta { - return metav1.ObjectMeta{ - Namespace: namespace, - Name: name, - SelfLink: fmt.Sprintf("/apis/eventing/v1alpha1/namespaces/%s/object/%s", namespace, name), - } -} -func feedObjectMeta(namespace, generateName string) metav1.ObjectMeta { - return metav1.ObjectMeta{ - Namespace: namespace, - GenerateName: generateName, - OwnerReferences: []metav1.OwnerReference{ - getOwnerReference(true), - }, - } -} - -func getOwnerReference(blockOwnerDeletion bool) metav1.OwnerReference { - return metav1.OwnerReference{ - APIVersion: flowsv1alpha1.SchemeGroupVersion.String(), - Kind: "Flow", - Name: flowName, - Controller: &trueVal, - BlockOwnerDeletion: &blockOwnerDeletion, - } -} diff --git a/pkg/controller/flow/resources/channel.go b/pkg/controller/flow/resources/channel.go deleted file mode 100644 index f839732761e..00000000000 --- a/pkg/controller/flow/resources/channel.go +++ /dev/null @@ -1,42 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resources - -import ( - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - "github.com/knative/eventing/pkg/controller" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func MakeChannel(defaultBusName string, flow *v1alpha1.Flow) *channelsv1alpha1.Channel { - channelName := flow.Name - channel := &channelsv1alpha1.Channel{ - ObjectMeta: metav1.ObjectMeta{ - Name: channelName, - Namespace: flow.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *controller.NewControllerRef(flow, false), - }, - }, - Spec: channelsv1alpha1.ChannelSpec{ - ClusterBus: defaultBusName, - }, - } - return channel -} diff --git a/pkg/controller/flow/resources/feed.go b/pkg/controller/flow/resources/feed.go deleted file mode 100644 index a0fdcf13d94..00000000000 --- a/pkg/controller/flow/resources/feed.go +++ /dev/null @@ -1,56 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resources - -import ( - feedsv1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - - "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - "github.com/knative/eventing/pkg/controller" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func MakeFeed(channelDNS string, flow *v1alpha1.Flow) *feedsv1alpha1.Feed { - feed := &feedsv1alpha1.Feed{ - ObjectMeta: metav1.ObjectMeta{ - GenerateName: flow.Name + "-", - Namespace: flow.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *controller.NewControllerRef(flow, true), - }, - }, - Spec: feedsv1alpha1.FeedSpec{ - Action: feedsv1alpha1.FeedAction{DNSName: channelDNS}, - Trigger: feedsv1alpha1.EventTrigger{ - EventType: flow.Spec.Trigger.EventType, - Resource: flow.Spec.Trigger.Resource, - Service: flow.Spec.Trigger.Service, - }, - }, - } - if flow.Spec.ServiceAccountName != "" { - feed.Spec.ServiceAccountName = flow.Spec.ServiceAccountName - } - - if flow.Spec.Trigger.Parameters != nil { - feed.Spec.Trigger.Parameters = flow.Spec.Trigger.Parameters - } - if flow.Spec.Trigger.ParametersFrom != nil { - feed.Spec.Trigger.ParametersFrom = flow.Spec.Trigger.ParametersFrom - } - return feed -} diff --git a/pkg/controller/flow/resources/stub_test.go b/pkg/controller/flow/resources/stub_test.go deleted file mode 100644 index d4c244afe73..00000000000 --- a/pkg/controller/flow/resources/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resources - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/controller/flow/resources/subscription.go b/pkg/controller/flow/resources/subscription.go deleted file mode 100644 index 7ff4cf1b3d7..00000000000 --- a/pkg/controller/flow/resources/subscription.go +++ /dev/null @@ -1,43 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resources - -import ( - channelsv1alpha1 "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - "github.com/knative/eventing/pkg/controller" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -func MakeSubscription(channelName string, target string, flow *v1alpha1.Flow) *channelsv1alpha1.Subscription { - subscriptionName := flow.Name - subscription := &channelsv1alpha1.Subscription{ - ObjectMeta: metav1.ObjectMeta{ - Name: subscriptionName, - Namespace: flow.Namespace, - OwnerReferences: []metav1.OwnerReference{ - *controller.NewControllerRef(flow, false), - }, - }, - Spec: channelsv1alpha1.SubscriptionSpec{ - Channel: channelName, - Subscriber: target, - }, - } - return subscription -} diff --git a/pkg/controller/owner_references.go b/pkg/controller/owner_references.go index 073e2a8ed0f..46cda13987c 100644 --- a/pkg/controller/owner_references.go +++ b/pkg/controller/owner_references.go @@ -22,35 +22,11 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime/schema" - channelsv1alpha "github.com/knative/eventing/pkg/apis/channels/v1alpha1" eventingv1alpha "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - feedsv1alpha "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - flowsv1alpha "github.com/knative/eventing/pkg/apis/flows/v1alpha1" ) func kind(obj metav1.Object) schema.GroupVersionKind { switch obj.(type) { - // Channels - case *channelsv1alpha.Bus: - return channelsv1alpha.SchemeGroupVersion.WithKind("Bus") - case *channelsv1alpha.Channel: - return channelsv1alpha.SchemeGroupVersion.WithKind("Channel") - case *channelsv1alpha.ClusterBus: - return channelsv1alpha.SchemeGroupVersion.WithKind("ClusterBus") - - // Feeds - case *feedsv1alpha.ClusterEventType: - return feedsv1alpha.SchemeGroupVersion.WithKind("ClusterEventType") - case *feedsv1alpha.ClusterEventSource: - return feedsv1alpha.SchemeGroupVersion.WithKind("ClusterEventSource") - case *feedsv1alpha.EventType: - return feedsv1alpha.SchemeGroupVersion.WithKind("EventType") - case *feedsv1alpha.EventSource: - return feedsv1alpha.SchemeGroupVersion.WithKind("EventSource") - - // Flows - case *flowsv1alpha.Flow: - return flowsv1alpha.SchemeGroupVersion.WithKind("Flow") // Eventing case *eventingv1alpha.Channel: diff --git a/pkg/controller/util/bus_util.go b/pkg/controller/util/bus_util.go deleted file mode 100644 index 0b6f72b9dbd..00000000000 --- a/pkg/controller/util/bus_util.go +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package util - -import ( - "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// NewBusCondition creates a new bus condition with the provided values and both times set to now(). -func NewBusCondition(condType v1alpha1.BusConditionType, status v1.ConditionStatus, reason, message string) *v1alpha1.BusCondition { - return &v1alpha1.BusCondition{ - Type: condType, - Status: status, - LastUpdateTime: meta_v1.Now(), - LastTransitionTime: meta_v1.Now(), - Reason: reason, - Message: message, - } -} - -// GetBusCondition returns the bus condition with the provided type. -func GetBusCondition(status v1alpha1.BusStatus, condType v1alpha1.BusConditionType) *v1alpha1.BusCondition { - for i := range status.Conditions { - c := status.Conditions[i] - if c.Type == condType { - return &c - } - } - return nil -} - -// SetBusCondition updates the bus status to include the provided condition. If the condition that -// we are about to add already exists and has the same status and reason then no update happens. -func SetBusCondition(status *v1alpha1.BusStatus, condition v1alpha1.BusCondition) { - currentCond := GetBusCondition(*status, condition.Type) - if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason { - return - } - // Do not update lastTransitionTime if the status of the condition doesn't change. - if currentCond != nil && currentCond.Status == condition.Status { - condition.LastTransitionTime = currentCond.LastTransitionTime - } - newConditions := filterOutBusCondition(status.Conditions, condition.Type) - status.Conditions = append(newConditions, condition) -} - -// RemoveBusCondition removes the bus condition with the provided type. -func RemoveBusCondition(status *v1alpha1.BusStatus, condType v1alpha1.BusConditionType) { - status.Conditions = filterOutBusCondition(status.Conditions, condType) -} - -// ConsolidateBusCondition computes and sets the overall "Ready" condition of the bus -// given all other sub-conditions. -func ConsolidateBusCondition(bus v1alpha1.GenericBus) { - dispatching := GetBusCondition(*bus.GetStatus(), v1alpha1.BusDispatching) - provisioning := GetBusCondition(*bus.GetStatus(), v1alpha1.BusProvisioning) - serviceable := GetBusCondition(*bus.GetStatus(), v1alpha1.BusServiceable) - needsProvitioner := bus.GetSpec().Provisioner != nil - - var cond *v1alpha1.BusCondition - - if dispatching != nil && dispatching.Status == v1.ConditionTrue && - serviceable != nil && serviceable.Status == v1.ConditionTrue && - ((provisioning != nil && provisioning.Status == v1.ConditionTrue) || !needsProvitioner) { - cond = NewBusCondition(v1alpha1.BusReady, v1.ConditionTrue, "", "") - } else { - cond = NewBusCondition(v1alpha1.BusReady, v1.ConditionFalse, "", "") - } - SetBusCondition(bus.GetStatus(), *cond) -} - -// IsBusReady returns whether all readiness conditions of a bus are met, as a boolean. -func IsBusReady(status *v1alpha1.BusStatus) bool { - c := GetBusCondition(*status, v1alpha1.BusReady) - return c != nil && c.Status == v1.ConditionTrue -} - -// filterOutBusCondition returns a new slice of bus conditions without conditions with the provided type. -func filterOutBusCondition(conditions []v1alpha1.BusCondition, condType v1alpha1.BusConditionType) []v1alpha1.BusCondition { - var newConditions []v1alpha1.BusCondition - for _, c := range conditions { - if c.Type == condType { - continue - } - newConditions = append(newConditions, c) - } - return newConditions -} diff --git a/pkg/controller/util/channel_util.go b/pkg/controller/util/channel_util.go deleted file mode 100644 index 2fbce0caca0..00000000000 --- a/pkg/controller/util/channel_util.go +++ /dev/null @@ -1,103 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package util - -import ( - "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// NewChannelCondition creates a new channel condition with the provided values and both times set to now(). -func NewChannelCondition(condType v1alpha1.ChannelConditionType, status v1.ConditionStatus, reason, message string) *v1alpha1.ChannelCondition { - return &v1alpha1.ChannelCondition{ - Type: condType, - Status: status, - LastUpdateTime: meta_v1.Now(), - LastTransitionTime: meta_v1.Now(), - Reason: reason, - Message: message, - } -} - -// GetChannelCondition returns the channel condition with the provided type. -func GetChannelCondition(status v1alpha1.ChannelStatus, condType v1alpha1.ChannelConditionType) *v1alpha1.ChannelCondition { - for i := range status.Conditions { - c := status.Conditions[i] - if c.Type == condType { - return &c - } - } - return nil -} - -// SetChannelCondition updates the channel status to include the provided condition. If the condition that -// we are about to add already exists and has the same status and reason then no update happens. -func SetChannelCondition(status *v1alpha1.ChannelStatus, condition v1alpha1.ChannelCondition) { - currentCond := GetChannelCondition(*status, condition.Type) - if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason { - return - } - // Do not update lastTransitionTime if the status of the condition doesn't change. - if currentCond != nil && currentCond.Status == condition.Status { - condition.LastTransitionTime = currentCond.LastTransitionTime - } - newConditions := filterOutChannelCondition(status.Conditions, condition.Type) - status.Conditions = append(newConditions, condition) -} - -// RemoveChannelCondition removes the channel condition with the provided type. -func RemoveChannelCondition(status *v1alpha1.ChannelStatus, condType v1alpha1.ChannelConditionType) { - status.Conditions = filterOutChannelCondition(status.Conditions, condType) -} - -// ConsolidateChannelCondition computes and sets the overall "Ready" condition of the channel -// given all other sub-conditions. -func ConsolidateChannelCondition(status *v1alpha1.ChannelStatus) { - subConditionsTypes := []v1alpha1.ChannelConditionType{ - v1alpha1.ChannelProvisioned, - v1alpha1.ChannelRoutable, - v1alpha1.ChannelServiceable, - } - cond := NewChannelCondition(v1alpha1.ChannelReady, v1.ConditionTrue, "", "") - for _, t := range subConditionsTypes { - if c := GetChannelCondition(*status, t); c == nil || c.Status != v1.ConditionTrue { - cond.Status = v1.ConditionFalse - break - } - } - - SetChannelCondition(status, *cond) -} - -// IsChannelReady returns whether all readiness conditions of a channel are met, as a boolean. -func IsChannelReady(status *v1alpha1.ChannelStatus) bool { - c := GetChannelCondition(*status, v1alpha1.ChannelReady) - return c != nil && c.Status == v1.ConditionTrue -} - -// filterOutChannelCondition returns a new slice of channel conditions without conditions with the provided type. -func filterOutChannelCondition(conditions []v1alpha1.ChannelCondition, condType v1alpha1.ChannelConditionType) []v1alpha1.ChannelCondition { - var newConditions []v1alpha1.ChannelCondition - for _, c := range conditions { - if c.Type == condType { - continue - } - newConditions = append(newConditions, c) - } - return newConditions -} diff --git a/pkg/controller/util/stub_test.go b/pkg/controller/util/stub_test.go deleted file mode 100644 index 0fe9390462c..00000000000 --- a/pkg/controller/util/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package util - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/controller/util/subscription_util.go b/pkg/controller/util/subscription_util.go deleted file mode 100644 index d3db0ed6cc2..00000000000 --- a/pkg/controller/util/subscription_util.go +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2018 The Knative Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package util - -import ( - "github.com/knative/eventing/pkg/apis/channels/v1alpha1" - "k8s.io/api/core/v1" - meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// NewSubscriptionCondition creates a new subscription condition with the provided values and both times set to now(). -func NewSubscriptionCondition(condType v1alpha1.SubscriptionConditionType, status v1.ConditionStatus, reason, message string) *v1alpha1.SubscriptionCondition { - return &v1alpha1.SubscriptionCondition{ - Type: condType, - Status: status, - LastUpdateTime: meta_v1.Now(), - LastTransitionTime: meta_v1.Now(), - Reason: reason, - Message: message, - } -} - -// GetSubscriptionCondition returns the subscription condition with the provided type. -func GetSubscriptionCondition(status v1alpha1.SubscriptionStatus, condType v1alpha1.SubscriptionConditionType) *v1alpha1.SubscriptionCondition { - for i := range status.Conditions { - c := status.Conditions[i] - if c.Type == condType { - return &c - } - } - return nil -} - -// SetSubscriptionCondition updates the subscription status to include the provided condition. If the condition that -// we are about to add already exists and has the same status and reason then no update happens. -func SetSubscriptionCondition(status *v1alpha1.SubscriptionStatus, condition v1alpha1.SubscriptionCondition) { - currentCond := GetSubscriptionCondition(*status, condition.Type) - if currentCond != nil && currentCond.Status == condition.Status && currentCond.Reason == condition.Reason { - return - } - // Do not update lastTransitionTime if the status of the condition doesn't change. - if currentCond != nil && currentCond.Status == condition.Status { - condition.LastTransitionTime = currentCond.LastTransitionTime - } - newConditions := filterOutSubscriptionCondition(status.Conditions, condition.Type) - status.Conditions = append(newConditions, condition) -} - -// RemoveSubscriptionCondition removes the subscription condition with the provided type. -func RemoveSubscriptionCondition(status *v1alpha1.SubscriptionStatus, condType v1alpha1.SubscriptionConditionType) { - status.Conditions = filterOutSubscriptionCondition(status.Conditions, condType) -} - -// filterOutSubscriptionCondition returns a new slice of subscription conditions without conditions with the provided type. -func filterOutSubscriptionCondition(conditions []v1alpha1.SubscriptionCondition, condType v1alpha1.SubscriptionConditionType) []v1alpha1.SubscriptionCondition { - var newConditions []v1alpha1.SubscriptionCondition - for _, c := range conditions { - if c.Type == condType { - continue - } - newConditions = append(newConditions, c) - } - return newConditions -} diff --git a/pkg/sources/container_event_source_job.go b/pkg/sources/container_event_source_job.go deleted file mode 100644 index 0f948778c6b..00000000000 --- a/pkg/sources/container_event_source_job.go +++ /dev/null @@ -1,164 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package sources - -import ( - "encoding/base64" - "encoding/json" - - v1alpha1 "github.com/knative/eventing/pkg/apis/feeds/v1alpha1" - batchv1 "k8s.io/api/batch/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// FeedOperation specifies whether we're starting or stopping -type FeedOperation string - -const ( - // Each feed lifecycle pod gets these. - watcherContainerCPU = "400m" - - // START specifies a Feed should be started - StartFeed FeedOperation = "START" - - // STOP specifies a Feed should be stopped - StopFeed FeedOperation = "STOP" - - // FeedOperationKey is the Env variable that gets set to requested FeedOperationKey - FeedOperationKey string = "FEED_OPERATION" - - // FeedTriggerKey is the Env variable that gets set to serialized trigger configuration - FeedTriggerKey string = "FEED_TRIGGER" - - // FeedTargetKey is the Env variable that gets set to target of the feed - FeedTargetKey string = "FEED_TARGET" - - // FeedContextKey is the Env variable that gets set to serialized FeedContext if stopping - FeedContextKey string = "FEED_CONTEXT" - - // EventSourceParametersKey is the Env variable that gets set to serialized EventSourceSpec - EventSourceParametersKey string = "EVENT_SOURCE_PARAMETERS" - - // FeedNamespaceKey is the Env variable that gets set to namespace of the container performing - // the Feed lifecycle operation (aka, namespace of the feed). Uses downward api - FeedNamespaceKey string = "FEED_NAMESPACE" - - // FeedServiceAccount is the Env variable that gets set to serviceaccount of the - // container performing the Feed operation. Uses downward api - FeedServiceAccountKey string = "FEED_SERVICE_ACCOUNT" -) - -// MakeJob creates a Job to complete a start or stop operation for a Feed. -func MakeJob(feed *v1alpha1.Feed, spec *v1alpha1.EventSourceSpec, op FeedOperation, trigger EventTrigger, route string, feedContext FeedContext) (*batchv1.Job, error) { - labels := map[string]string{ - "app": "feedlifecyclepod", - } - - podTemplate, err := makePodTemplate(feed, spec, op, trigger, route, feedContext) - if err != nil { - return nil, err - } - - return &batchv1.Job{ - ObjectMeta: metav1.ObjectMeta{ - Name: "feedlet", - Namespace: feed.Namespace, - Labels: labels, - OwnerReferences: []metav1.OwnerReference{*metav1.NewControllerRef(feed, v1alpha1.SchemeGroupVersion.WithKind("Feed"))}, - }, - Spec: batchv1.JobSpec{ - Template: *podTemplate, - }, - }, nil -} - -// makePodTemplate creates a pod template for starting or stopping a feed. -func makePodTemplate(feed *v1alpha1.Feed, spec *v1alpha1.EventSourceSpec, op FeedOperation, trigger EventTrigger, route string, feedContext FeedContext) (*corev1.PodTemplateSpec, error) { - marshalledFeedContext, err := json.Marshal(feedContext) - if err != nil { - return nil, err - } - encodedFeedContext := base64.StdEncoding.EncodeToString(marshalledFeedContext) - - marshalledTrigger, err := json.Marshal(trigger) - if err != nil { - return nil, err - } - encodedTrigger := base64.StdEncoding.EncodeToString(marshalledTrigger) - - encodedParameters := "" - if spec.Parameters != nil { - marshalledParameters, err := json.Marshal(spec.Parameters) - if err != nil { - return nil, err - } - encodedParameters = base64.StdEncoding.EncodeToString(marshalledParameters) - } - - return &corev1.PodTemplateSpec{ - Spec: corev1.PodSpec{ - ServiceAccountName: feed.Spec.ServiceAccountName, - RestartPolicy: corev1.RestartPolicyNever, - Containers: []corev1.Container{ - { - Name: "feedlet", - Image: spec.Image, - ImagePullPolicy: "Always", - Env: []corev1.EnvVar{ - { - Name: FeedOperationKey, - Value: string(op), - }, - { - Name: FeedTargetKey, - Value: route, - }, - { - Name: FeedTriggerKey, - Value: encodedTrigger, - }, - { - Name: FeedContextKey, - Value: encodedFeedContext, - }, - { - Name: EventSourceParametersKey, - Value: encodedParameters, - }, - { - Name: FeedNamespaceKey, - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "metadata.namespace", - }, - }, - }, - { - Name: FeedServiceAccountKey, - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "spec.serviceAccountName", - }, - }, - }, - }, - }, - }, - }, - }, nil -} diff --git a/pkg/sources/event_source.go b/pkg/sources/event_source.go deleted file mode 100644 index ef3bf15c60e..00000000000 --- a/pkg/sources/event_source.go +++ /dev/null @@ -1,31 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package sources - -const ( - // EventTriggerKey is the Env variable that gets set to EventTrigger - EventTriggerKey string = "EVENT_TRIGGER" -) - -type FeedContext struct { - Context map[string]interface{} -} - -type EventSource interface { - StartFeed(trigger EventTrigger, route string) (*FeedContext, error) - StopFeed(trigger EventTrigger, feedContext FeedContext) error -} diff --git a/pkg/sources/event_source_wrapper.go b/pkg/sources/event_source_wrapper.go deleted file mode 100644 index 806bb7356d4..00000000000 --- a/pkg/sources/event_source_wrapper.go +++ /dev/null @@ -1,56 +0,0 @@ -package sources - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "io/ioutil" - "log" - "os" -) - -func RunEventSource(es EventSource) { - op := os.Getenv(FeedOperationKey) - feedContext := os.Getenv(FeedContextKey) - trigger := os.Getenv(FeedTriggerKey) - target := os.Getenv(FeedTargetKey) - - decodedContext, _ := base64.StdEncoding.DecodeString(feedContext) - decodedTrigger, _ := base64.StdEncoding.DecodeString(trigger) - var c FeedContext - var t EventTrigger - - err := json.Unmarshal(decodedContext, &c) - if err != nil { - panic(fmt.Sprintf("can not unmarshal %q %q : %s", FeedContextKey, decodedContext, err)) - } - - err = json.Unmarshal(decodedTrigger, &t) - if err != nil { - panic(fmt.Sprintf("can not unmarshal %q %q : %s", FeedTriggerKey, decodedTrigger, err)) - } - - log.Printf("Doing %q target: %q Received feedContext as %+v trigger %+v", op, target, c, t) - if op == string(StartFeed) { - b, err := es.StartFeed(t, target) - if err != nil { - panic(fmt.Sprintf("Failed to start feed: %s", err)) - } - - marshalledFeedContext, err := json.Marshal(b) - if err != nil { - panic(fmt.Sprintf("Failed to marshal returned feed context %+v : %s", b, err)) - } - encodedFeedContext := base64.StdEncoding.EncodeToString(marshalledFeedContext) - - err = ioutil.WriteFile("/dev/termination-log", []byte(encodedFeedContext), os.ModeDevice) - if err != nil { - panic(fmt.Sprintf("Failed to write the termination message: %s", err)) - } - } else { - err = es.StopFeed(t, c) - if err != nil { - panic(fmt.Sprintf("Failed to stop feed: %s", err)) - } - } -} diff --git a/pkg/sources/event_trigger.go b/pkg/sources/event_trigger.go deleted file mode 100644 index a36ff723607..00000000000 --- a/pkg/sources/event_trigger.go +++ /dev/null @@ -1,17 +0,0 @@ -package sources - -// EventTrigger is a version of v1alpha1.EventTrigger where -// parameters have been resolved. -type EventTrigger struct { - // EventType is the type of event to be observed - EventType string `json:"eventType"` - - // Resource is the resource(s) from which to observe events. - Resource string `json:"resource"` - - // Service is the hostname of the service that should be observed. - Service string `json:"service"` - - // Parameters is an opaque map of configuration needed by the EventSource. - Parameters map[string]interface{} `json:"params"` -} diff --git a/pkg/sources/gcppubsub/eventsource.yaml b/pkg/sources/gcppubsub/eventsource.yaml deleted file mode 100644 index 62d8d483a5d..00000000000 --- a/pkg/sources/gcppubsub/eventsource.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://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. - -apiVersion: feeds.knative.dev/v1alpha1 -kind: EventSource -metadata: - name: pubsub.googleapis.com - namespace: default -spec: - source: pubsub.googleapis.com - image: github.com/knative/eventing/pkg/sources/gcppubsub - parameters: - image: github.com/knative/eventing/pkg/sources/gcppubsub/receive_adapter diff --git a/pkg/sources/gcppubsub/eventtype.yaml b/pkg/sources/gcppubsub/eventtype.yaml deleted file mode 100644 index b86901eb734..00000000000 --- a/pkg/sources/gcppubsub/eventtype.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://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. - -apiVersion: feeds.knative.dev/v1alpha1 -kind: EventType -metadata: - name: google.pubsub.topic.publish - namespace: default -spec: - eventSource: pubsub.googleapis.com - description: "subscription for receiving pubsub messages" diff --git a/pkg/sources/gcppubsub/gcp_pubsub.go b/pkg/sources/gcppubsub/gcp_pubsub.go deleted file mode 100644 index 65bac12d013..00000000000 --- a/pkg/sources/gcppubsub/gcp_pubsub.go +++ /dev/null @@ -1,251 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "encoding/base64" - "encoding/json" - "flag" - "fmt" - "log" - "os" - "regexp" - - "github.com/golang/glog" - "github.com/google/uuid" - "github.com/knative/eventing/pkg/sources" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - apierrs "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - - "cloud.google.com/go/pubsub" - "golang.org/x/net/context" -) - -const ( - deployment = "deployment" - subscription = "subscription" -) - -var ( - resourceFormat = regexp.MustCompile("^projects/([^/]+)/topics/([^/]+)$") -) - -type GCPPubSubEventSource struct { - // kubeclientset is a standard kubernetes clientset - kubeclientset kubernetes.Interface - image string - // namespace where the feed is created - feedNamespace string - // serviceAccount that the container runs as. Launches Receive Adapter with the - // same Service Account - feedServiceAccountName string -} - -// The Go client libraries make you explicitly reconstruct the URI through builder patterns, so we sadly must crack it first. -func crackTopic(topic string) (projectID, topicID string, err error) { - matches := resourceFormat.FindStringSubmatch(topic) - if len(matches) != 3 { - return "", "", fmt.Errorf(`Google Cloud Pub/Sub topics must match the pattern projects/*/topics/*";Got %q`, topic) - } - return matches[1], matches[2], nil -} - -func NewGCPPubSubEventSource(kubeclientset kubernetes.Interface, feedNamespace string, feedServiceAccountName string, image string) sources.EventSource { - glog.Infof("Image: %q", image) - return &GCPPubSubEventSource{kubeclientset: kubeclientset, feedNamespace: feedNamespace, feedServiceAccountName: feedServiceAccountName, image: image} -} - -func (t *GCPPubSubEventSource) StopFeed(trigger sources.EventTrigger, feedContext sources.FeedContext) error { - glog.Infof("Stopping GCP PUBSUB Feed with context %+v", feedContext) - - projectID, _, err := crackTopic(trigger.Resource) - if err != nil { - glog.Warningf("Failed to delete deployment: %s", err) - return err - } - - if _, ok := feedContext.Context[deployment]; ok { - deploymentName := feedContext.Context[deployment].(string) - - err = t.deleteWatcher(t.feedNamespace, deploymentName) - if err != nil { - glog.Warningf("Failed to delete deployment: %s", err) - return err - } - } - - if _, ok := feedContext.Context[subscription]; ok { - subscriptionName := feedContext.Context[subscription].(string) - ctx := context.Background() - // Creates a client. - client, err := pubsub.NewClient(ctx, projectID) - if err != nil { - glog.Infof("Failed to create client: %v", err) - return err - } - - sub := client.Subscription(subscriptionName) - err = sub.Delete(ctx) - if err != nil { - glog.Warningf("Failed to delete subscription %q : %s : %+v", subscriptionName, err, err) - // Return error only if it's something else than NotFound - rpcStatus := status.Convert(err) - if rpcStatus.Code() != codes.NotFound { - return err - } - glog.Infof("Subscription %q already deleted", subscriptionName) - } - } - return nil -} - -func (t *GCPPubSubEventSource) StartFeed(trigger sources.EventTrigger, route string) (*sources.FeedContext, error) { - glog.Infof("CREATING GCP PUBSUB feed") - - projectID, topicID, err := crackTopic(trigger.Resource) - if err != nil { - glog.Infof("Failed to create deployment: %v", err) - return nil, err - } - - // Just generate a random UUID as a subscriptionName - uuid, err := uuid.NewRandom() - if err != nil { - glog.Infof("Failed to get create random uuid: %v", err) - return nil, err - } - subscriptionName := fmt.Sprintf("sub-%s", uuid.String()) - - glog.Infof("ProjectID: %q Topic: %q SubscriptionName: %q Route: %s", projectID, topicID, subscriptionName, route) - - ctx := context.Background() - - // TODO: Create a unique credential here just for this watcher... - - // Creates a client. - client, err := pubsub.NewClient(ctx, projectID) - if err != nil { - glog.Infof("Failed to create client: %v", err) - return nil, err - } - - sub, err := client.CreateSubscription(ctx, subscriptionName, - pubsub.SubscriptionConfig{Topic: client.Topic(topicID)}) - if err != nil { - glog.Infof("Failed to create subscription (might already exist): %+v", err) - return nil, err - } - - glog.Infof("Created subscription: %v", sub) - - // Create actual watcher - deploymentName := subscriptionName - err = t.createWatcher(deploymentName, t.image, projectID, topicID, subscriptionName, route) - if err != nil { - glog.Infof("Failed to create deployment: %v", err) - - // delete the subscription so it's not left floating around. - errDelete := sub.Delete(ctx) - if errDelete != nil { - glog.Infof("Failed to delete subscription while trying to clean up %q : %s", subscriptionName, errDelete) - } - - return nil, err - } - - return &sources.FeedContext{ - Context: map[string]interface{}{ - subscription: subscriptionName, - deployment: deploymentName, - }}, nil - -} - -func (t *GCPPubSubEventSource) createWatcher(deploymentName string, image string, projectID string, topicID string, subscription string, route string) error { - dc := t.kubeclientset.AppsV1().Deployments(t.feedNamespace) - - // First, check if deployment exists already. - if _, err := dc.Get(deploymentName, metav1.GetOptions{}); err != nil { - if !apierrs.IsNotFound(err) { - glog.Infof("deployments.Get for %q failed: %s", deploymentName, err) - return err - } - glog.Infof("Deployment %q doesn't exist, creating", deploymentName) - } else { - glog.Infof("Found existing deployment %q", deploymentName) - return nil - } - - // TODO: Create ownerref to the feed so when the feed goes away deployment - // gets removed. Currently we manually delete the deployment. - deployment := MakeWatcherDeployment(t.feedNamespace, deploymentName, t.feedServiceAccountName, image, projectID, topicID, subscription, route) - _, createErr := dc.Create(deployment) - return createErr -} - -func (t *GCPPubSubEventSource) deleteWatcher(namespace string, deploymentName string) error { - dc := t.kubeclientset.AppsV1().Deployments(namespace) - - // First, check if deployment exists already. - if _, err := dc.Get(deploymentName, metav1.GetOptions{}); err != nil { - if !apierrs.IsNotFound(err) { - glog.Infof("deployments.Get for %q failed: %s", deploymentName, err) - return err - } - glog.Infof("Deployment %q already deleted", deploymentName) - return nil - } - - return dc.Delete(deploymentName, &metav1.DeleteOptions{}) -} - -type parameters struct { - Image string `json:"image,omitempty"` -} - -func main() { - flag.Parse() - - decodedParameters, _ := base64.StdEncoding.DecodeString(os.Getenv(sources.EventSourceParametersKey)) - - feedNamespace := os.Getenv(sources.FeedNamespaceKey) - feedServiceAccountName := os.Getenv(sources.FeedServiceAccountKey) - - var p parameters - err := json.Unmarshal(decodedParameters, &p) - if err != nil { - panic(fmt.Sprintf("can not unmarshal %q : %s", decodedParameters, err)) - } - - cfg, err := clientcmd.BuildConfigFromFlags("", "") - if err != nil { - glog.Fatalf("Error building kubeconfig: %v", err) - } - - kubeClient, err := kubernetes.NewForConfig(cfg) - if err != nil { - glog.Fatalf("Error building kubernetes clientset: %v", err) - } - - sources.RunEventSource(NewGCPPubSubEventSource(kubeClient, feedNamespace, feedServiceAccountName, p.Image)) - log.Printf("Done...") -} diff --git a/pkg/sources/gcppubsub/googleapis-serviceentry.yaml b/pkg/sources/gcppubsub/googleapis-serviceentry.yaml deleted file mode 100644 index 7e1ba890110..00000000000 --- a/pkg/sources/gcppubsub/googleapis-serviceentry.yaml +++ /dev/null @@ -1,65 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: googleapis -spec: - hosts: - - "*.googleapis.com" - - "accounts.google.com" - ports: - - number: 443 - name: https - protocol: HTTPS - location: MESH_EXTERNAL ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: metadata-server-gke -spec: - hosts: - - metadata - ports: - - number: 80 - name: http - protocol: HTTP - - number: 443 - name: https - protocol: HTTPS - location: MESH_EXTERNAL - resolution: DNS - endpoints: - - address: 169.254.169.254 ---- -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: metadata-server -spec: - hosts: - - metadata.google.internal - addresses: - - 169.254.169.254 - ports: - - number: 80 - name: http - protocol: HTTP - - number: 443 - name: https - protocol: HTTPS - resolution: DNS - endpoints: - - address: 169.254.169.254 diff --git a/pkg/sources/gcppubsub/kodata/LICENSE b/pkg/sources/gcppubsub/kodata/LICENSE deleted file mode 120000 index 14776154326..00000000000 --- a/pkg/sources/gcppubsub/kodata/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../LICENSE \ No newline at end of file diff --git a/pkg/sources/gcppubsub/kodata/VENDOR-LICENSE b/pkg/sources/gcppubsub/kodata/VENDOR-LICENSE deleted file mode 120000 index 7322c09d957..00000000000 --- a/pkg/sources/gcppubsub/kodata/VENDOR-LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/pkg/sources/gcppubsub/receive_adapter/kodata/LICENSE b/pkg/sources/gcppubsub/receive_adapter/kodata/LICENSE deleted file mode 120000 index 2a64f9d0fc6..00000000000 --- a/pkg/sources/gcppubsub/receive_adapter/kodata/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../LICENSE \ No newline at end of file diff --git a/pkg/sources/gcppubsub/receive_adapter/kodata/VENDOR-LICENSE b/pkg/sources/gcppubsub/receive_adapter/kodata/VENDOR-LICENSE deleted file mode 120000 index 681789ed22f..00000000000 --- a/pkg/sources/gcppubsub/receive_adapter/kodata/VENDOR-LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/pkg/sources/gcppubsub/receive_adapter/receive_adapter.go b/pkg/sources/gcppubsub/receive_adapter/receive_adapter.go deleted file mode 100644 index 46933b12aee..00000000000 --- a/pkg/sources/gcppubsub/receive_adapter/receive_adapter.go +++ /dev/null @@ -1,97 +0,0 @@ -package main - -import ( - "flag" - "fmt" - "io/ioutil" - "log" - "net/http" - "os" - - // Imports the Google Cloud Pub/Sub client package. - "cloud.google.com/go/pubsub" - "github.com/knative/pkg/cloudevents" - "golang.org/x/net/context" -) - -const ( - // Environment variable containing project id - envProject = "PROJECT_ID" - - // Environment variable containing topic id - envTopic = "TOPIC_ID" - - // Target for messages - envTarget = "TARGET" - - // Name of the subscription to use - envSubscription = "SUBSCRIPTION" -) - -func main() { - flag.Parse() - - projectID := os.Getenv(envProject) - topicID := os.Getenv(envTopic) - target := os.Getenv(envTarget) - subscriptionName := os.Getenv(envSubscription) - - log.Printf("projectID is: %q", projectID) - log.Printf("topicID is: %q", topicID) - log.Printf("subscriptionName is: %q", subscriptionName) - log.Printf("Target is: %q", target) - - ctx := context.Background() - source := fmt.Sprintf("//pubsub.googleapis.com/projects/%s/topics/%s", projectID, topicID) - - // Creates a client. - // TODO: Support additional ways of specifying the credentials for creating. - client, err := pubsub.NewClient(ctx, projectID) - if err != nil { - log.Fatalf("Failed to create client: %v", err) - } - - sub := client.Subscription(subscriptionName) - - err = sub.Receive(ctx, func(ctx context.Context, m *pubsub.Message) { - log.Printf("Got message: %s", m.Data) - err = postMessage(target, source, m) - if err != nil { - log.Printf("Failed to post message: %s", err) - m.Nack() - } else { - m.Ack() - } - }) - if err != nil { - log.Fatalf("Failed to create receive: %v", err) - } -} - -func postMessage(target string, source string, m *pubsub.Message) error { - URL := fmt.Sprintf("http://%s/", target) - ctx := cloudevents.EventContext{ - CloudEventsVersion: cloudevents.CloudEventsVersion, - EventType: "google.pubsub.topic.publish", - EventID: m.ID, - EventTime: m.PublishTime, - Source: source, - } - req, err := cloudevents.Binary.NewRequest(URL, m, ctx) - if err != nil { - log.Printf("Failed to marshal the message: %+v : %s", m, err) - return err - } - - log.Printf("Posting to %q", URL) - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - log.Printf("response Status: %s", resp.Status) - body, _ := ioutil.ReadAll(resp.Body) - log.Printf("response Body: %s", string(body)) - return nil -} diff --git a/pkg/sources/gcppubsub/receive_adapter/stub_test.go b/pkg/sources/gcppubsub/receive_adapter/stub_test.go deleted file mode 100644 index ee7bbe8c445..00000000000 --- a/pkg/sources/gcppubsub/receive_adapter/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/sources/gcppubsub/stub_test.go b/pkg/sources/gcppubsub/stub_test.go deleted file mode 100644 index ee7bbe8c445..00000000000 --- a/pkg/sources/gcppubsub/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/sources/gcppubsub/watcher_pod.go b/pkg/sources/gcppubsub/watcher_pod.go deleted file mode 100644 index 086a328e31f..00000000000 --- a/pkg/sources/gcppubsub/watcher_pod.go +++ /dev/null @@ -1,83 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - sidecarIstioInjectAnnotation = "sidecar.istio.io/inject" -) - -// MakeWatcherDeployment creates a deployment for a watcher. -// TODO: a whole bunch... -func MakeWatcherDeployment(namespace string, deploymentName string, serviceAccount string, image string, projectID string, topicID string, subscription string, route string) *appsv1.Deployment { - replicas := int32(1) - labels := map[string]string{ - "watcher": deploymentName, - } - return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: deploymentName, - Namespace: namespace, - Labels: labels, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: &replicas, - Selector: &metav1.LabelSelector{MatchLabels: labels}, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - // Inject Istio so any connection made from the receive adapter - // goes through and is enforced by Istio. - Annotations: map[string]string{sidecarIstioInjectAnnotation: "true"}, - }, - Spec: corev1.PodSpec{ - ServiceAccountName: serviceAccount, - Containers: []corev1.Container{ - { - Name: "receive-adapter", - Image: image, - ImagePullPolicy: "Always", - Env: []corev1.EnvVar{ - { - Name: "PROJECT_ID", - Value: projectID, - }, - { - Name: "TOPIC_ID", - Value: topicID, - }, - { - Name: "SUBSCRIPTION", - Value: subscription, - }, - { - Name: "TARGET", - Value: route, - }, - }, - }, - }, - }, - }, - }, - } -} diff --git a/pkg/sources/github/README.md b/pkg/sources/github/README.md deleted file mode 100644 index c71e1061496..00000000000 --- a/pkg/sources/github/README.md +++ /dev/null @@ -1,99 +0,0 @@ -Knative GitHub Source -===================== - -This source will enable receiving of GitHub events from within Knative Eventing. - -## Events - -- dev.knative.github.pullrequest - -## Images - -Uses two images, the feedlet and the receive adapter. - -### Feedlet - -A Feedlet is a container to create or destroy event source bindings. - -The Feedlet is run as a Job by the Feed controller. - -This Feedlet will create the Receive Adapter and register a webhook in GitHub -for the account provided. - -### Receive Adapter - -The Receive Adapter is a Service.serving.knative.dev resource that is registered -to receive GitHub webhook requests. The source will wrap this request in a -CloudEvent and send it to the action provided. - -## Usage - -The GitHub Source expects there to be a secret in the following form: - -```yaml -apiVersion: v1 -kind: Secret -metadata: - name: githubsecret -type: Opaque -stringData: - githubCredentials: > - { - "accessToken": "", - "secretToken": "" - } - -``` - -The Feedlet requires a ServiceAccount to run as a cluster admin for the targeted namespace: - -```yaml -apiVersion: v1 -kind: ServiceAccount -metadata: - name: feed-sa - namespace: default ---- -apiVersion: rbac.authorization.k8s.io/v1beta1 -kind: ClusterRoleBinding -metadata: - name: feed-admin -subjects: - - kind: ServiceAccount - name: feed-sa - namespace: default -roleRef: - kind: ClusterRole - name: cluster-admin - apiGroup: rbac.authorization.k8s.io - -``` - -To create a Receive Adapter (via the Feedlet), a Flow needs to exist: - -```yaml -apiVersion: flows.knative.dev/v1alpha1 -kind: Flow -metadata: - name: my-github-flow - namespace: default -spec: - serviceAccountName: feed-sa - trigger: - eventType: pullrequest - resource: / # TODO: Fill this out - service: github - parameters: - secretName: githubsecret - secretKey: githubCredentials - parametersFrom: - - secretKeyRef: - name: githubsecret - key: githubCredentials - action: - target: - kind: Service - apiVersion: serving.knative.dev/v1alpha1 - name: my-service - -``` \ No newline at end of file diff --git a/pkg/sources/github/events.go b/pkg/sources/github/events.go deleted file mode 100644 index 310fd55287e..00000000000 --- a/pkg/sources/github/events.go +++ /dev/null @@ -1,32 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package github - -const ( - // Event Names - PullrequestEvent = "dev.knative.github.pullrequest" - UnsupportedEvent = "dev.knative.github.unsupported" -) - -var CloudEventType = map[string]string{ - "pull_request": PullrequestEvent, - "": UnsupportedEvent, -} - -var GithubEventType = map[string]string{ - PullrequestEvent: "pull_request", -} diff --git a/pkg/sources/github/eventsource.yaml b/pkg/sources/github/eventsource.yaml deleted file mode 100644 index a86410f0a4b..00000000000 --- a/pkg/sources/github/eventsource.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://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. - -apiVersion: feeds.knative.dev/v1alpha1 -kind: EventSource -metadata: - name: github - namespace: default -spec: - source: github - image: github.com/knative/eventing/pkg/sources/github/feedlet - parameters: - image: github.com/knative/eventing/pkg/sources/github/receive_adapter diff --git a/pkg/sources/github/eventtype.yaml b/pkg/sources/github/eventtype.yaml deleted file mode 100644 index 32069634693..00000000000 --- a/pkg/sources/github/eventtype.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://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. - -apiVersion: feeds.knative.dev/v1alpha1 -kind: EventType -metadata: - name: dev.knative.github.pullrequest - namespace: default -spec: - eventSource: github - description: "notifications on pullrequests" diff --git a/pkg/sources/github/feedlet/feedlet.go b/pkg/sources/github/feedlet/feedlet.go deleted file mode 100644 index 8e52a8953d1..00000000000 --- a/pkg/sources/github/feedlet/feedlet.go +++ /dev/null @@ -1,463 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "context" - "flag" - "fmt" - "strconv" - "strings" - - apierrs "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/knative/eventing/pkg/sources" - servingclientset "github.com/knative/serving/pkg/client/clientset/versioned" - "golang.org/x/oauth2" - - "encoding/base64" - "encoding/json" - "os" - - ghclient "github.com/google/go-github/github" - "github.com/knative/eventing/pkg/sources/github" - "github.com/knative/eventing/pkg/sources/github/resources" - "github.com/knative/serving/pkg/apis/serving/v1alpha1" - "k8s.io/apimachinery/pkg/watch" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" - "log" - "time" -) - -/* -A Feedlet is a container to create or destroy event source bindings. -The Feedlet is run as a Job by the Feed controller. -*/ - -const ( - webhookIDKey = "id" - - // property bag keys - accessTokenKey = "accessToken" - secretTokenKey = "secretToken" - - // receiveAdapterSuffix is appended to the name of the service running the Receive Adapter - receiveAdapterSuffix = "rcvadptr" - - // watchTimeout is the timeout that the feedlet will wait for the Receiver Adapter to get a domain name. - watchTimeout = 5 * time.Minute - - // secretNameKey is the name of the secret that contains the GitHub credentials. - secretNameKey = "secretName" - // secretKeyKey is the name of key inside the secret that contains the GitHub credentials. - secretKeyKey = "secretKey" -) - -type githubEventSource struct { - // kubeclientset is a standard kubernetes clientset - kubeclientset kubernetes.Interface - // servingclientset is a clientset for serving API groups - servingclientset servingclientset.Interface - // namespace where the feed is created - feedNamespace string - // serviceAccount that the container runs as. Launches Receive Adapter with the - // same Service Account - feedServiceAccountName string - // image for the receive adapter - image string -} - -func NewGithubEventSource(kubeclientset kubernetes.Interface, servingclientset servingclientset.Interface, feedNamespace, feedServiceAccountName, image string) sources.EventSource { - return &githubEventSource{ - kubeclientset: kubeclientset, - servingclientset: servingclientset, - feedNamespace: feedNamespace, - feedServiceAccountName: feedServiceAccountName, - image: image, - } -} - -// TODO(n3wscott): Add a timeout for StopFeed. -func (t *githubEventSource) StopFeed(trigger sources.EventTrigger, feedContext sources.FeedContext) error { - log.Printf("stopping github webhook feed with context %+v", feedContext) - - return t.deleteWebhook(trigger, feedContext) -} - -// TODO(n3wscott): Add a timeout for StartFeed. -func (t *githubEventSource) StartFeed(trigger sources.EventTrigger, target string) (*sources.FeedContext, error) { - - // Create the Receive Adapter Service that will accept incoming requests from GitHub. - service, err := t.createReceiveAdapter(trigger, target) - if err != nil { - return nil, fmt.Errorf("failed to create service: %v", err) - } - - log.Printf("created Service: %+v", service) - - // Start watching the Receive Adapter Service for it's updated domain name. This will be passed - // to GitHub as part of the webhook registration. - receiveAdapterDomain, err := t.waitForServiceDomain(service.GetObjectMeta().GetName()) - if err != nil { - return nil, fmt.Errorf("failed to get the service: %v", err) - } - - return t.createWebhook(trigger, service.GetObjectMeta().GetName(), receiveAdapterDomain) -} - -func (t *githubEventSource) waitForServiceDomain(serviceName string) (string, error) { - sc := t.servingclientset.ServingV1alpha1().Services(t.feedNamespace) - - wt := int64(watchTimeout / time.Second) - opts := metav1.ListOptions{ - TypeMeta: metav1.TypeMeta{ - Kind: "Service", - APIVersion: "v1alpha1", - }, - FieldSelector: fmt.Sprintf("metadata.name=%s", serviceName), - LabelSelector: "receive-adapter=github", - Watch: true, - TimeoutSeconds: &wt, - } - - w, err := sc.Watch(opts) - if err != nil { - log.Printf("[%s] failed to create watch: %v", serviceName, err) - return "", err - } - - eventChan := w.ResultChan() - for { - event, more := <-eventChan - if !more { - return "", fmt.Errorf("[%s] watch channel closed, failed to observe Service domain update", serviceName) - } - switch event.Type { - case watch.Error: - return "", fmt.Errorf("[%s] watched Service error", serviceName) - case watch.Deleted: - return "", fmt.Errorf("[%s] watched Service deleted", serviceName) - case watch.Added, watch.Modified: - service, ok := event.Object.(*v1alpha1.Service) - if !ok { - log.Printf("[%s] expected a Service object, but got %T", serviceName, event.Object) - continue - } - if service.Name != serviceName { - log.Printf("Error: [%s] expected a service.Name %q to match expected serviceName %q", - serviceName, service.Name, serviceName) - continue - } - status := service.Status - if status.Domain != "" { - w.Stop() - return status.Domain, nil - } - - } - } -} - -func receiveAdapterName(trigger sources.EventTrigger) string { - // TODO(n3wscott): this needs more UUID on the end of it? - // TODO(n3wscott): Currently this needs to be deterministic so StopFeed can find the receive adapter. If the receive - // adapter name were added to the feed context, then this could be a uuid. There is an issue where where we will - // get blocked by a pre-existing conflicting name and we are not able to unblock or regenerate without deleting - // the feed and trying it again. - serviceName := fmt.Sprintf("%s-%s-%s", "github", trigger.Resource, receiveAdapterSuffix) - serviceName = strings.Replace(serviceName, "/", "-", -1) - serviceName = strings.Replace(serviceName, ".", "-", -1) - serviceName = strings.ToLower(serviceName) - return serviceName -} - -func (t *githubEventSource) createReceiveAdapter(trigger sources.EventTrigger, target string) (*v1alpha1.Service, error) { - sc := t.servingclientset.ServingV1alpha1().Services(t.feedNamespace) - - serviceName := receiveAdapterName(trigger) - - // First, check if service exists already. - if sc, err := sc.Get(serviceName, metav1.GetOptions{}); err != nil { - if !apierrs.IsNotFound(err) { - return nil, fmt.Errorf("service.Get for %q failed: %v", serviceName, err) - } - log.Printf("service %q doesn't exist, creating", serviceName) - } else { - log.Printf("found existing service %q", serviceName) - // Don't try again. - return sc, nil - } - - secretName, err := stringFrom(trigger.Parameters, secretNameKey) - if err != nil { - return nil, fmt.Errorf("failed to get secret name from trigger parameters: %v", err) - } - - secretKey, err := stringFrom(trigger.Parameters, secretKeyKey) - if err != nil { - return nil, fmt.Errorf("failed to get secret key from trigger parameters: %v", err) - } - - // TODO(n3wscott): if we add an owner reference here we can remove the delete service call on stopFeed - service := resources.MakeService(t.feedNamespace, serviceName, t.image, t.feedServiceAccountName, target, secretName, secretKey) - service, err = sc.Create(service) - if err != nil { - return nil, fmt.Errorf("failed to create service: %s", err) - } - - return service, nil -} - -func (t *githubEventSource) deleteReceiveAdapter(namespace string, serviceName string) error { - sc := t.servingclientset.ServingV1alpha1().Services(t.feedNamespace) - - // First, check if deployment exists already. - if _, err := sc.Get(serviceName, metav1.GetOptions{}); err != nil { - if !apierrs.IsNotFound(err) { - return fmt.Errorf("services.Get for %q failed: %v", serviceName, err) - } - log.Printf("service %q already deleted", serviceName) - // Don't try again. - return nil - } - - return sc.Delete(serviceName, &metav1.DeleteOptions{}) -} - -func (t *githubEventSource) deleteWebhook(trigger sources.EventTrigger, feedContext sources.FeedContext) error { - serviceName := receiveAdapterName(trigger) - err := t.deleteReceiveAdapter(t.feedNamespace, serviceName) - if err != nil { - log.Printf("Error: Failed to delete the ReceiveAdapter \"%s/%s\": %v", t.feedNamespace, serviceName, err) - // Continue deleting the webhook. - } - - owner, repo, err := parseOwnerRepoFrom(trigger.Resource) - if err != nil { - log.Printf("Error: Failed to parse owner and repo from tigger.resource: %v; bailing...", err) - // Don't try again. - return nil - } - - webhookID, err := webhookIDFrom(feedContext) - if err != nil { - log.Printf("Error: Failed to get webhook id from context: %v; bailing...", err) - // Don't try again. - return nil - } - - accessToken, err := stringFrom(trigger.Parameters, accessTokenKey) - if err != nil { - log.Printf("Error: Failed to get access token from trigger parameters: %v; bailing...", err) - // Don't try again. - return nil - } - - ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: accessToken}, - ) - tc := oauth2.NewClient(ctx, ts) - client := ghclient.NewClient(tc) - - _, err = client.Repositories.DeleteHook(ctx, owner, repo, webhookID) - if err != nil { - // Note: errResp.Message == "Not Found" is not ideal, but the gh errors are not very easy to parse. - // It would be better to look into the errResp.Errors[] and confirm that the issue is "not found". - if errResp, ok := err.(*ghclient.ErrorResponse); ok && errResp.Message == "Not Found" { - // If the webhook doesn't exist, nothing to do - log.Printf("webhook doesn't exist, nothing to delete") - return nil - } - return fmt.Errorf("failed to delete the webhook: %v", err) - } - log.Printf("deleted webhook \"%d\" successfully", webhookID) - return nil -} - -func (t *githubEventSource) createWebhook(trigger sources.EventTrigger, name, domain string) (*sources.FeedContext, error) { - - log.Printf("creating GitHub webhook") - - owner, repo, err := parseOwnerRepoFrom(trigger.Resource) - if err != nil { - return nil, err - } - - events, err := parseEventsFrom(trigger.EventType) - if err != nil { - return nil, err - } - - accessToken, err := stringFrom(trigger.Parameters, accessTokenKey) - if err != nil { - return nil, fmt.Errorf("failed to get access token from trigger parameters: %v", err) - } - - secretToken, err := stringFrom(trigger.Parameters, secretTokenKey) - if err != nil { - return nil, fmt.Errorf("failed to get secret token from trigger parameters: %v", err) - } - - ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: accessToken}, - ) - tc := oauth2.NewClient(ctx, ts) - - client := ghclient.NewClient(tc) - active := true - config := make(map[string]interface{}) - config["url"] = fmt.Sprintf("http://%s", domain) - config["content_type"] = "json" - config["secret"] = secretToken - - // GitHub hook names are required to be named "web" or the name of a GitHub service - hookname := "web" - hook := ghclient.Hook{ - Name: &hookname, - URL: &domain, - Events: events, - Active: &active, - Config: config, - } - - h, r, err := client.Repositories.CreateHook(ctx, owner, repo, &hook) - if err != nil { - log.Printf("create webhook error response:\n%+v", r) - return nil, fmt.Errorf("failed to create the webhook: %v", err) - } - log.Printf("created hook: %+v", h) - - return &sources.FeedContext{ - Context: map[string]interface{}{ - webhookIDKey: strconv.FormatInt(*h.ID, 10), - }}, nil -} - -type parameters struct { - Image string `json:"image,omitempty"` -} - -// The main entry point for the GitHub Feedlet -func main() { - flag.Parse() - - log.Printf("GitHub Feedlet starting...") - - decodedParameters, _ := base64.StdEncoding.DecodeString(os.Getenv(sources.EventSourceParametersKey)) - - var p parameters - err := json.Unmarshal(decodedParameters, &p) - if err != nil { - log.Printf("Fatal: can not unmarshal %q : %v", decodedParameters, err) - os.Exit(1) - } - - feedNamespace := os.Getenv(sources.FeedNamespaceKey) - if len(feedNamespace) == 0 { - log.Printf("Fatal: feed namespace not provided, expected envvar %q to be set", sources.FeedNamespaceKey) - os.Exit(1) - } - - feedServiceAccountName := os.Getenv(sources.FeedServiceAccountKey) - if len(feedServiceAccountName) == 0 { - log.Printf("Fatal: feed service account not provided, expected envvar %q to be set", sources.FeedServiceAccountKey) - os.Exit(1) - } - - cfg, err := clientcmd.BuildConfigFromFlags("", "") - if err != nil { - log.Printf("Error: error building kubeconfig: %v", err) - } - - kubeClient, err := kubernetes.NewForConfig(cfg) - if err != nil { - log.Printf("Error: error building kubernetes clientset: %v", err) - } - - servingClient, err := servingclientset.NewForConfig(cfg) - if err != nil { - log.Printf("Error: error building serving clientset: %v", err) - } - - sources.RunEventSource(NewGithubEventSource(kubeClient, servingClient, feedNamespace, feedServiceAccountName, p.Image)) - log.Printf("GitHub Feedlet finished") -} - -func webhookIDFrom(feedContext sources.FeedContext) (int64, error) { - webhookID, err := stringFrom(feedContext.Context, webhookIDKey) - if err != nil { - return 0, err - - } - id, err := int64From(webhookID) - if err != nil { - return 0, fmt.Errorf("failed to convert webhook %q to int64 : %v", webhookID, err) - } - return id, nil -} - -func int64From(strNum string) (int64, error) { - return strconv.ParseInt(strNum, 10, 64) -} - -// TODO(n3wscott): Move this to knative/pkg/context. -func stringFrom(bag map[string]interface{}, key string) (string, error) { - // check to see if the key is in the bag. - if _, ok := bag[key]; !ok { - return "", fmt.Errorf("%s not found", key) - } - value, ok := bag[key].(string) - if !ok { - return "", fmt.Errorf("value for %s was not a valid string", key) - } - return value, nil -} - -func parseOwnerRepoFrom(resource string) (string, string, error) { - if len(resource) == 0 { - return "", "", fmt.Errorf("resouce is empty") - } - components := strings.Split(resource, "/") - if len(components) != 2 { - return "", "", fmt.Errorf("resouce is malformatted, expected 'owner/repo' but found %q", resource) - } - owner := components[0] - if len(owner) == 0 { - return "", "", fmt.Errorf("owner is empty, expected 'owner/repo' but found %q", resource) - } - repo := components[1] - if len(repo) == 0 { - return "", "", fmt.Errorf("repo is empty, expected 'owner/repo' but found %q", resource) - } - - return owner, repo, nil -} - -func parseEventsFrom(eventType string) ([]string, error) { - if len(eventType) == 0 { - return []string(nil), fmt.Errorf("event type is empty") - } - event, ok := github.GithubEventType[eventType] - if !ok { - return []string(nil), fmt.Errorf("event type is unknown: %s", eventType) - } - return []string{event}, nil -} diff --git a/pkg/sources/github/feedlet/kodata/LICENSE b/pkg/sources/github/feedlet/kodata/LICENSE deleted file mode 120000 index 2a64f9d0fc6..00000000000 --- a/pkg/sources/github/feedlet/kodata/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../LICENSE \ No newline at end of file diff --git a/pkg/sources/github/feedlet/kodata/VENDOR-LICENSE b/pkg/sources/github/feedlet/kodata/VENDOR-LICENSE deleted file mode 120000 index 681789ed22f..00000000000 --- a/pkg/sources/github/feedlet/kodata/VENDOR-LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/pkg/sources/github/feedlet/stub_test.go b/pkg/sources/github/feedlet/stub_test.go deleted file mode 100644 index ee7bbe8c445..00000000000 --- a/pkg/sources/github/feedlet/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/sources/github/github-serviceentry.yaml b/pkg/sources/github/github-serviceentry.yaml deleted file mode 100644 index 9c9576aae7e..00000000000 --- a/pkg/sources/github/github-serviceentry.yaml +++ /dev/null @@ -1,25 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://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. -apiVersion: networking.istio.io/v1alpha3 -kind: ServiceEntry -metadata: - name: github-receive-adapter-ext -spec: - hosts: - - "*.github.com" - ports: - - number: 443 - name: https - protocol: HTTPS - location: MESH_EXTERNAL diff --git a/pkg/sources/github/receive_adapter/kodata/LICENSE b/pkg/sources/github/receive_adapter/kodata/LICENSE deleted file mode 120000 index 2a64f9d0fc6..00000000000 --- a/pkg/sources/github/receive_adapter/kodata/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../LICENSE \ No newline at end of file diff --git a/pkg/sources/github/receive_adapter/kodata/VENDOR-LICENSE b/pkg/sources/github/receive_adapter/kodata/VENDOR-LICENSE deleted file mode 120000 index 681789ed22f..00000000000 --- a/pkg/sources/github/receive_adapter/kodata/VENDOR-LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/pkg/sources/github/receive_adapter/receive_adapter.go b/pkg/sources/github/receive_adapter/receive_adapter.go deleted file mode 100644 index fcfbe62f1ef..00000000000 --- a/pkg/sources/github/receive_adapter/receive_adapter.go +++ /dev/null @@ -1,151 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - https://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "encoding/json" - "flag" - "fmt" - "os" - - "gopkg.in/go-playground/webhooks.v3" - gh "gopkg.in/go-playground/webhooks.v3/github" - - "io/ioutil" - "net/http" - - ghclient "github.com/google/go-github/github" - "github.com/google/uuid" - "github.com/knative/eventing/pkg/sources/github" - "github.com/knative/pkg/cloudevents" - "log" - "time" -) - -const ( - // Target for messages - envTarget = "TARGET" - // Environment variable containing json credentials - envSecret = "GITHUB_SECRET" -) - -// GithubHandler holds necessary objects for communicating with the Github. -type GithubHandler struct { - client *ghclient.Client - target string -} - -type GithubSecrets struct { - AccessToken string `json:"accessToken"` - SecretToken string `json:"secretToken"` -} - -// HandlePullRequest is invoked whenever a PullRequest is modified (created, updated, etc.) -func (h *GithubHandler) HandlePullRequest(payload interface{}, header webhooks.Header) { - log.Print("Handling Pull Request") - - hdr := http.Header(header) - - pl := payload.(gh.PullRequestPayload) - - source := pl.PullRequest.HTMLURL - - eventType, ok := github.CloudEventType[hdr.Get("X-GitHub-Event")] - if !ok { - eventType = github.UnsupportedEvent - } - - eventID := hdr.Get("X-GitHub-Delivery") - if len(eventID) == 0 { - if uuid, err := uuid.NewRandom(); err != nil { - eventID = uuid.String() - } - } - - postMessage(h.target, payload, source, eventType, eventID) -} - -func main() { - flag.Parse() - - target := os.Getenv(envTarget) - - log.Printf("Target is: %q", target) - - githubSecrets := os.Getenv(envSecret) - - var credentials GithubSecrets - err := json.Unmarshal([]byte(githubSecrets), &credentials) - if err != nil { - log.Fatalf("Failed to unmarshal credentials: %v", err) - return - } - - // Set up the auth for being able to talk to Github. - var tc *http.Client = nil - - client := ghclient.NewClient(tc) - - h := &GithubHandler{ - client: client, - target: target, - } - - hook := gh.New(&gh.Config{Secret: credentials.SecretToken}) - // TODO: GitHub has more than just Pull Request Events. This needs to - // handle them all? - hook.RegisterEvents(h.HandlePullRequest, gh.PullRequestEvent) - - // TODO(n3wscott): Do we need to configure the PORT? - err = webhooks.Run(hook, ":8080", "/") - if err != nil { - log.Fatalf("Failed to run the webhook") - } -} - -func postMessage(target string, payload interface{}, source, eventType, eventID string) error { - URL := fmt.Sprintf("http://%s/", target) - - ctx := cloudevents.EventContext{ - CloudEventsVersion: cloudevents.CloudEventsVersion, - EventType: eventType, - EventID: eventID, - EventTime: time.Now(), - Source: source, - } - req, err := cloudevents.Binary.NewRequest(URL, payload, ctx) - if err != nil { - log.Printf("Failed to marshal the message: %+v : %s", payload, err) - return err - } - - log.Printf("Posting to %q", URL) - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - return err - } - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - // TODO: in general, receive adapters may have to be able to retry for error cases. - log.Printf("response Status: %s", resp.Status) - body, _ := ioutil.ReadAll(resp.Body) - log.Printf("response Body: %s", string(body)) - } - return nil -} diff --git a/pkg/sources/github/receive_adapter/stub_test.go b/pkg/sources/github/receive_adapter/stub_test.go deleted file mode 100644 index ee7bbe8c445..00000000000 --- a/pkg/sources/github/receive_adapter/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/sources/github/resources/service.go b/pkg/sources/github/resources/service.go deleted file mode 100644 index 23d1576b6bb..00000000000 --- a/pkg/sources/github/resources/service.go +++ /dev/null @@ -1,68 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resources - -import ( - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "github.com/knative/serving/pkg/apis/serving/v1alpha1" -) - -func MakeService(namespace, name, image, serviceAccount, target, githubSecret, githubSecretKey string) *v1alpha1.Service { - labels := map[string]string{ - "receive-adapter": "github", - } - return &v1alpha1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Namespace: namespace, - Labels: labels, - }, - Spec: v1alpha1.ServiceSpec{ - RunLatest: &v1alpha1.RunLatestType{ - Configuration: v1alpha1.ConfigurationSpec{ - RevisionTemplate: v1alpha1.RevisionTemplateSpec{ - Spec: v1alpha1.RevisionSpec{ - ServiceAccountName: serviceAccount, - Container: corev1.Container{ - Image: image, - Env: []corev1.EnvVar{ - { - Name: "GITHUB_SECRET", - ValueFrom: &corev1.EnvVarSource{ - SecretKeyRef: &corev1.SecretKeySelector{ - LocalObjectReference: corev1.LocalObjectReference{ - Name: githubSecret, - }, - Key: githubSecretKey, - }, - }, - }, - { - Name: "TARGET", - Value: target, - }, - }, - }, - }, - }, - }, - }, - }, - } -} diff --git a/pkg/sources/github/resources/stub_test.go b/pkg/sources/github/resources/stub_test.go deleted file mode 100644 index d4c244afe73..00000000000 --- a/pkg/sources/github/resources/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package resources - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/sources/github/stub_test.go b/pkg/sources/github/stub_test.go deleted file mode 100644 index b92a55ef568..00000000000 --- a/pkg/sources/github/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package github - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/sources/k8sevents/eventsource.yaml b/pkg/sources/k8sevents/eventsource.yaml deleted file mode 100644 index d2cc3a3c150..00000000000 --- a/pkg/sources/k8sevents/eventsource.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://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. - -apiVersion: feeds.knative.dev/v1alpha1 -kind: EventSource -metadata: - name: k8sevents - namespace: default -spec: - source: k8sevents - image: github.com/knative/eventing/pkg/sources/k8sevents - parameters: - image: github.com/knative/eventing/pkg/sources/k8sevents/receive_adapter diff --git a/pkg/sources/k8sevents/eventtype.yaml b/pkg/sources/k8sevents/eventtype.yaml deleted file mode 100644 index 5b7aa277609..00000000000 --- a/pkg/sources/k8sevents/eventtype.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# Copyright 2018 The Knative Authors -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://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. - -apiVersion: feeds.knative.dev/v1alpha1 -kind: EventType -metadata: - # Warning: This event type has not been ratified by any K8S SIG and may - # change in future versions of Knative Eventing. - name: dev.knative.k8s.event - namespace: default -spec: - eventSource: k8sevents - description: "subscription for receiving k8s cluster events" diff --git a/pkg/sources/k8sevents/k8s_events.go b/pkg/sources/k8sevents/k8s_events.go deleted file mode 100644 index d4be32ab7a8..00000000000 --- a/pkg/sources/k8sevents/k8s_events.go +++ /dev/null @@ -1,171 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "encoding/base64" - "encoding/json" - "flag" - "fmt" - "log" - "os" - - "github.com/golang/glog" - "github.com/google/uuid" - "github.com/knative/eventing/pkg/sources" - - apierrs "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/clientcmd" -) - -const ( - projectIDKey = "projectID" - deployment = "deployment" - subscription = "subscription" -) - -type K8SEventsEventSource struct { - // kubeclientset is a standard kubernetes clientset - kubeclientset kubernetes.Interface - image string - // namespace where the feed is created - feedNamespace string - // serviceAccount that the container runs as. Launches Receive Adapter with the - // same Service Account - feedServiceAccountName string -} - -func NewK8SEventsEventSource(kubeclientset kubernetes.Interface, feedNamespace string, feedServiceAccountName string, image string) sources.EventSource { - glog.Infof("Image: %q", image) - return &K8SEventsEventSource{kubeclientset: kubeclientset, feedNamespace: feedNamespace, feedServiceAccountName: feedServiceAccountName, image: image} -} - -func (t *K8SEventsEventSource) StopFeed(trigger sources.EventTrigger, feedContext sources.FeedContext) error { - glog.Infof("Stopping K8S Events feed with context %+v", feedContext) - - deploymentName := feedContext.Context[deployment].(string) - - err := t.deleteWatcher(deploymentName) - if err != nil { - glog.Warningf("Failed to delete deployment: %s", err) - return err - } - return nil -} - -func (t *K8SEventsEventSource) StartFeed(trigger sources.EventTrigger, route string) (*sources.FeedContext, error) { - glog.Infof("CREATING K8S Event feed") - - namespace := trigger.Parameters["namespace"].(string) - - // Just generate a random UUID as a subscriptionName - uuid, err := uuid.NewRandom() - if err != nil { - glog.Infof("Failed to create new random uuid: %v", err) - return nil, err - } - subscriptionName := fmt.Sprintf("sub-%s", uuid.String()) - - glog.Infof("Namespace: %q Route: %s", namespace, route) - - // Create actual watcher - deploymentName := subscriptionName - err = t.createWatcher(deploymentName, t.image, namespace, route) - if err != nil { - glog.Infof("Failed to create deployment: %v", err) - return nil, err - } - - return &sources.FeedContext{ - Context: map[string]interface{}{ - subscription: subscriptionName, - deployment: deploymentName, - }}, nil - -} - -func (t *K8SEventsEventSource) createWatcher(deploymentName string, image string, eventNamespace string, route string) error { - dc := t.kubeclientset.AppsV1().Deployments(t.feedNamespace) - - // First, check if deployment exists already. - if _, err := dc.Get(deploymentName, metav1.GetOptions{}); err != nil { - if !apierrs.IsNotFound(err) { - glog.Infof("deployments.Get for %q failed: %v", deploymentName, err) - return err - } - glog.Infof("Deployment %q doesn't exist, creating", deploymentName) - } else { - glog.Infof("Found existing deployment %q", deploymentName) - return nil - } - - // TODO: Create ownerref to the feed so when the feed goes away deployment - // gets removed. Currently we manually delete the deployment. - deployment := MakeWatcherDeployment(t.feedNamespace, deploymentName, t.feedServiceAccountName, image, eventNamespace, route) - _, createErr := dc.Create(deployment) - return createErr -} - -func (t *K8SEventsEventSource) deleteWatcher(deploymentName string) error { - dc := t.kubeclientset.AppsV1().Deployments(t.feedNamespace) - - // First, check if deployment exists already. - if _, err := dc.Get(deploymentName, metav1.GetOptions{}); err != nil { - if !apierrs.IsNotFound(err) { - glog.Infof("deployments.Get for %q failed: %v", deploymentName, err) - return err - } - glog.Infof("Deployment %q already deleted", deploymentName) - return nil - } - - return dc.Delete(deploymentName, &metav1.DeleteOptions{}) -} - -type parameters struct { - Image string `json:"image,omitempty"` -} - -func main() { - flag.Parse() - - decodedParameters, _ := base64.StdEncoding.DecodeString(os.Getenv(sources.EventSourceParametersKey)) - - feedNamespace := os.Getenv(sources.FeedNamespaceKey) - feedServiceAccountName := os.Getenv(sources.FeedServiceAccountKey) - - var p parameters - err := json.Unmarshal(decodedParameters, &p) - if err != nil { - panic(fmt.Sprintf("can not unmarshal %q : %v", decodedParameters, err)) - } - - cfg, err := clientcmd.BuildConfigFromFlags("", "") - if err != nil { - glog.Fatalf("Error building kubeconfig: %v", err) - } - - kubeClient, err := kubernetes.NewForConfig(cfg) - if err != nil { - glog.Fatalf("Error building kubernetes clientset: %v", err) - } - - sources.RunEventSource(NewK8SEventsEventSource(kubeClient, feedNamespace, feedServiceAccountName, p.Image)) - log.Printf("Done...") -} diff --git a/pkg/sources/k8sevents/kodata/LICENSE b/pkg/sources/k8sevents/kodata/LICENSE deleted file mode 120000 index 14776154326..00000000000 --- a/pkg/sources/k8sevents/kodata/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../LICENSE \ No newline at end of file diff --git a/pkg/sources/k8sevents/kodata/VENDOR-LICENSE b/pkg/sources/k8sevents/kodata/VENDOR-LICENSE deleted file mode 120000 index 7322c09d957..00000000000 --- a/pkg/sources/k8sevents/kodata/VENDOR-LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/pkg/sources/k8sevents/receive_adapter/kodata/LICENSE b/pkg/sources/k8sevents/receive_adapter/kodata/LICENSE deleted file mode 120000 index 2a64f9d0fc6..00000000000 --- a/pkg/sources/k8sevents/receive_adapter/kodata/LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../LICENSE \ No newline at end of file diff --git a/pkg/sources/k8sevents/receive_adapter/kodata/VENDOR-LICENSE b/pkg/sources/k8sevents/receive_adapter/kodata/VENDOR-LICENSE deleted file mode 120000 index 681789ed22f..00000000000 --- a/pkg/sources/k8sevents/receive_adapter/kodata/VENDOR-LICENSE +++ /dev/null @@ -1 +0,0 @@ -../../../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/pkg/sources/k8sevents/receive_adapter/receive_adapter.go b/pkg/sources/k8sevents/receive_adapter/receive_adapter.go deleted file mode 100644 index e40ea37ebb8..00000000000 --- a/pkg/sources/k8sevents/receive_adapter/receive_adapter.go +++ /dev/null @@ -1,194 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - "flag" - "fmt" - "io/ioutil" - "log" - "net/http" - "os" - "strings" - - "github.com/golang/glog" - "github.com/knative/pkg/cloudevents" - "github.com/knative/pkg/signals" - corev1 "k8s.io/api/core/v1" - coreinformers "k8s.io/client-go/informers/core/v1" - "k8s.io/client-go/kubernetes" - "k8s.io/client-go/tools/cache" - "k8s.io/client-go/tools/clientcmd" -) - -const ( - // Target for messages - envTarget = "TARGET" - // Namespace to watch - envNamespace = "NAMESPACE" -) - -type EventWatcher struct { - target string -} - -func NewEventWatcher(target string) *EventWatcher { - return &EventWatcher{target: target} -} - -func (e *EventWatcher) updateEvent(old, new interface{}) { - e.addEvent(new) -} - -func (e *EventWatcher) addEvent(new interface{}) { - event := new.(*corev1.Event) - log.Printf("GOT EVENT: %+v", event) - postMessage(e.target, event) -} - -func main() { - flag.Parse() - - // set up signals so we handle the first shutdown signal gracefully - stopCh := signals.SetupSignalHandler() - - target := os.Getenv(envTarget) - namespace := os.Getenv(envNamespace) - - log.Printf("Target is: %q", target) - log.Printf("Namespace is: %q", namespace) - - cfg, err := clientcmd.BuildConfigFromFlags("", "") - if err != nil { - log.Fatalf("Error building kubeconfig: %v", err) - } - - kubeClient, err := kubernetes.NewForConfig(cfg) - if err != nil { - log.Fatalf("Error building kubernetes clientset: %v", err) - } - - log.Printf("Creating a new Event Watcher...") - watcher := NewEventWatcher(target) - - eventsInformer := coreinformers.NewFilteredEventInformer( - kubeClient, namespace, 0, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, nil) - - eventsInformer.AddEventHandler(cache.ResourceEventHandlerFuncs{ - AddFunc: watcher.addEvent, - UpdateFunc: watcher.updateEvent, - }) - - log.Printf("Starting eventsInformer...") - go eventsInformer.Run(stopCh) - - log.Printf("Waiting for caches to sync...") - if ok := cache.WaitForCacheSync(stopCh, eventsInformer.HasSynced); !ok { - glog.Fatalf("Failed to wait for events cache to sync") - } - log.Printf("Caches synced...") - <-stopCh - log.Printf("Exiting...") -} - -// Creates a URI of the form found in object metadata selfLinks -// Format looks like: /apis/feeds.knative.dev/v1alpha1/namespaces/default/feeds/k8s-events-example -// KNOWN ISSUES: -// * ObjectReference.APIVersion has no version information (e.g. serving.knative.dev rather than serving.knative.dev/v1alpha1) -// * ObjectReference does not have enough information to create the pluaralized list type (e.g. "revisions" from kind: Revision) -// -// Track these issues at https://github.com/kubernetes/kubernetes/issues/66313 -// We could possibly work around this by adding a lister for the resources referenced by these events. -func createSelfLink(o corev1.ObjectReference) string { - collectionNameHack := strings.ToLower(o.Kind) + "s" - versionNameHack := o.APIVersion - - // Core API types don't have a separate package name and only have a version string (e.g. /apis/v1/namespaces/default/pods/myPod) - // To avoid weird looking strings like "v1/versionUnknown" we'll sniff for a "." in the version - if strings.Contains(versionNameHack, ".") && !strings.Contains(versionNameHack, "/") { - versionNameHack = versionNameHack + "/versionUnknown" - } - return fmt.Sprintf("/apis/%s/namespaces/%s/%s/%s", versionNameHack, o.Namespace, collectionNameHack, o.Name) -} - -// Creates a CloudEvent Context for a given K8S event. For clarity, the following is a spew-dump -// of a real K8S event: -// &Event{ -// ObjectMeta:k8s_io_apimachinery_pkg_apis_meta_v1.ObjectMeta{ -// Name:k8s-events-00001.1542495ae3f5fbba, -// Namespace:default, -// SelfLink:/api/v1/namespaces/default/events/k8s-events-00001.1542495ae3f5fbba, -// UID:fc2fffb3-8a12-11e8-8874-42010a8a0fd9, -// ResourceVersion:2729, -// Generation:0, -// CreationTimestamp:2018-07-17 22:44:37 +0000 UTC, -// }, -// InvolvedObject:ObjectReference{ -// Kind:Revision, -// Namespace:default, -// Name:k8s-events-00001, -// UID:f5c19306-8a12-11e8-8874-42010a8a0fd9, -// APIVersion:serving.knative.dev, -// ResourceVersion:42683, -// }, -// Reason:RevisionReady, -// Message:Revision becomes ready upon endpoint "k8s-events-00001-service" becoming ready, -// Source:EventSource{ -// Component:revision-controller, -// }, -// FirstTimestamp:2018-07-17 22:44:37 +0000 UTC, -// LastTimestamp:2018-07-17 22:49:40 +0000 UTC, -// Count:91, -// Type:Normal, -// EventTime:0001-01-01 00:00:00 +0000 UTC, -// } -func cloudEventsContext(m *corev1.Event) *cloudevents.EventContext { - return &cloudevents.EventContext{ - // Events are themselves object and have a unique UUID. Could also have used the UID - CloudEventsVersion: cloudevents.CloudEventsVersion, - EventType: "dev.knative.k8s.event", - EventID: string(m.ObjectMeta.UID), - Source: createSelfLink(m.InvolvedObject), - EventTime: m.ObjectMeta.CreationTimestamp.Time, - } -} - -func postMessage(target string, m *corev1.Event) error { - ctx := cloudEventsContext(m) - - URL := fmt.Sprintf("http://%s/", target) - log.Printf("Posting to %q", URL) - // Explicitly using Binary encoding so that Istio, et. al. can better inspect - // event metadata. - req, err := cloudevents.Binary.NewRequest(URL, m, *ctx) - if err != nil { - log.Printf("Failed to create http request: %s", err) - return err - } - - client := &http.Client{} - resp, err := client.Do(req) - if err != nil { - log.Printf("Failed to do POST: %v", err) - return err - } - defer resp.Body.Close() - log.Printf("response Status: %s", resp.Status) - body, _ := ioutil.ReadAll(resp.Body) - log.Printf("response Body: %s", string(body)) - return nil -} diff --git a/pkg/sources/k8sevents/receive_adapter/stub_test.go b/pkg/sources/k8sevents/receive_adapter/stub_test.go deleted file mode 100644 index ee7bbe8c445..00000000000 --- a/pkg/sources/k8sevents/receive_adapter/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/sources/k8sevents/stub_test.go b/pkg/sources/k8sevents/stub_test.go deleted file mode 100644 index ee7bbe8c445..00000000000 --- a/pkg/sources/k8sevents/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/pkg/sources/k8sevents/watcher_pod.go b/pkg/sources/k8sevents/watcher_pod.go deleted file mode 100644 index 0fee312d272..00000000000 --- a/pkg/sources/k8sevents/watcher_pod.go +++ /dev/null @@ -1,75 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package main - -import ( - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -const ( - sidecarIstioInjectAnnotation = "sidecar.istio.io/inject" -) - -// MakeWatcherDeployment creates a deployment for a watcher. -// TODO: a whole bunch... -func MakeWatcherDeployment(namespace string, deploymentName string, serviceAccount string, image string, eventNamespace string, route string) *appsv1.Deployment { - replicas := int32(1) - labels := map[string]string{ - "watcher": deploymentName, - } - return &appsv1.Deployment{ - ObjectMeta: metav1.ObjectMeta{ - Name: deploymentName, - Namespace: namespace, - Labels: labels, - }, - Spec: appsv1.DeploymentSpec{ - Replicas: &replicas, - Selector: &metav1.LabelSelector{MatchLabels: labels}, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: labels, - // Inject Istio so any connection made from the receive adapter - // goes through and is enforced by Istio. - Annotations: map[string]string{sidecarIstioInjectAnnotation: "true"}, - }, - Spec: corev1.PodSpec{ - ServiceAccountName: serviceAccount, - Containers: []corev1.Container{ - { - Name: "receive-adapter", - Image: image, - ImagePullPolicy: "Always", - Env: []corev1.EnvVar{ - { - Name: "TARGET", - Value: route, - }, - { - Name: "NAMESPACE", - Value: eventNamespace, - }, - }, - }, - }, - }, - }, - }, - } -} diff --git a/pkg/sources/stub_test.go b/pkg/sources/stub_test.go deleted file mode 100644 index 46f51918354..00000000000 --- a/pkg/sources/stub_test.go +++ /dev/null @@ -1,24 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package sources - -import "testing" - -func TestStub(t *testing.T) { - // TODO: Implement tests in this directory, please. - // This file is used to make code coverage on this directly count. -} diff --git a/sample/README.md b/sample/README.md index 8d41f0c6cf3..69f85cf45ca 100644 --- a/sample/README.md +++ b/sample/README.md @@ -1,12 +1,3 @@ # Samples -This location is a staging ground for _Knative eventing_ samples, which are being moved to [the docs repository](https://github.com/knative/docs/tree/master/eventing/samples). - -The `docs` repo contains the following best-maintained samples: - -* [Handling Kubernetes events](https://github.com/knative/docs/tree/master/eventing/samples/k8s-events) - - A simple handler for processing k8s events from the local cluster. -* [Binding running services to an IoT core](https://github.com/knative/docs/tree/master/eventing/samples/event-flow) - - A sample using Google PubSub to read events from Google's IoT core. -* [Github Pull Request Handler](https://github.com/knative/docs/tree/master/eventing/samples/github-events) - - A simple handler for Github Pull Requests +_Knative eventing_ samples have moved to [the docs repository](https://github.com/knative/docs/tree/master/eventing/samples). diff --git a/test/crd_checks.go b/test/crd_checks.go index e551e342cc4..62d93f72732 100644 --- a/test/crd_checks.go +++ b/test/crd_checks.go @@ -23,8 +23,6 @@ import ( "fmt" "time" - flowsV1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" - flowstyped "github.com/knative/eventing/pkg/client/clientset/versioned/typed/flows/v1alpha1" servingV1alpha1 "github.com/knative/serving/pkg/apis/serving/v1alpha1" servingtyped "github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1" "go.opencensus.io/trace" @@ -54,21 +52,3 @@ func WaitForRouteState(client servingtyped.RouteInterface, name string, inState return inState(r) }) } - -// WaitForFlowState polls the status of the Flow called name -// from client every interval until inState returns `true` indicating it -// is done, returns an error or timeout. desc will be used to name the metric -// that is emitted to track how long it took for name to get into the state checked by inState. -func WaitForFlowState(client flowstyped.FlowInterface, name string, inState func(s *flowsV1alpha1.Flow) (bool, error), desc string) error { - metricName := fmt.Sprintf("WaitForFlowState/%s/%s", name, desc) - _, span := trace.StartSpan(context.Background(), metricName) - defer span.End() - - return wait.PollImmediate(interval, timeout, func() (bool, error) { - s, err := client.Get(name, metav1.GetOptions{}) - if err != nil { - return true, err - } - return inState(s) - }) -} diff --git a/test/image_paths.txt b/test/image_paths.txt index 62cdf1286b5..30e6b3e4451 100644 --- a/test/image_paths.txt +++ b/test/image_paths.txt @@ -1,8 +1,2 @@ -# The dispatcher used by the ClusterBus to dispatch messages between channels and subscriptions -github.com/knative/eventing/pkg/buses/stub/dispatcher -# The event source wrapper for Kubernetes events -github.com/knative/eventing/pkg/sources/k8sevents -# The actual event source which receives events and posts them to the given Route -github.com/knative/eventing/pkg/sources/k8sevents/receive_adapter # The receiver of events which is accessible through Route github.com/knative/eventing/test/e2e/k8sevents diff --git a/test/states.go b/test/states.go index e239dd12a81..849f5e360d8 100644 --- a/test/states.go +++ b/test/states.go @@ -16,7 +16,6 @@ limitations under the License. package test import ( - flowsV1alpha1 "github.com/knative/eventing/pkg/apis/flows/v1alpha1" "github.com/knative/serving/pkg/apis/serving/v1alpha1" corev1 "k8s.io/api/core/v1" ) @@ -44,12 +43,6 @@ func IsRouteReady(r *v1alpha1.Route) (bool, error) { return r.Status.IsReady(), nil } -// IsFlowReady will check the status conditions of the flow and return true if the flow is -// ready. -func IsFlowReady(f *flowsV1alpha1.Flow) (bool, error) { - return f.Status.IsReady(), nil -} - // PodsRunning will check the status conditions of the pod list and return true all pods are Running func PodsRunning(podList *corev1.PodList) (bool, error) { for _, pod := range podList.Items { diff --git a/third_party/VENDOR-LICENSE b/third_party/VENDOR-LICENSE index 95fbb4d3739..a6e32d48afd 100644 --- a/third_party/VENDOR-LICENSE +++ b/third_party/VENDOR-LICENSE @@ -207,32 +207,6 @@ Import: github.com/knative/eventing/vendor/cloud.google.com/go -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/Shopify/sarama - -Copyright (c) 2013 Shopify - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - =========================================================== Import: github.com/knative/eventing/vendor/github.com/beorn7/perks @@ -259,34 +233,6 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/bsm/sarama-cluster - -(The MIT License) - -Copyright (c) 2017 Black Square Media Ltd - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - =========================================================== Import: github.com/knative/eventing/vendor/github.com/davecgh/go-spew @@ -308,87 +254,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/eapache/go-resiliency - -The MIT License (MIT) - -Copyright (c) 2014 Evan Huus - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - - - -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/eapache/go-xerial-snappy - -The MIT License (MIT) - -Copyright (c) 2016 Evan Huus - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/eapache/queue - -The MIT License (MIT) - -Copyright (c) 2014 Evan Huus - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - - =========================================================== Import: github.com/knative/eventing/vendor/github.com/evanphx/json-patch @@ -1411,39 +1276,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/golang/snappy - -Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - =========================================================== Import: github.com/knative/eventing/vendor/github.com/google/btree @@ -1685,386 +1517,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/google/go-github - -Copyright (c) 2013 The go-github AUTHORS. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------- - -Some documentation is taken from the GitHub Developer site -, which is available under the following Creative -Commons Attribution 3.0 License. This applies only to the go-github source -code and would not apply to any compiled binaries. - -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE -COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY -COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS -AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. - -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE -TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY -BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS -CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND -CONDITIONS. - -1. Definitions - - a. "Adaptation" means a work based upon the Work, or upon the Work and - other pre-existing works, such as a translation, adaptation, - derivative work, arrangement of music or other alterations of a - literary or artistic work, or phonogram or performance and includes - cinematographic adaptations or any other form in which the Work may be - recast, transformed, or adapted including in any form recognizably - derived from the original, except that a work that constitutes a - Collection will not be considered an Adaptation for the purpose of - this License. For the avoidance of doubt, where the Work is a musical - work, performance or phonogram, the synchronization of the Work in - timed-relation with a moving image ("synching") will be considered an - Adaptation for the purpose of this License. - b. "Collection" means a collection of literary or artistic works, such as - encyclopedias and anthologies, or performances, phonograms or - broadcasts, or other works or subject matter other than works listed - in Section 1(f) below, which, by reason of the selection and - arrangement of their contents, constitute intellectual creations, in - which the Work is included in its entirety in unmodified form along - with one or more other contributions, each constituting separate and - independent works in themselves, which together are assembled into a - collective whole. A work that constitutes a Collection will not be - considered an Adaptation (as defined above) for the purposes of this - License. - c. "Distribute" means to make available to the public the original and - copies of the Work or Adaptation, as appropriate, through sale or - other transfer of ownership. - d. "Licensor" means the individual, individuals, entity or entities that - offer(s) the Work under the terms of this License. - e. "Original Author" means, in the case of a literary or artistic work, - the individual, individuals, entity or entities who created the Work - or if no individual or entity can be identified, the publisher; and in - addition (i) in the case of a performance the actors, singers, - musicians, dancers, and other persons who act, sing, deliver, declaim, - play in, interpret or otherwise perform literary or artistic works or - expressions of folklore; (ii) in the case of a phonogram the producer - being the person or legal entity who first fixes the sounds of a - performance or other sounds; and, (iii) in the case of broadcasts, the - organization that transmits the broadcast. - f. "Work" means the literary and/or artistic work offered under the terms - of this License including without limitation any production in the - literary, scientific and artistic domain, whatever may be the mode or - form of its expression including digital form, such as a book, - pamphlet and other writing; a lecture, address, sermon or other work - of the same nature; a dramatic or dramatico-musical work; a - choreographic work or entertainment in dumb show; a musical - composition with or without words; a cinematographic work to which are - assimilated works expressed by a process analogous to cinematography; - a work of drawing, painting, architecture, sculpture, engraving or - lithography; a photographic work to which are assimilated works - expressed by a process analogous to photography; a work of applied - art; an illustration, map, plan, sketch or three-dimensional work - relative to geography, topography, architecture or science; a - performance; a broadcast; a phonogram; a compilation of data to the - extent it is protected as a copyrightable work; or a work performed by - a variety or circus performer to the extent it is not otherwise - considered a literary or artistic work. - g. "You" means an individual or entity exercising rights under this - License who has not previously violated the terms of this License with - respect to the Work, or who has received express permission from the - Licensor to exercise rights under this License despite a previous - violation. - h. "Publicly Perform" means to perform public recitations of the Work and - to communicate to the public those public recitations, by any means or - process, including by wire or wireless means or public digital - performances; to make available to the public Works in such a way that - members of the public may access these Works from a place and at a - place individually chosen by them; to perform the Work to the public - by any means or process and the communication to the public of the - performances of the Work, including by public digital performance; to - broadcast and rebroadcast the Work by any means including signs, - sounds or images. - i. "Reproduce" means to make copies of the Work by any means including - without limitation by sound or visual recordings and the right of - fixation and reproducing fixations of the Work, including storage of a - protected performance or phonogram in digital form or other electronic - medium. - -2. Fair Dealing Rights. Nothing in this License is intended to reduce, -limit, or restrict any uses free from copyright or rights arising from -limitations or exceptions that are provided for in connection with the -copyright protection under copyright law or other applicable laws. - -3. License Grant. Subject to the terms and conditions of this License, -Licensor hereby grants You a worldwide, royalty-free, non-exclusive, -perpetual (for the duration of the applicable copyright) license to -exercise the rights in the Work as stated below: - - a. to Reproduce the Work, to incorporate the Work into one or more - Collections, and to Reproduce the Work as incorporated in the - Collections; - b. to create and Reproduce Adaptations provided that any such Adaptation, - including any translation in any medium, takes reasonable steps to - clearly label, demarcate or otherwise identify that changes were made - to the original Work. For example, a translation could be marked "The - original work was translated from English to Spanish," or a - modification could indicate "The original work has been modified."; - c. to Distribute and Publicly Perform the Work including as incorporated - in Collections; and, - d. to Distribute and Publicly Perform Adaptations. - e. For the avoidance of doubt: - - i. Non-waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme cannot be waived, the Licensor - reserves the exclusive right to collect such royalties for any - exercise by You of the rights granted under this License; - ii. Waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme can be waived, the Licensor waives the - exclusive right to collect such royalties for any exercise by You - of the rights granted under this License; and, - iii. Voluntary License Schemes. The Licensor waives the right to - collect royalties, whether individually or, in the event that the - Licensor is a member of a collecting society that administers - voluntary licensing schemes, via that society, from any exercise - by You of the rights granted under this License. - -The above rights may be exercised in all media and formats whether now -known or hereafter devised. The above rights include the right to make -such modifications as are technically necessary to exercise the rights in -other media and formats. Subject to Section 8(f), all rights not expressly -granted by Licensor are hereby reserved. - -4. Restrictions. The license granted in Section 3 above is expressly made -subject to and limited by the following restrictions: - - a. You may Distribute or Publicly Perform the Work only under the terms - of this License. You must include a copy of, or the Uniform Resource - Identifier (URI) for, this License with every copy of the Work You - Distribute or Publicly Perform. You may not offer or impose any terms - on the Work that restrict the terms of this License or the ability of - the recipient of the Work to exercise the rights granted to that - recipient under the terms of the License. You may not sublicense the - Work. You must keep intact all notices that refer to this License and - to the disclaimer of warranties with every copy of the Work You - Distribute or Publicly Perform. When You Distribute or Publicly - Perform the Work, You may not impose any effective technological - measures on the Work that restrict the ability of a recipient of the - Work from You to exercise the rights granted to that recipient under - the terms of the License. This Section 4(a) applies to the Work as - incorporated in a Collection, but this does not require the Collection - apart from the Work itself to be made subject to the terms of this - License. If You create a Collection, upon notice from any Licensor You - must, to the extent practicable, remove from the Collection any credit - as required by Section 4(b), as requested. If You create an - Adaptation, upon notice from any Licensor You must, to the extent - practicable, remove from the Adaptation any credit as required by - Section 4(b), as requested. - b. If You Distribute, or Publicly Perform the Work or any Adaptations or - Collections, You must, unless a request has been made pursuant to - Section 4(a), keep intact all copyright notices for the Work and - provide, reasonable to the medium or means You are utilizing: (i) the - name of the Original Author (or pseudonym, if applicable) if supplied, - and/or if the Original Author and/or Licensor designate another party - or parties (e.g., a sponsor institute, publishing entity, journal) for - attribution ("Attribution Parties") in Licensor's copyright notice, - terms of service or by other reasonable means, the name of such party - or parties; (ii) the title of the Work if supplied; (iii) to the - extent reasonably practicable, the URI, if any, that Licensor - specifies to be associated with the Work, unless such URI does not - refer to the copyright notice or licensing information for the Work; - and (iv) , consistent with Section 3(b), in the case of an Adaptation, - a credit identifying the use of the Work in the Adaptation (e.g., - "French translation of the Work by Original Author," or "Screenplay - based on original Work by Original Author"). The credit required by - this Section 4 (b) may be implemented in any reasonable manner; - provided, however, that in the case of a Adaptation or Collection, at - a minimum such credit will appear, if a credit for all contributing - authors of the Adaptation or Collection appears, then as part of these - credits and in a manner at least as prominent as the credits for the - other contributing authors. For the avoidance of doubt, You may only - use the credit required by this Section for the purpose of attribution - in the manner set out above and, by exercising Your rights under this - License, You may not implicitly or explicitly assert or imply any - connection with, sponsorship or endorsement by the Original Author, - Licensor and/or Attribution Parties, as appropriate, of You or Your - use of the Work, without the separate, express prior written - permission of the Original Author, Licensor and/or Attribution - Parties. - c. Except as otherwise agreed in writing by the Licensor or as may be - otherwise permitted by applicable law, if You Reproduce, Distribute or - Publicly Perform the Work either by itself or as part of any - Adaptations or Collections, You must not distort, mutilate, modify or - take other derogatory action in relation to the Work which would be - prejudicial to the Original Author's honor or reputation. Licensor - agrees that in those jurisdictions (e.g. Japan), in which any exercise - of the right granted in Section 3(b) of this License (the right to - make Adaptations) would be deemed to be a distortion, mutilation, - modification or other derogatory action prejudicial to the Original - Author's honor and reputation, the Licensor will waive or not assert, - as appropriate, this Section, to the fullest extent permitted by the - applicable national law, to enable You to reasonably exercise Your - right under Section 3(b) of this License (right to make Adaptations) - but not otherwise. - -5. Representations, Warranties and Disclaimer - -UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR -OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY -KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, -INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, -FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF -LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, -WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION -OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. - -6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE -LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR -ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES -ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS -BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -7. Termination - - a. This License and the rights granted hereunder will terminate - automatically upon any breach by You of the terms of this License. - Individuals or entities who have received Adaptations or Collections - from You under this License, however, will not have their licenses - terminated provided such individuals or entities remain in full - compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will - survive any termination of this License. - b. Subject to the above terms and conditions, the license granted here is - perpetual (for the duration of the applicable copyright in the Work). - Notwithstanding the above, Licensor reserves the right to release the - Work under different license terms or to stop distributing the Work at - any time; provided, however that any such election will not serve to - withdraw this License (or any other license that has been, or is - required to be, granted under the terms of this License), and this - License will continue in full force and effect unless terminated as - stated above. - -8. Miscellaneous - - a. Each time You Distribute or Publicly Perform the Work or a Collection, - the Licensor offers to the recipient a license to the Work on the same - terms and conditions as the license granted to You under this License. - b. Each time You Distribute or Publicly Perform an Adaptation, Licensor - offers to the recipient a license to the original Work on the same - terms and conditions as the license granted to You under this License. - c. If any provision of this License is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability of - the remainder of the terms of this License, and without further action - by the parties to this agreement, such provision shall be reformed to - the minimum extent necessary to make such provision valid and - enforceable. - d. No term or provision of this License shall be deemed waived and no - breach consented to unless such waiver or consent shall be in writing - and signed by the party to be charged with such waiver or consent. - e. This License constitutes the entire agreement between the parties with - respect to the Work licensed here. There are no understandings, - agreements or representations with respect to the Work not specified - here. Licensor shall not be bound by any additional provisions that - may appear in any communication from You. This License may not be - modified without the mutual written agreement of the Licensor and You. - f. The rights granted under, and the subject matter referenced, in this - License were drafted utilizing the terminology of the Berne Convention - for the Protection of Literary and Artistic Works (as amended on - September 28, 1979), the Rome Convention of 1961, the WIPO Copyright - Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 - and the Universal Copyright Convention (as revised on July 24, 1971). - These rights and subject matter take effect in the relevant - jurisdiction in which the License terms are sought to be enforced - according to the corresponding provisions of the implementation of - those treaty provisions in the applicable national law. If the - standard suite of rights granted under applicable copyright law - includes additional rights not granted under this License, such - additional rights are deemed to be included in the License; this - License is not intended to restrict the license of any rights under - applicable law. - - -Creative Commons Notice - - Creative Commons is not a party to this License, and makes no warranty - whatsoever in connection with the Work. Creative Commons will not be - liable to You or any party on any legal theory for any damages - whatsoever, including without limitation any general, special, - incidental or consequential damages arising in connection to this - license. Notwithstanding the foregoing two (2) sentences, if Creative - Commons has expressly identified itself as the Licensor hereunder, it - shall have all rights and obligations of Licensor. - - Except for the limited purpose of indicating to the public that the - Work is licensed under the CCPL, Creative Commons does not authorize - the use by either party of the trademark "Creative Commons" or any - related trademark or logo of Creative Commons without the prior - written consent of Creative Commons. Any permitted use will be in - compliance with Creative Commons' then-current trademark usage - guidelines, as may be published on its website or otherwise made - available upon request from time to time. For the avoidance of doubt, - this trademark restriction does not form part of this License. - - Creative Commons may be contacted at http://creativecommons.org/. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/google/go-querystring - -Copyright (c) 2013 Google. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - =========================================================== Import: github.com/knative/eventing/vendor/github.com/google/gofuzz @@ -2306,39 +1758,6 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/googleapis/gax-go - -Copyright 2016, Google Inc. -All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - =========================================================== Import: github.com/knative/eventing/vendor/github.com/googleapis/gnostic @@ -3019,8 +2438,7 @@ SOFTWARE. =========================================================== -Import: github.com/knative/eventing/vendor/github.com/knative/build - +Import: github.com/knative/eventing/vendor/github.com/knative/pkg Apache License Version 2.0, January 2004 @@ -3227,11 +2645,24 @@ Import: github.com/knative/eventing/vendor/github.com/knative/build =========================================================== -Import: github.com/knative/eventing/vendor/github.com/knative/pkg +Import: github.com/knative/eventing/vendor/github.com/markbates/inflect - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +Copyright (c) 2011 Chris Farmiloe + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/mattbaird/jsonpatch + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION @@ -3409,7 +2840,7 @@ Import: github.com/knative/eventing/vendor/github.com/knative/pkg APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" + boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -3417,7 +2848,7 @@ Import: github.com/knative/eventing/vendor/github.com/knative/pkg same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -3433,9 +2864,9 @@ Import: github.com/knative/eventing/vendor/github.com/knative/pkg -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/knative/serving +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/matttproud/golang_protobuf_extensions Apache License Version 2.0, January 2004 @@ -3617,7 +3048,7 @@ Import: github.com/knative/eventing/vendor/github.com/knative/serving APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" + boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -3625,7 +3056,7 @@ Import: github.com/knative/eventing/vendor/github.com/knative/serving same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright [yyyy] [name of copyright owner] + Copyright {yyyy} {name of copyright owner} Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -3642,20 +3073,7 @@ Import: github.com/knative/eventing/vendor/github.com/knative/serving =========================================================== -Import: github.com/knative/eventing/vendor/github.com/markbates/inflect - -Copyright (c) 2011 Chris Farmiloe - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/mattbaird/jsonpatch +Import: github.com/knative/eventing/vendor/github.com/modern-go/concurrent Apache License Version 2.0, January 2004 @@ -3837,7 +3255,7 @@ Import: github.com/knative/eventing/vendor/github.com/mattbaird/jsonpatch APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -3845,7 +3263,7 @@ Import: github.com/knative/eventing/vendor/github.com/mattbaird/jsonpatch same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {yyyy} {name of copyright owner} + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -3861,9 +3279,8 @@ Import: github.com/knative/eventing/vendor/github.com/mattbaird/jsonpatch - =========================================================== -Import: github.com/knative/eventing/vendor/github.com/matttproud/golang_protobuf_extensions +Import: github.com/knative/eventing/vendor/github.com/modern-go/reflect2 Apache License Version 2.0, January 2004 @@ -4045,7 +3462,7 @@ Import: github.com/knative/eventing/vendor/github.com/matttproud/golang_protobuf APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "{}" + boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a @@ -4053,7 +3470,7 @@ Import: github.com/knative/eventing/vendor/github.com/matttproud/golang_protobuf same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright {yyyy} {name of copyright owner} + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -4070,7 +3487,65 @@ Import: github.com/knative/eventing/vendor/github.com/matttproud/golang_protobuf =========================================================== -Import: github.com/knative/eventing/vendor/github.com/modern-go/concurrent +Import: github.com/knative/eventing/vendor/github.com/pborman/uuid + +Copyright (c) 2009,2014 Google Inc. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/peterbourgon/diskv + +Copyright (c) 2011-2012 Peter Bourgon + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/prometheus/client_golang Apache License Version 2.0, January 2004 @@ -4277,7 +3752,7 @@ Import: github.com/knative/eventing/vendor/github.com/modern-go/concurrent =========================================================== -Import: github.com/knative/eventing/vendor/github.com/modern-go/reflect2 +Import: github.com/knative/eventing/vendor/github.com/prometheus/client_model Apache License Version 2.0, January 2004 @@ -4484,99 +3959,7 @@ Import: github.com/knative/eventing/vendor/github.com/modern-go/reflect2 =========================================================== -Import: github.com/knative/eventing/vendor/github.com/pborman/uuid - -Copyright (c) 2009,2014 Google Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/peterbourgon/diskv - -Copyright (c) 2011-2012 Peter Bourgon - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/pierrec/lz4 - -Copyright (c) 2015, Pierre Curto -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of xxHash nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - - -=========================================================== -Import: github.com/knative/eventing/vendor/github.com/prometheus/client_golang +Import: github.com/knative/eventing/vendor/github.com/prometheus/common Apache License Version 2.0, January 2004 @@ -4783,7 +4166,7 @@ Import: github.com/knative/eventing/vendor/github.com/prometheus/client_golang =========================================================== -Import: github.com/knative/eventing/vendor/github.com/prometheus/client_model +Import: github.com/knative/eventing/vendor/github.com/prometheus/procfs Apache License Version 2.0, January 2004 @@ -4990,459 +4373,283 @@ Import: github.com/knative/eventing/vendor/github.com/prometheus/client_model =========================================================== -Import: github.com/knative/eventing/vendor/github.com/prometheus/common +Import: github.com/knative/eventing/vendor/github.com/spf13/pflag - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +Copyright (c) 2012 Alex Ogier. All rights reserved. +Copyright (c) 2012 The Go Authors. All rights reserved. - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: - 1. Definitions. + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. +=========================================================== +Import: github.com/knative/eventing/vendor/go.uber.org/atomic - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. +Copyright (c) 2016 Uber Technologies, Inc. - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. +=========================================================== +Import: github.com/knative/eventing/vendor/go.uber.org/multierr - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: +Copyright (c) 2017 Uber Technologies, Inc. - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. +=========================================================== +Import: github.com/knative/eventing/vendor/go.uber.org/zap - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. +Copyright (c) 2016-2017 Uber Technologies, Inc. - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. - END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/crypto - Copyright [yyyy] [name of copyright owner] +Copyright (c) 2009 The Go Authors. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: - http://www.apache.org/licenses/LICENSE-2.0 + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. - 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. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =========================================================== -Import: github.com/knative/eventing/vendor/github.com/prometheus/procfs +Import: github.com/knative/eventing/vendor/golang.org/x/net - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +Copyright (c) 2009 The Go Authors. All rights reserved. - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: - 1. Definitions. + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/oauth2 - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. +Copyright (c) 2009 The Go Authors. All rights reserved. - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - END OF TERMS AND CONDITIONS - APPENDIX: How to apply the Apache License to your work. - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. +=========================================================== +Import: github.com/knative/eventing/vendor/golang.org/x/sync - Copyright [yyyy] [name of copyright owner] +Copyright (c) 2009 The Go Authors. All rights reserved. - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: - http://www.apache.org/licenses/LICENSE-2.0 + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. - 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. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =========================================================== -Import: github.com/knative/eventing/vendor/github.com/rcrowley/go-metrics +Import: github.com/knative/eventing/vendor/golang.org/x/sys -Copyright 2012 Richard Crowley. All rights reserved. +Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - -THIS SOFTWARE IS PROVIDED BY RICHARD CROWLEY ``AS IS'' AND ANY EXPRESS -OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL RICHARD CROWLEY OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -THE POSSIBILITY OF SUCH DAMAGE. + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. -The views and conclusions contained in the software and documentation -are those of the authors and should not be interpreted as representing -official policies, either expressed or implied, of Richard Crowley. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =========================================================== -Import: github.com/knative/eventing/vendor/github.com/spf13/pflag +Import: github.com/knative/eventing/vendor/golang.org/x/text -Copyright (c) 2012 Alex Ogier. All rights reserved. -Copyright (c) 2012 The Go Authors. All rights reserved. +Copyright (c) 2009 The Go Authors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are @@ -5473,992 +4680,35 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. =========================================================== -Import: github.com/knative/eventing/vendor/go.opencensus.io - +Import: github.com/knative/eventing/vendor/golang.org/x/time - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ +Copyright (c) 2009 The Go Authors. All rights reserved. - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - - -=========================================================== -Import: github.com/knative/eventing/vendor/go.uber.org/atomic - -Copyright (c) 2016 Uber Technologies, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/go.uber.org/multierr - -Copyright (c) 2017 Uber Technologies, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/go.uber.org/zap - -Copyright (c) 2016-2017 Uber Technologies, Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/crypto - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/net - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/oauth2 - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/sync - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/sys - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/text - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/golang.org/x/time - -Copyright (c) 2009 The Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/google.golang.org/api - -Copyright (c) 2011 Google Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/google.golang.org/genproto - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/google.golang.org/grpc - - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. - - - -=========================================================== -Import: github.com/knative/eventing/vendor/gopkg.in/go-playground/webhooks.v3 - -The MIT License (MIT) - -Copyright (c) 2015 Dean Karn - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/cloud.google.com/go/iam/iam.go b/vendor/cloud.google.com/go/iam/iam.go deleted file mode 100644 index 37720aa2d67..00000000000 --- a/vendor/cloud.google.com/go/iam/iam.go +++ /dev/null @@ -1,284 +0,0 @@ -// Copyright 2016 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 iam supports the resource-specific operations of Google Cloud -// IAM (Identity and Access Management) for the Google Cloud Libraries. -// See https://cloud.google.com/iam for more about IAM. -// -// Users of the Google Cloud Libraries will typically not use this package -// directly. Instead they will begin with some resource that supports IAM, like -// a pubsub topic, and call its IAM method to get a Handle for that resource. -package iam - -import ( - "time" - - gax "github.com/googleapis/gax-go" - "golang.org/x/net/context" - pb "google.golang.org/genproto/googleapis/iam/v1" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" -) - -// client abstracts the IAMPolicy API to allow multiple implementations. -type client interface { - Get(ctx context.Context, resource string) (*pb.Policy, error) - Set(ctx context.Context, resource string, p *pb.Policy) error - Test(ctx context.Context, resource string, perms []string) ([]string, error) -} - -// grpcClient implements client for the standard gRPC-based IAMPolicy service. -type grpcClient struct { - c pb.IAMPolicyClient -} - -var withRetry = gax.WithRetry(func() gax.Retryer { - return gax.OnCodes([]codes.Code{ - codes.DeadlineExceeded, - codes.Unavailable, - }, gax.Backoff{ - Initial: 100 * time.Millisecond, - Max: 60 * time.Second, - Multiplier: 1.3, - }) -}) - -func (g *grpcClient) Get(ctx context.Context, resource string) (*pb.Policy, error) { - var proto *pb.Policy - err := gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error { - var err error - proto, err = g.c.GetIamPolicy(ctx, &pb.GetIamPolicyRequest{Resource: resource}) - return err - }, withRetry) - if err != nil { - return nil, err - } - return proto, nil -} - -func (g *grpcClient) Set(ctx context.Context, resource string, p *pb.Policy) error { - return gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error { - _, err := g.c.SetIamPolicy(ctx, &pb.SetIamPolicyRequest{ - Resource: resource, - Policy: p, - }) - return err - }, withRetry) -} - -func (g *grpcClient) Test(ctx context.Context, resource string, perms []string) ([]string, error) { - var res *pb.TestIamPermissionsResponse - err := gax.Invoke(ctx, func(ctx context.Context, _ gax.CallSettings) error { - var err error - res, err = g.c.TestIamPermissions(ctx, &pb.TestIamPermissionsRequest{ - Resource: resource, - Permissions: perms, - }) - return err - }, withRetry) - if err != nil { - return nil, err - } - return res.Permissions, nil -} - -// A Handle provides IAM operations for a resource. -type Handle struct { - c client - resource string -} - -// InternalNewHandle is for use by the Google Cloud Libraries only. -// -// InternalNewHandle returns a Handle for resource. -// The conn parameter refers to a server that must support the IAMPolicy service. -func InternalNewHandle(conn *grpc.ClientConn, resource string) *Handle { - return InternalNewHandleClient(&grpcClient{c: pb.NewIAMPolicyClient(conn)}, resource) -} - -// InternalNewHandleClient is for use by the Google Cloud Libraries only. -// -// InternalNewHandleClient returns a Handle for resource using the given -// client implementation. -func InternalNewHandleClient(c client, resource string) *Handle { - return &Handle{ - c: c, - resource: resource, - } -} - -// Policy retrieves the IAM policy for the resource. -func (h *Handle) Policy(ctx context.Context) (*Policy, error) { - proto, err := h.c.Get(ctx, h.resource) - if err != nil { - return nil, err - } - return &Policy{InternalProto: proto}, nil -} - -// SetPolicy replaces the resource's current policy with the supplied Policy. -// -// If policy was created from a prior call to Get, then the modification will -// only succeed if the policy has not changed since the Get. -func (h *Handle) SetPolicy(ctx context.Context, policy *Policy) error { - return h.c.Set(ctx, h.resource, policy.InternalProto) -} - -// TestPermissions returns the subset of permissions that the caller has on the resource. -func (h *Handle) TestPermissions(ctx context.Context, permissions []string) ([]string, error) { - return h.c.Test(ctx, h.resource, permissions) -} - -// A RoleName is a name representing a collection of permissions. -type RoleName string - -// Common role names. -const ( - Owner RoleName = "roles/owner" - Editor RoleName = "roles/editor" - Viewer RoleName = "roles/viewer" -) - -const ( - // AllUsers is a special member that denotes all users, even unauthenticated ones. - AllUsers = "allUsers" - - // AllAuthenticatedUsers is a special member that denotes all authenticated users. - AllAuthenticatedUsers = "allAuthenticatedUsers" -) - -// A Policy is a list of Bindings representing roles -// granted to members. -// -// The zero Policy is a valid policy with no bindings. -type Policy struct { - // TODO(jba): when type aliases are available, put Policy into an internal package - // and provide an exported alias here. - - // This field is exported for use by the Google Cloud Libraries only. - // It may become unexported in a future release. - InternalProto *pb.Policy -} - -// Members returns the list of members with the supplied role. -// The return value should not be modified. Use Add and Remove -// to modify the members of a role. -func (p *Policy) Members(r RoleName) []string { - b := p.binding(r) - if b == nil { - return nil - } - return b.Members -} - -// HasRole reports whether member has role r. -func (p *Policy) HasRole(member string, r RoleName) bool { - return memberIndex(member, p.binding(r)) >= 0 -} - -// Add adds member member to role r if it is not already present. -// A new binding is created if there is no binding for the role. -func (p *Policy) Add(member string, r RoleName) { - b := p.binding(r) - if b == nil { - if p.InternalProto == nil { - p.InternalProto = &pb.Policy{} - } - p.InternalProto.Bindings = append(p.InternalProto.Bindings, &pb.Binding{ - Role: string(r), - Members: []string{member}, - }) - return - } - if memberIndex(member, b) < 0 { - b.Members = append(b.Members, member) - return - } -} - -// Remove removes member from role r if it is present. -func (p *Policy) Remove(member string, r RoleName) { - bi := p.bindingIndex(r) - if bi < 0 { - return - } - bindings := p.InternalProto.Bindings - b := bindings[bi] - mi := memberIndex(member, b) - if mi < 0 { - return - } - // Order doesn't matter for bindings or members, so to remove, move the last item - // into the removed spot and shrink the slice. - if len(b.Members) == 1 { - // Remove binding. - last := len(bindings) - 1 - bindings[bi] = bindings[last] - bindings[last] = nil - p.InternalProto.Bindings = bindings[:last] - return - } - // Remove member. - // TODO(jba): worry about multiple copies of m? - last := len(b.Members) - 1 - b.Members[mi] = b.Members[last] - b.Members[last] = "" - b.Members = b.Members[:last] -} - -// Roles returns the names of all the roles that appear in the Policy. -func (p *Policy) Roles() []RoleName { - if p.InternalProto == nil { - return nil - } - var rns []RoleName - for _, b := range p.InternalProto.Bindings { - rns = append(rns, RoleName(b.Role)) - } - return rns -} - -// binding returns the Binding for the suppied role, or nil if there isn't one. -func (p *Policy) binding(r RoleName) *pb.Binding { - i := p.bindingIndex(r) - if i < 0 { - return nil - } - return p.InternalProto.Bindings[i] -} - -func (p *Policy) bindingIndex(r RoleName) int { - if p.InternalProto == nil { - return -1 - } - for i, b := range p.InternalProto.Bindings { - if b.Role == string(r) { - return i - } - } - return -1 -} - -// memberIndex returns the index of m in b's Members, or -1 if not found. -func memberIndex(m string, b *pb.Binding) int { - if b == nil { - return -1 - } - for i, mm := range b.Members { - if mm == m { - return i - } - } - return -1 -} diff --git a/vendor/cloud.google.com/go/internal/optional/optional.go b/vendor/cloud.google.com/go/internal/optional/optional.go deleted file mode 100644 index 4c15410aa04..00000000000 --- a/vendor/cloud.google.com/go/internal/optional/optional.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2016 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 optional provides versions of primitive types that can -// be nil. These are useful in methods that update some of an API object's -// fields. -package optional - -import ( - "fmt" - "strings" - "time" -) - -type ( - // Bool is either a bool or nil. - Bool interface{} - - // String is either a string or nil. - String interface{} - - // Int is either an int or nil. - Int interface{} - - // Uint is either a uint or nil. - Uint interface{} - - // Float64 is either a float64 or nil. - Float64 interface{} - - // Duration is either a time.Duration or nil. - Duration interface{} -) - -// ToBool returns its argument as a bool. -// It panics if its argument is nil or not a bool. -func ToBool(v Bool) bool { - x, ok := v.(bool) - if !ok { - doPanic("Bool", v) - } - return x -} - -// ToString returns its argument as a string. -// It panics if its argument is nil or not a string. -func ToString(v String) string { - x, ok := v.(string) - if !ok { - doPanic("String", v) - } - return x -} - -// ToInt returns its argument as an int. -// It panics if its argument is nil or not an int. -func ToInt(v Int) int { - x, ok := v.(int) - if !ok { - doPanic("Int", v) - } - return x -} - -// ToUint returns its argument as a uint. -// It panics if its argument is nil or not a uint. -func ToUint(v Uint) uint { - x, ok := v.(uint) - if !ok { - doPanic("Uint", v) - } - return x -} - -// ToFloat64 returns its argument as a float64. -// It panics if its argument is nil or not a float64. -func ToFloat64(v Float64) float64 { - x, ok := v.(float64) - if !ok { - doPanic("Float64", v) - } - return x -} - -// ToDuration returns its argument as a time.Duration. -// It panics if its argument is nil or not a time.Duration. -func ToDuration(v Duration) time.Duration { - x, ok := v.(time.Duration) - if !ok { - doPanic("Duration", v) - } - return x -} - -func doPanic(capType string, v interface{}) { - panic(fmt.Sprintf("optional.%s value should be %s, got %T", capType, strings.ToLower(capType), v)) -} diff --git a/vendor/cloud.google.com/go/internal/version/version.go b/vendor/cloud.google.com/go/internal/version/version.go deleted file mode 100644 index f5c23a564c6..00000000000 --- a/vendor/cloud.google.com/go/internal/version/version.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2016 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. - -//go:generate ./update_version.sh - -// Package version contains version information for Google Cloud Client -// Libraries for Go, as reported in request headers. -package version - -import ( - "runtime" - "strings" - "unicode" -) - -// Repo is the current version of the client libraries in this -// repo. It should be a date in YYYYMMDD format. -const Repo = "20180226" - -// Go returns the Go runtime version. The returned string -// has no whitespace. -func Go() string { - return goVersion -} - -var goVersion = goVer(runtime.Version()) - -const develPrefix = "devel +" - -func goVer(s string) string { - if strings.HasPrefix(s, develPrefix) { - s = s[len(develPrefix):] - if p := strings.IndexFunc(s, unicode.IsSpace); p >= 0 { - s = s[:p] - } - return s - } - - if strings.HasPrefix(s, "go1") { - s = s[2:] - var prerelease string - if p := strings.IndexFunc(s, notSemverRune); p >= 0 { - s, prerelease = s[:p], s[p:] - } - if strings.HasSuffix(s, ".") { - s += "0" - } else if strings.Count(s, ".") < 2 { - s += ".0" - } - if prerelease != "" { - s += "-" + prerelease - } - return s - } - return "" -} - -func notSemverRune(r rune) bool { - return strings.IndexRune("0123456789.", r) < 0 -} diff --git a/vendor/cloud.google.com/go/pubsub/apiv1/doc.go b/vendor/cloud.google.com/go/pubsub/apiv1/doc.go deleted file mode 100644 index ccdce27d66b..00000000000 --- a/vendor/cloud.google.com/go/pubsub/apiv1/doc.go +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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 -// -// https://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. - -// AUTO-GENERATED CODE. DO NOT EDIT. - -// Package pubsub is an auto-generated package for the -// Google Cloud Pub/Sub API. -// -// NOTE: This package is in alpha. It is not stable, and is likely to change. -// -// Provides reliable, many-to-many, asynchronous messaging between -// applications. -// -// Use the client at cloud.google.com/go/pubsub in preference to this. -package pubsub // import "cloud.google.com/go/pubsub/apiv1" - -import ( - "golang.org/x/net/context" - "google.golang.org/grpc/metadata" -) - -func insertMetadata(ctx context.Context, mds ...metadata.MD) context.Context { - out, _ := metadata.FromOutgoingContext(ctx) - out = out.Copy() - for _, md := range mds { - for k, v := range md { - out[k] = append(out[k], v...) - } - } - return metadata.NewOutgoingContext(ctx, out) -} - -// DefaultAuthScopes reports the default set of authentication scopes to use with this package. -func DefaultAuthScopes() []string { - return []string{ - "https://www.googleapis.com/auth/cloud-platform", - "https://www.googleapis.com/auth/pubsub", - } -} diff --git a/vendor/cloud.google.com/go/pubsub/apiv1/path_funcs.go b/vendor/cloud.google.com/go/pubsub/apiv1/path_funcs.go deleted file mode 100644 index b9ab4848db1..00000000000 --- a/vendor/cloud.google.com/go/pubsub/apiv1/path_funcs.go +++ /dev/null @@ -1,95 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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 -// -// https://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 pubsub - -// PublisherProjectPath returns the path for the project resource. -// -// Deprecated: Use -// fmt.Sprintf("projects/%s", project) -// instead. -func PublisherProjectPath(project string) string { - return "" + - "projects/" + - project + - "" -} - -// PublisherTopicPath returns the path for the topic resource. -// -// Deprecated: Use -// fmt.Sprintf("projects/%s/topics/%s", project, topic) -// instead. -func PublisherTopicPath(project, topic string) string { - return "" + - "projects/" + - project + - "/topics/" + - topic + - "" -} - -// SubscriberProjectPath returns the path for the project resource. -// -// Deprecated: Use -// fmt.Sprintf("projects/%s", project) -// instead. -func SubscriberProjectPath(project string) string { - return "" + - "projects/" + - project + - "" -} - -// SubscriberSnapshotPath returns the path for the snapshot resource. -// -// Deprecated: Use -// fmt.Sprintf("projects/%s/snapshots/%s", project, snapshot) -// instead. -func SubscriberSnapshotPath(project, snapshot string) string { - return "" + - "projects/" + - project + - "/snapshots/" + - snapshot + - "" -} - -// SubscriberSubscriptionPath returns the path for the subscription resource. -// -// Deprecated: Use -// fmt.Sprintf("projects/%s/subscriptions/%s", project, subscription) -// instead. -func SubscriberSubscriptionPath(project, subscription string) string { - return "" + - "projects/" + - project + - "/subscriptions/" + - subscription + - "" -} - -// SubscriberTopicPath returns the path for the topic resource. -// -// Deprecated: Use -// fmt.Sprintf("projects/%s/topics/%s", project, topic) -// instead. -func SubscriberTopicPath(project, topic string) string { - return "" + - "projects/" + - project + - "/topics/" + - topic + - "" -} diff --git a/vendor/cloud.google.com/go/pubsub/apiv1/publisher_client.go b/vendor/cloud.google.com/go/pubsub/apiv1/publisher_client.go deleted file mode 100644 index 6e9b1faee1a..00000000000 --- a/vendor/cloud.google.com/go/pubsub/apiv1/publisher_client.go +++ /dev/null @@ -1,398 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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 -// -// https://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. - -// AUTO-GENERATED CODE. DO NOT EDIT. - -package pubsub - -import ( - "math" - "time" - - "cloud.google.com/go/iam" - "cloud.google.com/go/internal/version" - gax "github.com/googleapis/gax-go" - "golang.org/x/net/context" - "google.golang.org/api/iterator" - "google.golang.org/api/option" - "google.golang.org/api/transport" - pubsubpb "google.golang.org/genproto/googleapis/pubsub/v1" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" -) - -// PublisherCallOptions contains the retry settings for each method of PublisherClient. -type PublisherCallOptions struct { - CreateTopic []gax.CallOption - UpdateTopic []gax.CallOption - Publish []gax.CallOption - GetTopic []gax.CallOption - ListTopics []gax.CallOption - ListTopicSubscriptions []gax.CallOption - DeleteTopic []gax.CallOption -} - -func defaultPublisherClientOptions() []option.ClientOption { - return []option.ClientOption{ - option.WithEndpoint("pubsub.googleapis.com:443"), - option.WithScopes(DefaultAuthScopes()...), - } -} - -func defaultPublisherCallOptions() *PublisherCallOptions { - retry := map[[2]string][]gax.CallOption{ - {"default", "idempotent"}: { - gax.WithRetry(func() gax.Retryer { - return gax.OnCodes([]codes.Code{ - codes.DeadlineExceeded, - codes.Unavailable, - }, gax.Backoff{ - Initial: 100 * time.Millisecond, - Max: 60000 * time.Millisecond, - Multiplier: 1.3, - }) - }), - }, - {"messaging", "one_plus_delivery"}: { - gax.WithRetry(func() gax.Retryer { - return gax.OnCodes([]codes.Code{ - codes.Aborted, - codes.Canceled, - codes.DeadlineExceeded, - codes.Internal, - codes.ResourceExhausted, - codes.Unavailable, - codes.Unknown, - }, gax.Backoff{ - Initial: 100 * time.Millisecond, - Max: 60000 * time.Millisecond, - Multiplier: 1.3, - }) - }), - }, - } - return &PublisherCallOptions{ - CreateTopic: retry[[2]string{"default", "idempotent"}], - UpdateTopic: retry[[2]string{"default", "idempotent"}], - Publish: retry[[2]string{"messaging", "one_plus_delivery"}], - GetTopic: retry[[2]string{"default", "idempotent"}], - ListTopics: retry[[2]string{"default", "idempotent"}], - ListTopicSubscriptions: retry[[2]string{"default", "idempotent"}], - DeleteTopic: retry[[2]string{"default", "idempotent"}], - } -} - -// PublisherClient is a client for interacting with Google Cloud Pub/Sub API. -type PublisherClient struct { - // The connection to the service. - conn *grpc.ClientConn - - // The gRPC API client. - publisherClient pubsubpb.PublisherClient - - // The call options for this service. - CallOptions *PublisherCallOptions - - // The x-goog-* metadata to be sent with each request. - xGoogMetadata metadata.MD -} - -// NewPublisherClient creates a new publisher client. -// -// The service that an application uses to manipulate topics, and to send -// messages to a topic. -func NewPublisherClient(ctx context.Context, opts ...option.ClientOption) (*PublisherClient, error) { - conn, err := transport.DialGRPC(ctx, append(defaultPublisherClientOptions(), opts...)...) - if err != nil { - return nil, err - } - c := &PublisherClient{ - conn: conn, - CallOptions: defaultPublisherCallOptions(), - - publisherClient: pubsubpb.NewPublisherClient(conn), - } - c.SetGoogleClientInfo() - return c, nil -} - -// Connection returns the client's connection to the API service. -func (c *PublisherClient) Connection() *grpc.ClientConn { - return c.conn -} - -// Close closes the connection to the API service. The user should invoke this when -// the client is no longer required. -func (c *PublisherClient) Close() error { - return c.conn.Close() -} - -// SetGoogleClientInfo sets the name and version of the application in -// the `x-goog-api-client` header passed on each request. Intended for -// use by Google-written clients. -func (c *PublisherClient) SetGoogleClientInfo(keyval ...string) { - kv := append([]string{"gl-go", version.Go()}, keyval...) - kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) - c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) -} - -func (c *PublisherClient) SubscriptionIAM(subscription *pubsubpb.Subscription) *iam.Handle { - return iam.InternalNewHandle(c.Connection(), subscription.Name) -} - -func (c *PublisherClient) TopicIAM(topic *pubsubpb.Topic) *iam.Handle { - return iam.InternalNewHandle(c.Connection(), topic.Name) -} - -// CreateTopic creates the given topic with the given name. -func (c *PublisherClient) CreateTopic(ctx context.Context, req *pubsubpb.Topic, opts ...gax.CallOption) (*pubsubpb.Topic, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.CreateTopic[0:len(c.CallOptions.CreateTopic):len(c.CallOptions.CreateTopic)], opts...) - var resp *pubsubpb.Topic - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.publisherClient.CreateTopic(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return resp, nil -} - -// UpdateTopic updates an existing topic. Note that certain properties of a topic are not -// modifiable. Options settings follow the style guide: -// NOTE: The style guide requires body: "topic" instead of body: "*". -// Keeping the latter for internal consistency in V1, however it should be -// corrected in V2. See -// https://cloud.google.com/apis/design/standard_methods#update for details. -func (c *PublisherClient) UpdateTopic(ctx context.Context, req *pubsubpb.UpdateTopicRequest, opts ...gax.CallOption) (*pubsubpb.Topic, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.UpdateTopic[0:len(c.CallOptions.UpdateTopic):len(c.CallOptions.UpdateTopic)], opts...) - var resp *pubsubpb.Topic - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.publisherClient.UpdateTopic(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return resp, nil -} - -// Publish adds one or more messages to the topic. Returns NOT_FOUND if the topic -// does not exist. The message payload must not be empty; it must contain -// either a non-empty data field, or at least one attribute. -func (c *PublisherClient) Publish(ctx context.Context, req *pubsubpb.PublishRequest, opts ...gax.CallOption) (*pubsubpb.PublishResponse, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.Publish[0:len(c.CallOptions.Publish):len(c.CallOptions.Publish)], opts...) - var resp *pubsubpb.PublishResponse - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.publisherClient.Publish(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return resp, nil -} - -// GetTopic gets the configuration of a topic. -func (c *PublisherClient) GetTopic(ctx context.Context, req *pubsubpb.GetTopicRequest, opts ...gax.CallOption) (*pubsubpb.Topic, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.GetTopic[0:len(c.CallOptions.GetTopic):len(c.CallOptions.GetTopic)], opts...) - var resp *pubsubpb.Topic - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.publisherClient.GetTopic(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return resp, nil -} - -// ListTopics lists matching topics. -func (c *PublisherClient) ListTopics(ctx context.Context, req *pubsubpb.ListTopicsRequest, opts ...gax.CallOption) *TopicIterator { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.ListTopics[0:len(c.CallOptions.ListTopics):len(c.CallOptions.ListTopics)], opts...) - it := &TopicIterator{} - it.InternalFetch = func(pageSize int, pageToken string) ([]*pubsubpb.Topic, string, error) { - var resp *pubsubpb.ListTopicsResponse - req.PageToken = pageToken - if pageSize > math.MaxInt32 { - req.PageSize = math.MaxInt32 - } else { - req.PageSize = int32(pageSize) - } - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.publisherClient.ListTopics(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, "", err - } - return resp.Topics, resp.NextPageToken, nil - } - fetch := func(pageSize int, pageToken string) (string, error) { - items, nextPageToken, err := it.InternalFetch(pageSize, pageToken) - if err != nil { - return "", err - } - it.items = append(it.items, items...) - return nextPageToken, nil - } - it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) - return it -} - -// ListTopicSubscriptions lists the name of the subscriptions for this topic. -func (c *PublisherClient) ListTopicSubscriptions(ctx context.Context, req *pubsubpb.ListTopicSubscriptionsRequest, opts ...gax.CallOption) *StringIterator { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.ListTopicSubscriptions[0:len(c.CallOptions.ListTopicSubscriptions):len(c.CallOptions.ListTopicSubscriptions)], opts...) - it := &StringIterator{} - it.InternalFetch = func(pageSize int, pageToken string) ([]string, string, error) { - var resp *pubsubpb.ListTopicSubscriptionsResponse - req.PageToken = pageToken - if pageSize > math.MaxInt32 { - req.PageSize = math.MaxInt32 - } else { - req.PageSize = int32(pageSize) - } - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.publisherClient.ListTopicSubscriptions(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, "", err - } - return resp.Subscriptions, resp.NextPageToken, nil - } - fetch := func(pageSize int, pageToken string) (string, error) { - items, nextPageToken, err := it.InternalFetch(pageSize, pageToken) - if err != nil { - return "", err - } - it.items = append(it.items, items...) - return nextPageToken, nil - } - it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) - return it -} - -// DeleteTopic deletes the topic with the given name. Returns NOT_FOUND if the topic -// does not exist. After a topic is deleted, a new topic may be created with -// the same name; this is an entirely new topic with none of the old -// configuration or subscriptions. Existing subscriptions to this topic are -// not deleted, but their topic field is set to _deleted-topic_. -func (c *PublisherClient) DeleteTopic(ctx context.Context, req *pubsubpb.DeleteTopicRequest, opts ...gax.CallOption) error { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.DeleteTopic[0:len(c.CallOptions.DeleteTopic):len(c.CallOptions.DeleteTopic)], opts...) - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - _, err = c.publisherClient.DeleteTopic(ctx, req, settings.GRPC...) - return err - }, opts...) - return err -} - -// StringIterator manages a stream of string. -type StringIterator struct { - items []string - pageInfo *iterator.PageInfo - nextFunc func() error - - // InternalFetch is for use by the Google Cloud Libraries only. - // It is not part of the stable interface of this package. - // - // InternalFetch returns results from a single call to the underlying RPC. - // The number of results is no greater than pageSize. - // If there are no more results, nextPageToken is empty and err is nil. - InternalFetch func(pageSize int, pageToken string) (results []string, nextPageToken string, err error) -} - -// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. -func (it *StringIterator) PageInfo() *iterator.PageInfo { - return it.pageInfo -} - -// Next returns the next result. Its second return value is iterator.Done if there are no more -// results. Once Next returns Done, all subsequent calls will return Done. -func (it *StringIterator) Next() (string, error) { - var item string - if err := it.nextFunc(); err != nil { - return item, err - } - item = it.items[0] - it.items = it.items[1:] - return item, nil -} - -func (it *StringIterator) bufLen() int { - return len(it.items) -} - -func (it *StringIterator) takeBuf() interface{} { - b := it.items - it.items = nil - return b -} - -// TopicIterator manages a stream of *pubsubpb.Topic. -type TopicIterator struct { - items []*pubsubpb.Topic - pageInfo *iterator.PageInfo - nextFunc func() error - - // InternalFetch is for use by the Google Cloud Libraries only. - // It is not part of the stable interface of this package. - // - // InternalFetch returns results from a single call to the underlying RPC. - // The number of results is no greater than pageSize. - // If there are no more results, nextPageToken is empty and err is nil. - InternalFetch func(pageSize int, pageToken string) (results []*pubsubpb.Topic, nextPageToken string, err error) -} - -// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. -func (it *TopicIterator) PageInfo() *iterator.PageInfo { - return it.pageInfo -} - -// Next returns the next result. Its second return value is iterator.Done if there are no more -// results. Once Next returns Done, all subsequent calls will return Done. -func (it *TopicIterator) Next() (*pubsubpb.Topic, error) { - var item *pubsubpb.Topic - if err := it.nextFunc(); err != nil { - return item, err - } - item = it.items[0] - it.items = it.items[1:] - return item, nil -} - -func (it *TopicIterator) bufLen() int { - return len(it.items) -} - -func (it *TopicIterator) takeBuf() interface{} { - b := it.items - it.items = nil - return b -} diff --git a/vendor/cloud.google.com/go/pubsub/apiv1/subscriber_client.go b/vendor/cloud.google.com/go/pubsub/apiv1/subscriber_client.go deleted file mode 100644 index a2266a3a4dc..00000000000 --- a/vendor/cloud.google.com/go/pubsub/apiv1/subscriber_client.go +++ /dev/null @@ -1,593 +0,0 @@ -// Copyright 2018 Google LLC -// -// 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 -// -// https://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. - -// AUTO-GENERATED CODE. DO NOT EDIT. - -package pubsub - -import ( - "math" - "time" - - "cloud.google.com/go/iam" - "cloud.google.com/go/internal/version" - gax "github.com/googleapis/gax-go" - "golang.org/x/net/context" - "google.golang.org/api/iterator" - "google.golang.org/api/option" - "google.golang.org/api/transport" - pubsubpb "google.golang.org/genproto/googleapis/pubsub/v1" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/metadata" -) - -// SubscriberCallOptions contains the retry settings for each method of SubscriberClient. -type SubscriberCallOptions struct { - CreateSubscription []gax.CallOption - GetSubscription []gax.CallOption - UpdateSubscription []gax.CallOption - ListSubscriptions []gax.CallOption - DeleteSubscription []gax.CallOption - ModifyAckDeadline []gax.CallOption - Acknowledge []gax.CallOption - Pull []gax.CallOption - StreamingPull []gax.CallOption - ModifyPushConfig []gax.CallOption - ListSnapshots []gax.CallOption - CreateSnapshot []gax.CallOption - UpdateSnapshot []gax.CallOption - DeleteSnapshot []gax.CallOption - Seek []gax.CallOption -} - -func defaultSubscriberClientOptions() []option.ClientOption { - return []option.ClientOption{ - option.WithEndpoint("pubsub.googleapis.com:443"), - option.WithScopes(DefaultAuthScopes()...), - } -} - -func defaultSubscriberCallOptions() *SubscriberCallOptions { - retry := map[[2]string][]gax.CallOption{ - {"default", "idempotent"}: { - gax.WithRetry(func() gax.Retryer { - return gax.OnCodes([]codes.Code{ - codes.DeadlineExceeded, - codes.Unavailable, - }, gax.Backoff{ - Initial: 100 * time.Millisecond, - Max: 60000 * time.Millisecond, - Multiplier: 1.3, - }) - }), - }, - {"messaging", "pull"}: { - gax.WithRetry(func() gax.Retryer { - return gax.OnCodes([]codes.Code{ - codes.Canceled, - codes.DeadlineExceeded, - codes.Internal, - codes.ResourceExhausted, - codes.Unavailable, - }, gax.Backoff{ - Initial: 100 * time.Millisecond, - Max: 60000 * time.Millisecond, - Multiplier: 1.3, - }) - }), - }, - {"streaming_messaging", "pull"}: { - gax.WithRetry(func() gax.Retryer { - return gax.OnCodes([]codes.Code{ - codes.Canceled, - codes.DeadlineExceeded, - codes.Internal, - codes.ResourceExhausted, - codes.Unavailable, - }, gax.Backoff{ - Initial: 100 * time.Millisecond, - Max: 60000 * time.Millisecond, - Multiplier: 1.3, - }) - }), - }, - } - return &SubscriberCallOptions{ - CreateSubscription: retry[[2]string{"default", "idempotent"}], - GetSubscription: retry[[2]string{"default", "idempotent"}], - UpdateSubscription: retry[[2]string{"default", "idempotent"}], - ListSubscriptions: retry[[2]string{"default", "idempotent"}], - DeleteSubscription: retry[[2]string{"default", "idempotent"}], - ModifyAckDeadline: retry[[2]string{"default", "non_idempotent"}], - Acknowledge: retry[[2]string{"messaging", "non_idempotent"}], - Pull: retry[[2]string{"messaging", "pull"}], - StreamingPull: retry[[2]string{"streaming_messaging", "pull"}], - ModifyPushConfig: retry[[2]string{"default", "non_idempotent"}], - ListSnapshots: retry[[2]string{"default", "idempotent"}], - CreateSnapshot: retry[[2]string{"default", "idempotent"}], - UpdateSnapshot: retry[[2]string{"default", "idempotent"}], - DeleteSnapshot: retry[[2]string{"default", "idempotent"}], - Seek: retry[[2]string{"default", "non_idempotent"}], - } -} - -// SubscriberClient is a client for interacting with Google Cloud Pub/Sub API. -type SubscriberClient struct { - // The connection to the service. - conn *grpc.ClientConn - - // The gRPC API client. - subscriberClient pubsubpb.SubscriberClient - - // The call options for this service. - CallOptions *SubscriberCallOptions - - // The x-goog-* metadata to be sent with each request. - xGoogMetadata metadata.MD -} - -// NewSubscriberClient creates a new subscriber client. -// -// The service that an application uses to manipulate subscriptions and to -// consume messages from a subscription via the Pull method. -func NewSubscriberClient(ctx context.Context, opts ...option.ClientOption) (*SubscriberClient, error) { - conn, err := transport.DialGRPC(ctx, append(defaultSubscriberClientOptions(), opts...)...) - if err != nil { - return nil, err - } - c := &SubscriberClient{ - conn: conn, - CallOptions: defaultSubscriberCallOptions(), - - subscriberClient: pubsubpb.NewSubscriberClient(conn), - } - c.SetGoogleClientInfo() - return c, nil -} - -// Connection returns the client's connection to the API service. -func (c *SubscriberClient) Connection() *grpc.ClientConn { - return c.conn -} - -// Close closes the connection to the API service. The user should invoke this when -// the client is no longer required. -func (c *SubscriberClient) Close() error { - return c.conn.Close() -} - -// SetGoogleClientInfo sets the name and version of the application in -// the `x-goog-api-client` header passed on each request. Intended for -// use by Google-written clients. -func (c *SubscriberClient) SetGoogleClientInfo(keyval ...string) { - kv := append([]string{"gl-go", version.Go()}, keyval...) - kv = append(kv, "gapic", version.Repo, "gax", gax.Version, "grpc", grpc.Version) - c.xGoogMetadata = metadata.Pairs("x-goog-api-client", gax.XGoogHeader(kv...)) -} - -func (c *SubscriberClient) SubscriptionIAM(subscription *pubsubpb.Subscription) *iam.Handle { - return iam.InternalNewHandle(c.Connection(), subscription.Name) -} - -func (c *SubscriberClient) TopicIAM(topic *pubsubpb.Topic) *iam.Handle { - return iam.InternalNewHandle(c.Connection(), topic.Name) -} - -// CreateSubscription creates a subscription to a given topic. -// If the subscription already exists, returns ALREADY_EXISTS. -// If the corresponding topic doesn't exist, returns NOT_FOUND. -// -// If the name is not provided in the request, the server will assign a random -// name for this subscription on the same project as the topic, conforming -// to the -// resource name format (at https://cloud.google.com/pubsub/docs/overview#names). -// The generated name is populated in the returned Subscription object. -// Note that for REST API requests, you must specify a name in the request. -func (c *SubscriberClient) CreateSubscription(ctx context.Context, req *pubsubpb.Subscription, opts ...gax.CallOption) (*pubsubpb.Subscription, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.CreateSubscription[0:len(c.CallOptions.CreateSubscription):len(c.CallOptions.CreateSubscription)], opts...) - var resp *pubsubpb.Subscription - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.subscriberClient.CreateSubscription(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return resp, nil -} - -// GetSubscription gets the configuration details of a subscription. -func (c *SubscriberClient) GetSubscription(ctx context.Context, req *pubsubpb.GetSubscriptionRequest, opts ...gax.CallOption) (*pubsubpb.Subscription, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.GetSubscription[0:len(c.CallOptions.GetSubscription):len(c.CallOptions.GetSubscription)], opts...) - var resp *pubsubpb.Subscription - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.subscriberClient.GetSubscription(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return resp, nil -} - -// UpdateSubscription updates an existing subscription. Note that certain properties of a -// subscription, such as its topic, are not modifiable. -// NOTE: The style guide requires body: "subscription" instead of body: "*". -// Keeping the latter for internal consistency in V1, however it should be -// corrected in V2. See -// https://cloud.google.com/apis/design/standard_methods#update for details. -func (c *SubscriberClient) UpdateSubscription(ctx context.Context, req *pubsubpb.UpdateSubscriptionRequest, opts ...gax.CallOption) (*pubsubpb.Subscription, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.UpdateSubscription[0:len(c.CallOptions.UpdateSubscription):len(c.CallOptions.UpdateSubscription)], opts...) - var resp *pubsubpb.Subscription - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.subscriberClient.UpdateSubscription(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return resp, nil -} - -// ListSubscriptions lists matching subscriptions. -func (c *SubscriberClient) ListSubscriptions(ctx context.Context, req *pubsubpb.ListSubscriptionsRequest, opts ...gax.CallOption) *SubscriptionIterator { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.ListSubscriptions[0:len(c.CallOptions.ListSubscriptions):len(c.CallOptions.ListSubscriptions)], opts...) - it := &SubscriptionIterator{} - it.InternalFetch = func(pageSize int, pageToken string) ([]*pubsubpb.Subscription, string, error) { - var resp *pubsubpb.ListSubscriptionsResponse - req.PageToken = pageToken - if pageSize > math.MaxInt32 { - req.PageSize = math.MaxInt32 - } else { - req.PageSize = int32(pageSize) - } - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.subscriberClient.ListSubscriptions(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, "", err - } - return resp.Subscriptions, resp.NextPageToken, nil - } - fetch := func(pageSize int, pageToken string) (string, error) { - items, nextPageToken, err := it.InternalFetch(pageSize, pageToken) - if err != nil { - return "", err - } - it.items = append(it.items, items...) - return nextPageToken, nil - } - it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) - return it -} - -// DeleteSubscription deletes an existing subscription. All messages retained in the subscription -// are immediately dropped. Calls to Pull after deletion will return -// NOT_FOUND. After a subscription is deleted, a new one may be created with -// the same name, but the new one has no association with the old -// subscription or its topic unless the same topic is specified. -func (c *SubscriberClient) DeleteSubscription(ctx context.Context, req *pubsubpb.DeleteSubscriptionRequest, opts ...gax.CallOption) error { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.DeleteSubscription[0:len(c.CallOptions.DeleteSubscription):len(c.CallOptions.DeleteSubscription)], opts...) - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - _, err = c.subscriberClient.DeleteSubscription(ctx, req, settings.GRPC...) - return err - }, opts...) - return err -} - -// ModifyAckDeadline modifies the ack deadline for a specific message. This method is useful -// to indicate that more time is needed to process a message by the -// subscriber, or to make the message available for redelivery if the -// processing was interrupted. Note that this does not modify the -// subscription-level ackDeadlineSeconds used for subsequent messages. -func (c *SubscriberClient) ModifyAckDeadline(ctx context.Context, req *pubsubpb.ModifyAckDeadlineRequest, opts ...gax.CallOption) error { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.ModifyAckDeadline[0:len(c.CallOptions.ModifyAckDeadline):len(c.CallOptions.ModifyAckDeadline)], opts...) - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - _, err = c.subscriberClient.ModifyAckDeadline(ctx, req, settings.GRPC...) - return err - }, opts...) - return err -} - -// Acknowledge acknowledges the messages associated with the ack_ids in the -// AcknowledgeRequest. The Pub/Sub system can remove the relevant messages -// from the subscription. -// -// Acknowledging a message whose ack deadline has expired may succeed, -// but such a message may be redelivered later. Acknowledging a message more -// than once will not result in an error. -func (c *SubscriberClient) Acknowledge(ctx context.Context, req *pubsubpb.AcknowledgeRequest, opts ...gax.CallOption) error { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.Acknowledge[0:len(c.CallOptions.Acknowledge):len(c.CallOptions.Acknowledge)], opts...) - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - _, err = c.subscriberClient.Acknowledge(ctx, req, settings.GRPC...) - return err - }, opts...) - return err -} - -// Pull pulls messages from the server. Returns an empty list if there are no -// messages available in the backlog. The server may return UNAVAILABLE if -// there are too many concurrent pull requests pending for the given -// subscription. -func (c *SubscriberClient) Pull(ctx context.Context, req *pubsubpb.PullRequest, opts ...gax.CallOption) (*pubsubpb.PullResponse, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.Pull[0:len(c.CallOptions.Pull):len(c.CallOptions.Pull)], opts...) - var resp *pubsubpb.PullResponse - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.subscriberClient.Pull(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return resp, nil -} - -// StreamingPull (EXPERIMENTAL) StreamingPull is an experimental feature. This RPC will -// respond with UNIMPLEMENTED errors unless you have been invited to test -// this feature. Contact cloud-pubsub@google.com with any questions. -// -// Establishes a stream with the server, which sends messages down to the -// client. The client streams acknowledgements and ack deadline modifications -// back to the server. The server will close the stream and return the status -// on any error. The server may close the stream with status OK to reassign -// server-side resources, in which case, the client should re-establish the -// stream. UNAVAILABLE may also be returned in the case of a transient error -// (e.g., a server restart). These should also be retried by the client. Flow -// control can be achieved by configuring the underlying RPC channel. -func (c *SubscriberClient) StreamingPull(ctx context.Context, opts ...gax.CallOption) (pubsubpb.Subscriber_StreamingPullClient, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.StreamingPull[0:len(c.CallOptions.StreamingPull):len(c.CallOptions.StreamingPull)], opts...) - var resp pubsubpb.Subscriber_StreamingPullClient - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.subscriberClient.StreamingPull(ctx, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return resp, nil -} - -// ModifyPushConfig modifies the PushConfig for a specified subscription. -// -// This may be used to change a push subscription to a pull one (signified by -// an empty PushConfig) or vice versa, or change the endpoint URL and other -// attributes of a push subscription. Messages will accumulate for delivery -// continuously through the call regardless of changes to the PushConfig. -func (c *SubscriberClient) ModifyPushConfig(ctx context.Context, req *pubsubpb.ModifyPushConfigRequest, opts ...gax.CallOption) error { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.ModifyPushConfig[0:len(c.CallOptions.ModifyPushConfig):len(c.CallOptions.ModifyPushConfig)], opts...) - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - _, err = c.subscriberClient.ModifyPushConfig(ctx, req, settings.GRPC...) - return err - }, opts...) - return err -} - -// ListSnapshots lists the existing snapshots. -func (c *SubscriberClient) ListSnapshots(ctx context.Context, req *pubsubpb.ListSnapshotsRequest, opts ...gax.CallOption) *SnapshotIterator { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.ListSnapshots[0:len(c.CallOptions.ListSnapshots):len(c.CallOptions.ListSnapshots)], opts...) - it := &SnapshotIterator{} - it.InternalFetch = func(pageSize int, pageToken string) ([]*pubsubpb.Snapshot, string, error) { - var resp *pubsubpb.ListSnapshotsResponse - req.PageToken = pageToken - if pageSize > math.MaxInt32 { - req.PageSize = math.MaxInt32 - } else { - req.PageSize = int32(pageSize) - } - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.subscriberClient.ListSnapshots(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, "", err - } - return resp.Snapshots, resp.NextPageToken, nil - } - fetch := func(pageSize int, pageToken string) (string, error) { - items, nextPageToken, err := it.InternalFetch(pageSize, pageToken) - if err != nil { - return "", err - } - it.items = append(it.items, items...) - return nextPageToken, nil - } - it.pageInfo, it.nextFunc = iterator.NewPageInfo(fetch, it.bufLen, it.takeBuf) - return it -} - -// CreateSnapshot creates a snapshot from the requested subscription. -// If the snapshot already exists, returns ALREADY_EXISTS. -// If the requested subscription doesn't exist, returns NOT_FOUND. -// -// If the name is not provided in the request, the server will assign a random -// name for this snapshot on the same project as the subscription, conforming -// to the -// resource name format (at https://cloud.google.com/pubsub/docs/overview#names). -// The generated name is populated in the returned Snapshot object. -// Note that for REST API requests, you must specify a name in the request. -func (c *SubscriberClient) CreateSnapshot(ctx context.Context, req *pubsubpb.CreateSnapshotRequest, opts ...gax.CallOption) (*pubsubpb.Snapshot, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.CreateSnapshot[0:len(c.CallOptions.CreateSnapshot):len(c.CallOptions.CreateSnapshot)], opts...) - var resp *pubsubpb.Snapshot - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.subscriberClient.CreateSnapshot(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return resp, nil -} - -// UpdateSnapshot updates an existing snapshot. Note that certain properties of a snapshot -// are not modifiable. -// NOTE: The style guide requires body: "snapshot" instead of body: "*". -// Keeping the latter for internal consistency in V1, however it should be -// corrected in V2. See -// https://cloud.google.com/apis/design/standard_methods#update for details. -func (c *SubscriberClient) UpdateSnapshot(ctx context.Context, req *pubsubpb.UpdateSnapshotRequest, opts ...gax.CallOption) (*pubsubpb.Snapshot, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.UpdateSnapshot[0:len(c.CallOptions.UpdateSnapshot):len(c.CallOptions.UpdateSnapshot)], opts...) - var resp *pubsubpb.Snapshot - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.subscriberClient.UpdateSnapshot(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return resp, nil -} - -// DeleteSnapshot removes an existing snapshot. All messages retained in the snapshot -// are immediately dropped. After a snapshot is deleted, a new one may be -// created with the same name, but the new one has no association with the old -// snapshot or its subscription, unless the same subscription is specified. -func (c *SubscriberClient) DeleteSnapshot(ctx context.Context, req *pubsubpb.DeleteSnapshotRequest, opts ...gax.CallOption) error { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.DeleteSnapshot[0:len(c.CallOptions.DeleteSnapshot):len(c.CallOptions.DeleteSnapshot)], opts...) - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - _, err = c.subscriberClient.DeleteSnapshot(ctx, req, settings.GRPC...) - return err - }, opts...) - return err -} - -// Seek seeks an existing subscription to a point in time or to a given snapshot, -// whichever is provided in the request. -func (c *SubscriberClient) Seek(ctx context.Context, req *pubsubpb.SeekRequest, opts ...gax.CallOption) (*pubsubpb.SeekResponse, error) { - ctx = insertMetadata(ctx, c.xGoogMetadata) - opts = append(c.CallOptions.Seek[0:len(c.CallOptions.Seek):len(c.CallOptions.Seek)], opts...) - var resp *pubsubpb.SeekResponse - err := gax.Invoke(ctx, func(ctx context.Context, settings gax.CallSettings) error { - var err error - resp, err = c.subscriberClient.Seek(ctx, req, settings.GRPC...) - return err - }, opts...) - if err != nil { - return nil, err - } - return resp, nil -} - -// SnapshotIterator manages a stream of *pubsubpb.Snapshot. -type SnapshotIterator struct { - items []*pubsubpb.Snapshot - pageInfo *iterator.PageInfo - nextFunc func() error - - // InternalFetch is for use by the Google Cloud Libraries only. - // It is not part of the stable interface of this package. - // - // InternalFetch returns results from a single call to the underlying RPC. - // The number of results is no greater than pageSize. - // If there are no more results, nextPageToken is empty and err is nil. - InternalFetch func(pageSize int, pageToken string) (results []*pubsubpb.Snapshot, nextPageToken string, err error) -} - -// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. -func (it *SnapshotIterator) PageInfo() *iterator.PageInfo { - return it.pageInfo -} - -// Next returns the next result. Its second return value is iterator.Done if there are no more -// results. Once Next returns Done, all subsequent calls will return Done. -func (it *SnapshotIterator) Next() (*pubsubpb.Snapshot, error) { - var item *pubsubpb.Snapshot - if err := it.nextFunc(); err != nil { - return item, err - } - item = it.items[0] - it.items = it.items[1:] - return item, nil -} - -func (it *SnapshotIterator) bufLen() int { - return len(it.items) -} - -func (it *SnapshotIterator) takeBuf() interface{} { - b := it.items - it.items = nil - return b -} - -// SubscriptionIterator manages a stream of *pubsubpb.Subscription. -type SubscriptionIterator struct { - items []*pubsubpb.Subscription - pageInfo *iterator.PageInfo - nextFunc func() error - - // InternalFetch is for use by the Google Cloud Libraries only. - // It is not part of the stable interface of this package. - // - // InternalFetch returns results from a single call to the underlying RPC. - // The number of results is no greater than pageSize. - // If there are no more results, nextPageToken is empty and err is nil. - InternalFetch func(pageSize int, pageToken string) (results []*pubsubpb.Subscription, nextPageToken string, err error) -} - -// PageInfo supports pagination. See the google.golang.org/api/iterator package for details. -func (it *SubscriptionIterator) PageInfo() *iterator.PageInfo { - return it.pageInfo -} - -// Next returns the next result. Its second return value is iterator.Done if there are no more -// results. Once Next returns Done, all subsequent calls will return Done. -func (it *SubscriptionIterator) Next() (*pubsubpb.Subscription, error) { - var item *pubsubpb.Subscription - if err := it.nextFunc(); err != nil { - return item, err - } - item = it.items[0] - it.items = it.items[1:] - return item, nil -} - -func (it *SubscriptionIterator) bufLen() int { - return len(it.items) -} - -func (it *SubscriptionIterator) takeBuf() interface{} { - b := it.items - it.items = nil - return b -} diff --git a/vendor/cloud.google.com/go/pubsub/doc.go b/vendor/cloud.google.com/go/pubsub/doc.go deleted file mode 100644 index 848b410e2e3..00000000000 --- a/vendor/cloud.google.com/go/pubsub/doc.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright 2016 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 pubsub provides an easy way to publish and receive Google Cloud Pub/Sub -messages, hiding the the details of the underlying server RPCs. Google Cloud -Pub/Sub is a many-to-many, asynchronous messaging system that decouples senders -and receivers. - -Note: This package is in beta. Some backwards-incompatible changes may occur. - -More information about Google Cloud Pub/Sub is available at -https://cloud.google.com/pubsub/docs - -See https://godoc.org/cloud.google.com/go for authentication, timeouts, -connection pooling and similar aspects of this package. - - -Publishing - -Google Cloud Pub/Sub messages are published to topics. Topics may be created -using the pubsub package like so: - - topic, err := pubsubClient.CreateTopic(context.Background(), "topic-name") - -Messages may then be published to a topic: - - res := topic.Publish(ctx, &pubsub.Message{Data: []byte("payload")}) - -Publish queues the message for publishing and returns immediately. When enough -messages have accumulated, or enough time has elapsed, the batch of messages is -sent to the Pub/Sub service. - -Publish returns a PublishResult, which behaves like a future: its Get method -blocks until the message has been sent to the service. - -The first time you call Publish on a topic, goroutines are started in the -background. To clean up these goroutines, call Stop: - - topic.Stop() - -Receiving - -To receive messages published to a topic, clients create subscriptions -to the topic. There may be more than one subscription per topic; each message -that is published to the topic will be delivered to all of its subscriptions. - -Subsciptions may be created like so: - - sub, err := pubsubClient.CreateSubscription(context.Background(), "sub-name", - pubsub.SubscriptionConfig{Topic: topic}) - -Messages are then consumed from a subscription via callback. - - err := sub.Receive(context.Background(), func(ctx context.Context, m *Message) { - log.Printf("Got message: %s", m.Data) - m.Ack() - }) - if err != nil { - // Handle error. - } - -The callback is invoked concurrently by multiple goroutines, maximizing -throughput. To terminate a call to Receive, cancel its context. - -Once client code has processed the message, it must call Message.Ack, otherwise -the message will eventually be redelivered. As an optimization, if the client -cannot or doesn't want to process the message, it can call Message.Nack to -speed redelivery. For more information and configuration options, see -"Deadlines" below. - -Note: It is possible for Messages to be redelivered, even if Message.Ack has -been called. Client code must be robust to multiple deliveries of messages. - -Deadlines - -The default pubsub deadlines are suitable for most use cases, but may be -overridden. This section describes the tradeoffs that should be considered -when overriding the defaults. - -Behind the scenes, each message returned by the Pub/Sub server has an -associated lease, known as an "ACK deadline". -Unless a message is acknowledged within the ACK deadline, or the client requests that -the ACK deadline be extended, the message will become elegible for redelivery. -As a convenience, the pubsub package will automatically extend deadlines until -either: - * Message.Ack or Message.Nack is called, or - * the "MaxExtension" period elapses from the time the message is fetched from the server. - -The initial ACK deadline given to each messages defaults to 10 seconds, but may -be overridden during subscription creation. Selecting an ACK deadline is a -tradeoff between message redelivery latency and RPC volume. If the pubsub -package fails to acknowledge or extend a message (e.g. due to unexpected -termination of the process), a shorter ACK deadline will generally result in -faster message redelivery by the Pub/Sub system. However, a short ACK deadline -may also increase the number of deadline extension RPCs that the pubsub package -sends to the server. - -The default max extension period is DefaultReceiveSettings.MaxExtension, and can -be overridden by setting Subscription.ReceiveSettings.MaxExtension. Selecting a -max extension period is a tradeoff between the speed at which client code must -process messages, and the redelivery delay if messages fail to be acknowledged -(e.g. because client code neglects to do so). Using a large MaxExtension -increases the available time for client code to process messages. However, if -the client code neglects to call Message.Ack/Nack, a large MaxExtension will -increase the delay before the message is redelivered. - -Slow Message Processing - -For use cases where message processing exceeds 30 minutes, we recommend using -the base client in a pull model, since long-lived streams are periodically killed -by firewalls. See the example at https://godoc.org/cloud.google.com/go/pubsub/apiv1#example-SubscriberClient-Pull-LengthyClientProcessing -*/ -package pubsub // import "cloud.google.com/go/pubsub" diff --git a/vendor/cloud.google.com/go/pubsub/flow_controller.go b/vendor/cloud.google.com/go/pubsub/flow_controller.go deleted file mode 100644 index 0fd7bd6c83d..00000000000 --- a/vendor/cloud.google.com/go/pubsub/flow_controller.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright 2017 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 pubsub - -import ( - "golang.org/x/net/context" - "golang.org/x/sync/semaphore" -) - -// flowController implements flow control for Subscription.Receive. -type flowController struct { - maxSize int // max total size of messages - semCount, semSize *semaphore.Weighted // enforces max number and size of messages -} - -// newFlowController creates a new flowController that ensures no more than -// maxCount messages or maxSize bytes are outstanding at once. If maxCount or -// maxSize is < 1, then an unlimited number of messages or bytes is permitted, -// respectively. -func newFlowController(maxCount, maxSize int) *flowController { - fc := &flowController{ - maxSize: maxSize, - semCount: nil, - semSize: nil, - } - if maxCount > 0 { - fc.semCount = semaphore.NewWeighted(int64(maxCount)) - } - if maxSize > 0 { - fc.semSize = semaphore.NewWeighted(int64(maxSize)) - } - return fc -} - -// acquire blocks until one message of size bytes can proceed or ctx is done. -// It returns nil in the first case, or ctx.Err() in the second. -// -// acquire allows large messages to proceed by treating a size greater than maxSize -// as if it were equal to maxSize. -func (f *flowController) acquire(ctx context.Context, size int) error { - if f.semCount != nil { - if err := f.semCount.Acquire(ctx, 1); err != nil { - return err - } - } - if f.semSize != nil { - if err := f.semSize.Acquire(ctx, f.bound(size)); err != nil { - if f.semCount != nil { - f.semCount.Release(1) - } - return err - } - } - return nil -} - -// tryAcquire returns false if acquire would block. Otherwise, it behaves like -// acquire and returns true. -// -// tryAcquire allows large messages to proceed by treating a size greater than -// maxSize as if it were equal to maxSize. -func (f *flowController) tryAcquire(size int) bool { - if f.semCount != nil { - if !f.semCount.TryAcquire(1) { - return false - } - } - if f.semSize != nil { - if !f.semSize.TryAcquire(f.bound(size)) { - if f.semCount != nil { - f.semCount.Release(1) - } - return false - } - } - return true -} - -// release notes that one message of size bytes is no longer outstanding. -func (f *flowController) release(size int) { - if f.semCount != nil { - f.semCount.Release(1) - } - if f.semSize != nil { - f.semSize.Release(f.bound(size)) - } -} - -func (f *flowController) bound(size int) int64 { - if size > f.maxSize { - return int64(f.maxSize) - } - return int64(size) -} diff --git a/vendor/cloud.google.com/go/pubsub/go18.go b/vendor/cloud.google.com/go/pubsub/go18.go deleted file mode 100644 index 7a7229460ad..00000000000 --- a/vendor/cloud.google.com/go/pubsub/go18.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright 2018 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. - -// +build go1.8 - -package pubsub - -import ( - "log" - "sync" - - "go.opencensus.io/plugin/ocgrpc" - "go.opencensus.io/stats" - "go.opencensus.io/stats/view" - "go.opencensus.io/tag" - "golang.org/x/net/context" - "google.golang.org/api/option" - "google.golang.org/grpc" -) - -func openCensusOptions() []option.ClientOption { - return []option.ClientOption{ - option.WithGRPCDialOption(grpc.WithStatsHandler(&ocgrpc.ClientHandler{})), - } -} - -var subscriptionKey tag.Key - -func init() { - var err error - if subscriptionKey, err = tag.NewKey("subscription"); err != nil { - log.Fatal("cannot create 'subscription' key") - } -} - -const statsPrefix = "cloud.google.com/go/pubsub/" - -var ( - // PullCount is a measure of the number of messages pulled. - // It is EXPERIMENTAL and subject to change or removal without notice. - PullCount = stats.Int64(statsPrefix+"pull_count", "Number of PubSub messages pulled", stats.UnitNone) - - // AckCount is a measure of the number of messages acked. - // It is EXPERIMENTAL and subject to change or removal without notice. - AckCount = stats.Int64(statsPrefix+"ack_count", "Number of PubSub messages acked", stats.UnitNone) - - // NackCount is a measure of the number of messages nacked. - // It is EXPERIMENTAL and subject to change or removal without notice. - NackCount = stats.Int64(statsPrefix+"nack_count", "Number of PubSub messages nacked", stats.UnitNone) - - // ModAckCount is a measure of the number of messages whose ack-deadline was modified. - // It is EXPERIMENTAL and subject to change or removal without notice. - ModAckCount = stats.Int64(statsPrefix+"mod_ack_count", "Number of ack-deadlines modified", stats.UnitNone) - - // StreamOpenCount is a measure of the number of times a streaming-pull stream was opened. - // It is EXPERIMENTAL and subject to change or removal without notice. - StreamOpenCount = stats.Int64(statsPrefix+"stream_open_count", "Number of calls opening a new streaming pull", stats.UnitNone) - - // StreamRetryCount is a measure of the number of times a streaming-pull operation was retried. - // It is EXPERIMENTAL and subject to change or removal without notice. - StreamRetryCount = stats.Int64(statsPrefix+"stream_retry_count", "Number of retries of a stream send or receive", stats.UnitNone) - - // StreamRequestCount is a measure of the number of requests sent on a streaming-pull stream. - // It is EXPERIMENTAL and subject to change or removal without notice. - StreamRequestCount = stats.Int64(statsPrefix+"stream_request_count", "Number gRPC StreamingPull request messages sent", stats.UnitNone) - - // StreamResponseCount is a measure of the number of responses received on a streaming-pull stream. - // It is EXPERIMENTAL and subject to change or removal without notice. - StreamResponseCount = stats.Int64(statsPrefix+"stream_response_count", "Number of gRPC StreamingPull response messages received", stats.UnitNone) - - // PullCountView is a cumulative sum of PullCount. - // It is EXPERIMENTAL and subject to change or removal without notice. - PullCountView *view.View - - // AckCountView is a cumulative sum of AckCount. - // It is EXPERIMENTAL and subject to change or removal without notice. - AckCountView *view.View - - // NackCountView is a cumulative sum of NackCount. - // It is EXPERIMENTAL and subject to change or removal without notice. - NackCountView *view.View - - // ModAckCountView is a cumulative sum of ModAckCount. - // It is EXPERIMENTAL and subject to change or removal without notice. - ModAckCountView *view.View - - // StreamOpenCountView is a cumulative sum of StreamOpenCount. - // It is EXPERIMENTAL and subject to change or removal without notice. - StreamOpenCountView *view.View - - // StreamRetryCountView is a cumulative sum of StreamRetryCount. - // It is EXPERIMENTAL and subject to change or removal without notice. - StreamRetryCountView *view.View - - // StreamRequestCountView is a cumulative sum of StreamRequestCount. - // It is EXPERIMENTAL and subject to change or removal without notice. - StreamRequestCountView *view.View - - // StreamResponseCountView is a cumulative sum of StreamResponseCount. - // It is EXPERIMENTAL and subject to change or removal without notice. - StreamResponseCountView *view.View -) - -func init() { - PullCountView = countView(PullCount) - AckCountView = countView(AckCount) - NackCountView = countView(NackCount) - ModAckCountView = countView(ModAckCount) - StreamOpenCountView = countView(StreamOpenCount) - StreamRetryCountView = countView(StreamRetryCount) - StreamRequestCountView = countView(StreamRequestCount) - StreamResponseCountView = countView(StreamResponseCount) -} - -func countView(m *stats.Int64Measure) *view.View { - return &view.View{ - Name: m.Name(), - Description: m.Description(), - TagKeys: []tag.Key{subscriptionKey}, - Measure: m, - Aggregation: view.Sum(), - } -} - -var logOnce sync.Once - -func withSubscriptionKey(ctx context.Context, subName string) context.Context { - ctx, err := tag.New(ctx, tag.Upsert(subscriptionKey, subName)) - if err != nil { - logOnce.Do(func() { - log.Printf("pubsub: error creating tag map: %v", err) - }) - } - return ctx -} - -func recordStat(ctx context.Context, m *stats.Int64Measure, n int64) { - stats.Record(ctx, m.M(n)) -} diff --git a/vendor/cloud.google.com/go/pubsub/internal/distribution/distribution.go b/vendor/cloud.google.com/go/pubsub/internal/distribution/distribution.go deleted file mode 100644 index c13fd636c72..00000000000 --- a/vendor/cloud.google.com/go/pubsub/internal/distribution/distribution.go +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2017 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 distribution - -import ( - "log" - "math" - "sort" - "sync/atomic" -) - -// D is a distribution. Methods of D can be called concurrently by multiple -// goroutines. -type D struct { - buckets []uint64 -} - -// New creates a new distribution capable of holding values from 0 to n-1. -func New(n int) *D { - return &D{ - buckets: make([]uint64, n), - } -} - -// Record records value v to the distribution. -// To help with distributions with long tails, if v is larger than the maximum value, -// Record records the maximum value instead. -// If v is negative, Record panics. -func (d *D) Record(v int) { - if v < 0 { - log.Panicf("Record: value out of range: %d", v) - } else if v >= len(d.buckets) { - v = len(d.buckets) - 1 - } - atomic.AddUint64(&d.buckets[v], 1) -} - -// Percentile computes the p-th percentile of the distribution where -// p is between 0 and 1. -func (d *D) Percentile(p float64) int { - // NOTE: This implementation uses the nearest-rank method. - // https://en.wikipedia.org/wiki/Percentile#The_nearest-rank_method - - if p < 0 || p > 1 { - log.Panicf("Percentile: percentile out of range: %f", p) - } - - bucketSums := make([]uint64, len(d.buckets)) - var sum uint64 - for i := range bucketSums { - sum += atomic.LoadUint64(&d.buckets[i]) - bucketSums[i] = sum - } - - total := bucketSums[len(bucketSums)-1] - target := uint64(math.Ceil(float64(total) * p)) - return sort.Search(len(bucketSums), func(i int) bool { return bucketSums[i] >= target }) -} diff --git a/vendor/cloud.google.com/go/pubsub/iterator.go b/vendor/cloud.google.com/go/pubsub/iterator.go deleted file mode 100644 index 78934e2c89f..00000000000 --- a/vendor/cloud.google.com/go/pubsub/iterator.go +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2016 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 pubsub - -import ( - "sync" - "time" - - vkit "cloud.google.com/go/pubsub/apiv1" - "cloud.google.com/go/pubsub/internal/distribution" - "golang.org/x/net/context" - pb "google.golang.org/genproto/googleapis/pubsub/v1" -) - -// newMessageIterator starts a new streamingMessageIterator. Stop must be called on the messageIterator -// when it is no longer needed. -// subName is the full name of the subscription to pull messages from. -// ctx is the context to use for acking messages and extending message deadlines. -func newMessageIterator(ctx context.Context, subc *vkit.SubscriberClient, subName string, po *pullOptions) *streamingMessageIterator { - ps := newPullStream(ctx, subc, subName, int32(po.ackDeadline.Seconds())) - return newStreamingMessageIterator(ctx, ps, po) -} - -type streamingMessageIterator struct { - ctx context.Context - po *pullOptions - ps *pullStream - kaTicker *time.Ticker // keep-alive (deadline extensions) - ackTicker *time.Ticker // message acks - nackTicker *time.Ticker // message nacks (more frequent than acks) - failed chan struct{} // closed on stream error - stopped chan struct{} // closed when Stop is called - drained chan struct{} // closed when stopped && no more pending messages - wg sync.WaitGroup - - mu sync.Mutex - ackTimeDist *distribution.D - keepAliveDeadlines map[string]time.Time - pendingReq *pb.StreamingPullRequest - pendingModAcks map[string]int32 // ack IDs whose ack deadline is to be modified - err error // error from stream failure -} - -func newStreamingMessageIterator(ctx context.Context, ps *pullStream, po *pullOptions) *streamingMessageIterator { - // TODO: make kaTicker frequency more configurable. (ackDeadline - 5s) is a - // reasonable default for now, because the minimum ack period is 10s. This - // gives us 5s grace. - keepAlivePeriod := po.ackDeadline - 5*time.Second - kaTicker := time.NewTicker(keepAlivePeriod) - - // Ack promptly so users don't lose work if client crashes. - ackTicker := time.NewTicker(100 * time.Millisecond) - nackTicker := time.NewTicker(100 * time.Millisecond) - it := &streamingMessageIterator{ - ctx: ctx, - ps: ps, - po: po, - kaTicker: kaTicker, - ackTicker: ackTicker, - nackTicker: nackTicker, - failed: make(chan struct{}), - stopped: make(chan struct{}), - drained: make(chan struct{}), - ackTimeDist: distribution.New(int(maxAckDeadline/time.Second) + 1), - keepAliveDeadlines: map[string]time.Time{}, - pendingReq: &pb.StreamingPullRequest{}, - pendingModAcks: map[string]int32{}, - } - it.wg.Add(1) - go it.sender() - return it -} - -// Subscription.receive will call stop on its messageIterator when finished with it. -// Stop will block until Done has been called on all Messages that have been -// returned by Next, or until the context with which the messageIterator was created -// is cancelled or exceeds its deadline. -func (it *streamingMessageIterator) stop() { - it.mu.Lock() - select { - case <-it.stopped: - default: - close(it.stopped) - } - it.checkDrained() - it.mu.Unlock() - it.wg.Wait() -} - -// checkDrained closes the drained channel if the iterator has been stopped and all -// pending messages have either been n/acked or expired. -// -// Called with the lock held. -func (it *streamingMessageIterator) checkDrained() { - select { - case <-it.drained: - return - default: - } - select { - case <-it.stopped: - if len(it.keepAliveDeadlines) == 0 { - close(it.drained) - } - default: - } -} - -// Called when a message is acked/nacked. -func (it *streamingMessageIterator) done(ackID string, ack bool, receiveTime time.Time) { - it.ackTimeDist.Record(int(time.Since(receiveTime) / time.Second)) - it.mu.Lock() - defer it.mu.Unlock() - delete(it.keepAliveDeadlines, ackID) - if ack { - it.pendingReq.AckIds = append(it.pendingReq.AckIds, ackID) - } else { - it.pendingModAcks[ackID] = 0 // Nack indicated by modifying the deadline to zero. - } - it.checkDrained() -} - -// fail is called when a stream method returns a permanent error. -func (it *streamingMessageIterator) fail(err error) { - it.mu.Lock() - if it.err == nil { - it.err = err - close(it.failed) - } - it.mu.Unlock() -} - -// receive makes a call to the stream's Recv method and returns -// its messages. -func (it *streamingMessageIterator) receive() ([]*Message, error) { - // Stop retrieving messages if the context is done, the stream - // failed, or the iterator's Stop method was called. - select { - case <-it.ctx.Done(): - return nil, it.ctx.Err() - default: - } - it.mu.Lock() - err := it.err - it.mu.Unlock() - if err != nil { - return nil, err - } - // Receive messages from stream. This may block indefinitely. - res, err := it.ps.Recv() - // The pullStream handles retries, so any error here is fatal. - if err != nil { - it.fail(err) - return nil, err - } - msgs, err := convertMessages(res.ReceivedMessages) - if err != nil { - it.fail(err) - return nil, err - } - - // We received some messages. Remember them so we can keep them alive. Also, - // arrange for a receipt mod-ack (which will occur at the next firing of - // nackTicker). - maxExt := time.Now().Add(it.po.maxExtension) - deadline := trunc32(int64(it.po.ackDeadline.Seconds())) - it.mu.Lock() - now := time.Now() - for _, m := range msgs { - m.receiveTime = now - m.doneFunc = it.done - it.keepAliveDeadlines[m.ackID] = maxExt - // The receipt mod-ack uses the subscription's configured ack deadline. Don't - // change the mod-ack if one is already pending. This is possible if there - // are retries. - if _, ok := it.pendingModAcks[m.ackID]; !ok { - it.pendingModAcks[m.ackID] = deadline - } - } - it.mu.Unlock() - return msgs, nil -} - -// sender runs in a goroutine and handles all sends to the stream. -func (it *streamingMessageIterator) sender() { - defer it.wg.Done() - defer it.kaTicker.Stop() - defer it.ackTicker.Stop() - defer it.nackTicker.Stop() - defer it.ps.CloseSend() - - done := false - for !done { - send := false - select { - case <-it.ctx.Done(): - // Context canceled or timed out: stop immediately, without - // another RPC. - return - - case <-it.failed: - // Stream failed: nothing to do, so stop immediately. - return - - case <-it.drained: - // All outstanding messages have been marked done: - // nothing left to do except send the final request. - it.mu.Lock() - send = (len(it.pendingReq.AckIds) > 0 || len(it.pendingModAcks) > 0) - done = true - - case <-it.kaTicker.C: - it.mu.Lock() - it.handleKeepAlives() - send = (len(it.pendingModAcks) > 0) - - case <-it.nackTicker.C: - it.mu.Lock() - send = (len(it.pendingModAcks) > 0) - - case <-it.ackTicker.C: - it.mu.Lock() - send = (len(it.pendingReq.AckIds) > 0) - } - // Lock is held here. - if send { - req := it.pendingReq - it.pendingReq = &pb.StreamingPullRequest{} - modAcks := it.pendingModAcks - it.pendingModAcks = map[string]int32{} - it.mu.Unlock() - for id, s := range modAcks { - req.ModifyDeadlineAckIds = append(req.ModifyDeadlineAckIds, id) - req.ModifyDeadlineSeconds = append(req.ModifyDeadlineSeconds, s) - } - err := it.send(req) - if err != nil { - // The streamingPuller handles retries, so any error here - // is fatal to the iterator. - it.fail(err) - return - } - } else { - it.mu.Unlock() - } - } -} - -func (it *streamingMessageIterator) send(req *pb.StreamingPullRequest) error { - // Note: len(modAckIDs) == len(modSecs) - var rest *pb.StreamingPullRequest - for len(req.AckIds) > 0 || len(req.ModifyDeadlineAckIds) > 0 { - req, rest = splitRequest(req, maxPayload) - if err := it.ps.Send(req); err != nil { - return err - } - req = rest - } - return nil -} - -// handleKeepAlives modifies the pending request to include deadline extensions -// for live messages. It also purges expired messages. -// -// Called with the lock held. -func (it *streamingMessageIterator) handleKeepAlives() { - now := time.Now() - dl := trunc32(int64(it.po.ackDeadline.Seconds())) - for id, expiry := range it.keepAliveDeadlines { - if expiry.Before(now) { - // This delete will not result in skipping any map items, as implied by - // the spec at https://golang.org/ref/spec#For_statements, "For - // statements with range clause", note 3, and stated explicitly at - // https://groups.google.com/forum/#!msg/golang-nuts/UciASUb03Js/pzSq5iVFAQAJ. - delete(it.keepAliveDeadlines, id) - } else { - // This will not overwrite a nack, because nacking removes the ID from keepAliveDeadlines. - it.pendingModAcks[id] = dl - } - } - it.checkDrained() -} diff --git a/vendor/cloud.google.com/go/pubsub/message.go b/vendor/cloud.google.com/go/pubsub/message.go deleted file mode 100644 index ac2cecca32b..00000000000 --- a/vendor/cloud.google.com/go/pubsub/message.go +++ /dev/null @@ -1,100 +0,0 @@ -// Copyright 2016 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 pubsub - -import ( - "time" - - "github.com/golang/protobuf/ptypes" - pb "google.golang.org/genproto/googleapis/pubsub/v1" -) - -// Message represents a Pub/Sub message. -type Message struct { - // ID identifies this message. - // This ID is assigned by the server and is populated for Messages obtained from a subscription. - // This field is read-only. - ID string - - // Data is the actual data in the message. - Data []byte - - // Attributes represents the key-value pairs the current message - // is labelled with. - Attributes map[string]string - - // ackID is the identifier to acknowledge this message. - ackID string - - // The time at which the message was published. - // This is populated by the server for Messages obtained from a subscription. - // This field is read-only. - PublishTime time.Time - - // receiveTime is the time the message was received by the client. - receiveTime time.Time - - // size is the approximate size of the message's data and attributes. - size int - - calledDone bool - - // The done method of the iterator that created this Message. - doneFunc func(string, bool, time.Time) -} - -func toMessage(resp *pb.ReceivedMessage) (*Message, error) { - if resp.Message == nil { - return &Message{ackID: resp.AckId}, nil - } - - pubTime, err := ptypes.Timestamp(resp.Message.PublishTime) - if err != nil { - return nil, err - } - return &Message{ - ackID: resp.AckId, - Data: resp.Message.Data, - Attributes: resp.Message.Attributes, - ID: resp.Message.MessageId, - PublishTime: pubTime, - }, nil -} - -// Ack indicates successful processing of a Message passed to the Subscriber.Receive callback. -// It should not be called on any other Message value. -// If message acknowledgement fails, the Message will be redelivered. -// Client code must call Ack or Nack when finished for each received Message. -// Calls to Ack or Nack have no effect after the first call. -func (m *Message) Ack() { - m.done(true) -} - -// Nack indicates that the client will not or cannot process a Message passed to the Subscriber.Receive callback. -// It should not be called on any other Message value. -// Nack will result in the Message being redelivered more quickly than if it were allowed to expire. -// Client code must call Ack or Nack when finished for each received Message. -// Calls to Ack or Nack have no effect after the first call. -func (m *Message) Nack() { - m.done(false) -} - -func (m *Message) done(ack bool) { - if m.calledDone { - return - } - m.calledDone = true - m.doneFunc(m.ackID, ack, m.receiveTime) -} diff --git a/vendor/cloud.google.com/go/pubsub/not_go18.go b/vendor/cloud.google.com/go/pubsub/not_go18.go deleted file mode 100644 index 09fd4bf5806..00000000000 --- a/vendor/cloud.google.com/go/pubsub/not_go18.go +++ /dev/null @@ -1,54 +0,0 @@ -// Copyright 2018 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. - -// +build !go1.8 - -package pubsub - -import ( - "golang.org/x/net/context" - "google.golang.org/api/option" -) - -// OpenCensus only supports go 1.8 and higher. - -func openCensusOptions() []option.ClientOption { return nil } - -func withSubscriptionKey(ctx context.Context, _ string) context.Context { - return ctx -} - -type dummy struct{} - -var ( - // Not supported below Go 1.8. - PullCount dummy - // Not supported below Go 1.8. - AckCount dummy - // Not supported below Go 1.8. - NackCount dummy - // Not supported below Go 1.8. - ModAckCount dummy - // Not supported below Go 1.8. - StreamOpenCount dummy - // Not supported below Go 1.8. - StreamRetryCount dummy - // Not supported below Go 1.8. - StreamRequestCount dummy - // Not supported below Go 1.8. - StreamResponseCount dummy -) - -func recordStat(context.Context, dummy, int64) { -} diff --git a/vendor/cloud.google.com/go/pubsub/pubsub.go b/vendor/cloud.google.com/go/pubsub/pubsub.go deleted file mode 100644 index 8475186d739..00000000000 --- a/vendor/cloud.google.com/go/pubsub/pubsub.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright 2014 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 pubsub // import "cloud.google.com/go/pubsub" - -import ( - "fmt" - "os" - "runtime" - "time" - - "cloud.google.com/go/internal/version" - vkit "cloud.google.com/go/pubsub/apiv1" - "golang.org/x/net/context" - "google.golang.org/api/option" - "google.golang.org/grpc" - "google.golang.org/grpc/keepalive" -) - -const ( - // ScopePubSub grants permissions to view and manage Pub/Sub - // topics and subscriptions. - ScopePubSub = "https://www.googleapis.com/auth/pubsub" - - // ScopeCloudPlatform grants permissions to view and manage your data - // across Google Cloud Platform services. - ScopeCloudPlatform = "https://www.googleapis.com/auth/cloud-platform" -) - -const ( - prodAddr = "https://pubsub.googleapis.com/" - minAckDeadline = 10 * time.Second - maxAckDeadline = 10 * time.Minute -) - -// Client is a Google Pub/Sub client scoped to a single project. -// -// Clients should be reused rather than being created as needed. -// A Client may be shared by multiple goroutines. -type Client struct { - projectID string - pubc *vkit.PublisherClient - subc *vkit.SubscriberClient -} - -// NewClient creates a new PubSub client. -func NewClient(ctx context.Context, projectID string, opts ...option.ClientOption) (c *Client, err error) { - var o []option.ClientOption - // Environment variables for gcloud emulator: - // https://cloud.google.com/sdk/gcloud/reference/beta/emulators/pubsub/ - if addr := os.Getenv("PUBSUB_EMULATOR_HOST"); addr != "" { - conn, err := grpc.Dial(addr, grpc.WithInsecure()) - if err != nil { - return nil, fmt.Errorf("grpc.Dial: %v", err) - } - o = []option.ClientOption{option.WithGRPCConn(conn)} - } else { - o = []option.ClientOption{ - // Create multiple connections to increase throughput. - option.WithGRPCConnectionPool(runtime.GOMAXPROCS(0)), - option.WithGRPCDialOption(grpc.WithKeepaliveParams(keepalive.ClientParameters{ - Time: 5 * time.Minute, - })), - } - o = append(o, openCensusOptions()...) - } - o = append(o, opts...) - pubc, err := vkit.NewPublisherClient(ctx, o...) - if err != nil { - return nil, fmt.Errorf("pubsub: %v", err) - } - subc, err := vkit.NewSubscriberClient(ctx, option.WithGRPCConn(pubc.Connection())) - if err != nil { - // Should never happen, since we are passing in the connection. - // If it does, we cannot close, because the user may have passed in their - // own connection originally. - return nil, fmt.Errorf("pubsub: %v", err) - } - pubc.SetGoogleClientInfo("gccl", version.Repo) - subc.SetGoogleClientInfo("gccl", version.Repo) - return &Client{ - projectID: projectID, - pubc: pubc, - subc: subc, - }, nil -} - -// Close releases any resources held by the client, -// such as memory and goroutines. -// -// If the client is available for the lifetime of the program, then Close need not be -// called at exit. -func (c *Client) Close() error { - // Return the first error, because the first call closes the connection. - err := c.pubc.Close() - _ = c.subc.Close() - return err -} - -func (c *Client) fullyQualifiedProjectName() string { - return fmt.Sprintf("projects/%s", c.projectID) -} diff --git a/vendor/cloud.google.com/go/pubsub/pullstream.go b/vendor/cloud.google.com/go/pubsub/pullstream.go deleted file mode 100644 index 4aea1d501b9..00000000000 --- a/vendor/cloud.google.com/go/pubsub/pullstream.go +++ /dev/null @@ -1,173 +0,0 @@ -// Copyright 2018 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 pubsub - -import ( - "io" - "sync" - "time" - - vkit "cloud.google.com/go/pubsub/apiv1" - gax "github.com/googleapis/gax-go" - "golang.org/x/net/context" - pb "google.golang.org/genproto/googleapis/pubsub/v1" - "google.golang.org/grpc" -) - -// A pullStream supports the methods of a StreamingPullClient, but re-opens -// the stream on a retryable error. -type pullStream struct { - ctx context.Context - open func() (pb.Subscriber_StreamingPullClient, error) - - mu sync.Mutex - spc *pb.Subscriber_StreamingPullClient - err error // permanent error -} - -func newPullStream(ctx context.Context, subc *vkit.SubscriberClient, subName string, ackDeadlineSecs int32) *pullStream { - ctx = withSubscriptionKey(ctx, subName) - return &pullStream{ - ctx: ctx, - open: func() (pb.Subscriber_StreamingPullClient, error) { - spc, err := subc.StreamingPull(ctx, gax.WithGRPCOptions(grpc.MaxCallRecvMsgSize(maxSendRecvBytes))) - if err == nil { - recordStat(ctx, StreamRequestCount, 1) - err = spc.Send(&pb.StreamingPullRequest{ - Subscription: subName, - StreamAckDeadlineSeconds: ackDeadlineSecs, - }) - } - if err != nil { - return nil, err - } - return spc, nil - }, - } -} - -// get returns either a valid *StreamingPullClient (SPC), or a permanent error. -// If the argument is nil, this is the first call for an RPC, and the current -// SPC will be returned (or a new one will be opened). Otherwise, this call is a -// request to re-open the stream because of a retryable error, and the argument -// is a pointer to the SPC that returned the error. -func (s *pullStream) get(spc *pb.Subscriber_StreamingPullClient) (*pb.Subscriber_StreamingPullClient, error) { - s.mu.Lock() - defer s.mu.Unlock() - // A stored error is permanent. - if s.err != nil { - return nil, s.err - } - // If the context is done, so are we. - select { - case <-s.ctx.Done(): - s.err = s.ctx.Err() - return nil, s.err - default: - } - // TODO(jba): We can use the following instead of the above after we drop support for 1.8: - // s.err = s.ctx.Err() - // if s.err != nil { - // return nil, s.err - // } - - // If the current and argument SPCs differ, return the current one. This subsumes two cases: - // 1. We have an SPC and the caller is getting the stream for the first time. - // 2. The caller wants to retry, but they have an older SPC; we've already retried. - if spc != s.spc { - return s.spc, nil - } - // Either this is the very first call on this stream (s.spc == nil), or we have a valid - // retry request. Either way, open a new stream. - // The lock is held here for a long time, but it doesn't matter because no callers could get - // anything done anyway. - s.spc = new(pb.Subscriber_StreamingPullClient) - recordStat(s.ctx, StreamOpenCount, 1) - *s.spc, s.err = s.open() // Setting s.err means any error from open is permanent. Reconsider. - return s.spc, s.err -} - -func (s *pullStream) call(f func(pb.Subscriber_StreamingPullClient) error) error { - var ( - spc *pb.Subscriber_StreamingPullClient - err error - bo gax.Backoff - ) - for i := 0; ; i++ { - spc, err = s.get(spc) - if err != nil { - // Preserve the existing behavior of not retrying on open. Is that a bug? - // (If we do decide to retry, don't retry after we're closed.) - return err - } - start := time.Now() - err = f(*spc) - if err != nil { - if isRetryable(err) { - recordStat(s.ctx, StreamRetryCount, 1) - if time.Since(start) < 30*time.Second { // don't sleep if we've been blocked for a while - if err := gax.Sleep(s.ctx, bo.Pause()); err != nil { - return err - } - } - continue - } - s.mu.Lock() - s.err = err - s.mu.Unlock() - } - return err - } -} - -func (s *pullStream) Send(req *pb.StreamingPullRequest) error { - return s.call(func(spc pb.Subscriber_StreamingPullClient) error { - recordStat(s.ctx, AckCount, int64(len(req.AckIds))) - zeroes := 0 - for _, mds := range req.ModifyDeadlineSeconds { - if mds == 0 { - zeroes++ - } - } - recordStat(s.ctx, NackCount, int64(zeroes)) - recordStat(s.ctx, ModAckCount, int64(len(req.ModifyDeadlineSeconds)-zeroes)) - recordStat(s.ctx, StreamRequestCount, 1) - return spc.Send(req) - }) -} - -func (s *pullStream) Recv() (*pb.StreamingPullResponse, error) { - var res *pb.StreamingPullResponse - err := s.call(func(spc pb.Subscriber_StreamingPullClient) error { - var err error - recordStat(s.ctx, StreamResponseCount, 1) - res, err = spc.Recv() - if err == nil { - recordStat(s.ctx, PullCount, int64(len(res.ReceivedMessages))) - } - return err - }) - return res, err -} - -func (s *pullStream) CloseSend() error { - err := s.call(func(spc pb.Subscriber_StreamingPullClient) error { - return spc.CloseSend() - }) - s.mu.Lock() - s.err = io.EOF // should not be retried - s.mu.Unlock() - return err -} diff --git a/vendor/cloud.google.com/go/pubsub/service.go b/vendor/cloud.google.com/go/pubsub/service.go deleted file mode 100644 index c63e4d92dcc..00000000000 --- a/vendor/cloud.google.com/go/pubsub/service.go +++ /dev/null @@ -1,120 +0,0 @@ -// Copyright 2016 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 pubsub - -import ( - "fmt" - "math" - "strings" - - pb "google.golang.org/genproto/googleapis/pubsub/v1" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// maxPayload is the maximum number of bytes to devote to actual ids in -// acknowledgement or modifyAckDeadline requests. A serialized -// AcknowledgeRequest proto has a small constant overhead, plus the size of the -// subscription name, plus 3 bytes per ID (a tag byte and two size bytes). A -// ModifyAckDeadlineRequest has an additional few bytes for the deadline. We -// don't know the subscription name here, so we just assume the size exclusive -// of ids is 100 bytes. -// -// With gRPC there is no way for the client to know the server's max message size (it is -// configurable on the server). We know from experience that it -// it 512K. -const ( - maxPayload = 512 * 1024 - reqFixedOverhead = 100 - overheadPerID = 3 - maxSendRecvBytes = 20 * 1024 * 1024 // 20M -) - -func convertMessages(rms []*pb.ReceivedMessage) ([]*Message, error) { - msgs := make([]*Message, 0, len(rms)) - for i, m := range rms { - msg, err := toMessage(m) - if err != nil { - return nil, fmt.Errorf("pubsub: cannot decode the retrieved message at index: %d, message: %+v", i, m) - } - msgs = append(msgs, msg) - } - return msgs, nil -} - -func trunc32(i int64) int32 { - if i > math.MaxInt32 { - i = math.MaxInt32 - } - return int32(i) -} - -// Logic from https://github.com/GoogleCloudPlatform/google-cloud-java/blob/master/google-cloud-pubsub/src/main/java/com/google/cloud/pubsub/v1/StatusUtil.java. -func isRetryable(err error) bool { - s, ok := status.FromError(err) - if !ok { // includes io.EOF, normal stream close, which causes us to reopen - return true - } - switch s.Code() { - case codes.DeadlineExceeded, codes.Internal, codes.Canceled, codes.ResourceExhausted: - return true - case codes.Unavailable: - return !strings.Contains(s.Message(), "Server shutdownNow invoked") - default: - return false - } -} - -// Split req into a prefix that is smaller than maxSize, and a remainder. -func splitRequest(req *pb.StreamingPullRequest, maxSize int) (prefix, remainder *pb.StreamingPullRequest) { - const int32Bytes = 4 - - // Copy all fields before splitting the variable-sized ones. - remainder = &pb.StreamingPullRequest{} - *remainder = *req - // Split message so it isn't too big. - size := reqFixedOverhead - i := 0 - for size < maxSize && (i < len(req.AckIds) || i < len(req.ModifyDeadlineAckIds)) { - if i < len(req.AckIds) { - size += overheadPerID + len(req.AckIds[i]) - } - if i < len(req.ModifyDeadlineAckIds) { - size += overheadPerID + len(req.ModifyDeadlineAckIds[i]) + int32Bytes - } - i++ - } - - min := func(a, b int) int { - if a < b { - return a - } - return b - } - - j := i - if size > maxSize { - j-- - } - k := min(j, len(req.AckIds)) - remainder.AckIds = req.AckIds[k:] - req.AckIds = req.AckIds[:k] - k = min(j, len(req.ModifyDeadlineAckIds)) - remainder.ModifyDeadlineAckIds = req.ModifyDeadlineAckIds[k:] - remainder.ModifyDeadlineSeconds = req.ModifyDeadlineSeconds[k:] - req.ModifyDeadlineAckIds = req.ModifyDeadlineAckIds[:k] - req.ModifyDeadlineSeconds = req.ModifyDeadlineSeconds[:k] - return req, remainder -} diff --git a/vendor/cloud.google.com/go/pubsub/snapshot.go b/vendor/cloud.google.com/go/pubsub/snapshot.go deleted file mode 100644 index 7140e96627a..00000000000 --- a/vendor/cloud.google.com/go/pubsub/snapshot.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2017 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 pubsub - -import ( - "fmt" - "strings" - "time" - - "github.com/golang/protobuf/ptypes" - "golang.org/x/net/context" - pb "google.golang.org/genproto/googleapis/pubsub/v1" -) - -// Snapshot is a reference to a PubSub snapshot. -type Snapshot struct { - c *Client - - // The fully qualified identifier for the snapshot, in the format "projects//snapshots/" - name string -} - -// ID returns the unique identifier of the snapshot within its project. -func (s *Snapshot) ID() string { - slash := strings.LastIndex(s.name, "/") - if slash == -1 { - // name is not a fully-qualified name. - panic("bad snapshot name") - } - return s.name[slash+1:] -} - -// SnapshotConfig contains the details of a Snapshot. -type SnapshotConfig struct { - *Snapshot - Topic *Topic - Expiration time.Time -} - -// Snapshot creates a reference to a snapshot. -func (c *Client) Snapshot(id string) *Snapshot { - return &Snapshot{ - c: c, - name: fmt.Sprintf("projects/%s/snapshots/%s", c.projectID, id), - } -} - -// Snapshots returns an iterator which returns snapshots for this project. -func (c *Client) Snapshots(ctx context.Context) *SnapshotConfigIterator { - it := c.subc.ListSnapshots(ctx, &pb.ListSnapshotsRequest{ - Project: c.fullyQualifiedProjectName(), - }) - next := func() (*SnapshotConfig, error) { - snap, err := it.Next() - if err != nil { - return nil, err - } - return toSnapshotConfig(snap, c) - } - return &SnapshotConfigIterator{next: next} -} - -// SnapshotConfigIterator is an iterator that returns a series of snapshots. -type SnapshotConfigIterator struct { - next func() (*SnapshotConfig, error) -} - -// Next returns the next SnapshotConfig. Its second return value is iterator.Done if there are no more results. -// Once Next returns iterator.Done, all subsequent calls will return iterator.Done. -func (snaps *SnapshotConfigIterator) Next() (*SnapshotConfig, error) { - return snaps.next() -} - -// Delete deletes a snapshot. -func (snap *Snapshot) Delete(ctx context.Context) error { - return snap.c.subc.DeleteSnapshot(ctx, &pb.DeleteSnapshotRequest{Snapshot: snap.name}) -} - -// SeekToTime seeks the subscription to a point in time. -// -// Messages retained in the subscription that were published before this -// time are marked as acknowledged, and messages retained in the -// subscription that were published after this time are marked as -// unacknowledged. Note that this operation affects only those messages -// retained in the subscription (configured by SnapshotConfig). For example, -// if `time` corresponds to a point before the message retention -// window (or to a point before the system's notion of the subscription -// creation time), only retained messages will be marked as unacknowledged, -// and already-expunged messages will not be restored. -func (s *Subscription) SeekToTime(ctx context.Context, t time.Time) error { - ts, err := ptypes.TimestampProto(t) - if err != nil { - return err - } - _, err = s.c.subc.Seek(ctx, &pb.SeekRequest{ - Subscription: s.name, - Target: &pb.SeekRequest_Time{ts}, - }) - return err -} - -// CreateSnapshot creates a new snapshot from this subscription. -// The snapshot will be for the topic this subscription is subscribed to. -// If the name is empty string, a unique name is assigned. -// -// The created snapshot is guaranteed to retain: -// (a) The existing backlog on the subscription. More precisely, this is -// defined as the messages in the subscription's backlog that are -// unacknowledged when Snapshot returns without error. -// (b) Any messages published to the subscription's topic following -// Snapshot returning without error. -func (s *Subscription) CreateSnapshot(ctx context.Context, name string) (*SnapshotConfig, error) { - if name != "" { - name = fmt.Sprintf("projects/%s/snapshots/%s", strings.Split(s.name, "/")[1], name) - } - snap, err := s.c.subc.CreateSnapshot(ctx, &pb.CreateSnapshotRequest{ - Name: name, - Subscription: s.name, - }) - if err != nil { - return nil, err - } - return toSnapshotConfig(snap, s.c) -} - -// SeekToSnapshot seeks the subscription to a snapshot. -// -// The snapshot need not be created from this subscription, -// but it must be for the topic this subscription is subscribed to. -func (s *Subscription) SeekToSnapshot(ctx context.Context, snap *Snapshot) error { - _, err := s.c.subc.Seek(ctx, &pb.SeekRequest{ - Subscription: s.name, - Target: &pb.SeekRequest_Snapshot{snap.name}, - }) - return err -} - -func toSnapshotConfig(snap *pb.Snapshot, c *Client) (*SnapshotConfig, error) { - exp, err := ptypes.Timestamp(snap.ExpireTime) - if err != nil { - return nil, err - } - return &SnapshotConfig{ - Snapshot: &Snapshot{c: c, name: snap.Name}, - Topic: newTopic(c, snap.Topic), - Expiration: exp, - }, nil -} diff --git a/vendor/cloud.google.com/go/pubsub/subscription.go b/vendor/cloud.google.com/go/pubsub/subscription.go deleted file mode 100644 index 93054d536a0..00000000000 --- a/vendor/cloud.google.com/go/pubsub/subscription.go +++ /dev/null @@ -1,522 +0,0 @@ -// Copyright 2016 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 pubsub - -import ( - "errors" - "fmt" - "io" - "strings" - "sync" - "time" - - "cloud.google.com/go/iam" - "cloud.google.com/go/internal/optional" - "github.com/golang/protobuf/ptypes" - durpb "github.com/golang/protobuf/ptypes/duration" - "golang.org/x/net/context" - "golang.org/x/sync/errgroup" - pb "google.golang.org/genproto/googleapis/pubsub/v1" - fmpb "google.golang.org/genproto/protobuf/field_mask" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" -) - -// Subscription is a reference to a PubSub subscription. -type Subscription struct { - c *Client - - // The fully qualified identifier for the subscription, in the format "projects//subscriptions/" - name string - - // Settings for pulling messages. Configure these before calling Receive. - ReceiveSettings ReceiveSettings - - mu sync.Mutex - receiveActive bool -} - -// Subscription creates a reference to a subscription. -func (c *Client) Subscription(id string) *Subscription { - return c.SubscriptionInProject(id, c.projectID) -} - -// SubscriptionInProject creates a reference to a subscription in a given project. -func (c *Client) SubscriptionInProject(id, projectID string) *Subscription { - return &Subscription{ - c: c, - name: fmt.Sprintf("projects/%s/subscriptions/%s", projectID, id), - } -} - -// String returns the globally unique printable name of the subscription. -func (s *Subscription) String() string { - return s.name -} - -// ID returns the unique identifier of the subscription within its project. -func (s *Subscription) ID() string { - slash := strings.LastIndex(s.name, "/") - if slash == -1 { - // name is not a fully-qualified name. - panic("bad subscription name") - } - return s.name[slash+1:] -} - -// Subscriptions returns an iterator which returns all of the subscriptions for the client's project. -func (c *Client) Subscriptions(ctx context.Context) *SubscriptionIterator { - it := c.subc.ListSubscriptions(ctx, &pb.ListSubscriptionsRequest{ - Project: c.fullyQualifiedProjectName(), - }) - return &SubscriptionIterator{ - c: c, - next: func() (string, error) { - sub, err := it.Next() - if err != nil { - return "", err - } - return sub.Name, nil - }, - } -} - -// SubscriptionIterator is an iterator that returns a series of subscriptions. -type SubscriptionIterator struct { - c *Client - next func() (string, error) -} - -// Next returns the next subscription. If there are no more subscriptions, iterator.Done will be returned. -func (subs *SubscriptionIterator) Next() (*Subscription, error) { - subName, err := subs.next() - if err != nil { - return nil, err - } - return &Subscription{c: subs.c, name: subName}, nil -} - -// PushConfig contains configuration for subscriptions that operate in push mode. -type PushConfig struct { - // A URL locating the endpoint to which messages should be pushed. - Endpoint string - - // Endpoint configuration attributes. See https://cloud.google.com/pubsub/docs/reference/rest/v1/projects.subscriptions#pushconfig for more details. - Attributes map[string]string -} - -func (pc *PushConfig) toProto() *pb.PushConfig { - return &pb.PushConfig{ - Attributes: pc.Attributes, - PushEndpoint: pc.Endpoint, - } -} - -// Subscription config contains the configuration of a subscription. -type SubscriptionConfig struct { - Topic *Topic - PushConfig PushConfig - - // The default maximum time after a subscriber receives a message before - // the subscriber should acknowledge the message. Note: messages which are - // obtained via Subscription.Receive need not be acknowledged within this - // deadline, as the deadline will be automatically extended. - AckDeadline time.Duration - - // Whether to retain acknowledged messages. If true, acknowledged messages - // will not be expunged until they fall out of the RetentionDuration window. - RetainAckedMessages bool - - // How long to retain messages in backlog, from the time of publish. If - // RetainAckedMessages is true, this duration affects the retention of - // acknowledged messages, otherwise only unacknowledged messages are retained. - // Defaults to 7 days. Cannot be longer than 7 days or shorter than 10 minutes. - RetentionDuration time.Duration -} - -func (cfg *SubscriptionConfig) toProto(name string) *pb.Subscription { - var pbPushConfig *pb.PushConfig - if cfg.PushConfig.Endpoint != "" || len(cfg.PushConfig.Attributes) != 0 { - pbPushConfig = &pb.PushConfig{ - Attributes: cfg.PushConfig.Attributes, - PushEndpoint: cfg.PushConfig.Endpoint, - } - } - var retentionDuration *durpb.Duration - if cfg.RetentionDuration != 0 { - retentionDuration = ptypes.DurationProto(cfg.RetentionDuration) - } - return &pb.Subscription{ - Name: name, - Topic: cfg.Topic.name, - PushConfig: pbPushConfig, - AckDeadlineSeconds: trunc32(int64(cfg.AckDeadline.Seconds())), - RetainAckedMessages: cfg.RetainAckedMessages, - MessageRetentionDuration: retentionDuration, - } -} - -func protoToSubscriptionConfig(pbSub *pb.Subscription, c *Client) (SubscriptionConfig, error) { - rd := time.Hour * 24 * 7 - var err error - if pbSub.MessageRetentionDuration != nil { - rd, err = ptypes.Duration(pbSub.MessageRetentionDuration) - if err != nil { - return SubscriptionConfig{}, err - } - } - return SubscriptionConfig{ - Topic: newTopic(c, pbSub.Topic), - AckDeadline: time.Second * time.Duration(pbSub.AckDeadlineSeconds), - PushConfig: PushConfig{ - Endpoint: pbSub.PushConfig.PushEndpoint, - Attributes: pbSub.PushConfig.Attributes, - }, - RetainAckedMessages: pbSub.RetainAckedMessages, - RetentionDuration: rd, - }, nil -} - -// ReceiveSettings configure the Receive method. -// A zero ReceiveSettings will result in values equivalent to DefaultReceiveSettings. -type ReceiveSettings struct { - // MaxExtension is the maximum period for which the Subscription should - // automatically extend the ack deadline for each message. - // - // The Subscription will automatically extend the ack deadline of all - // fetched Messages for the duration specified. Automatic deadline - // extension may be disabled by specifying a duration less than 0. - // - // Connections may be terminated if they last longer than 30m, which - // effectively makes that the ceiling for this value. For longer message - // processing, see the example at https://godoc.org/cloud.google.com/go/pubsub/apiv1#example_SubscriberClient_Pull_lengthyClientProcessing - MaxExtension time.Duration - - // MaxOutstandingMessages is the maximum number of unprocessed messages - // (unacknowledged but not yet expired). If MaxOutstandingMessages is 0, it - // will be treated as if it were DefaultReceiveSettings.MaxOutstandingMessages. - // If the value is negative, then there will be no limit on the number of - // unprocessed messages. - MaxOutstandingMessages int - - // MaxOutstandingBytes is the maximum size of unprocessed messages - // (unacknowledged but not yet expired). If MaxOutstandingBytes is 0, it will - // be treated as if it were DefaultReceiveSettings.MaxOutstandingBytes. If - // the value is negative, then there will be no limit on the number of bytes - // for unprocessed messages. - MaxOutstandingBytes int - - // NumGoroutines is the number of goroutines Receive will spawn to pull - // messages concurrently. If NumGoroutines is less than 1, it will be treated - // as if it were DefaultReceiveSettings.NumGoroutines. - // - // NumGoroutines does not limit the number of messages that can be processed - // concurrently. Even with one goroutine, many messages might be processed at - // once, because that goroutine may continually receive messages and invoke the - // function passed to Receive on them. To limit the number of messages being - // processed concurrently, set MaxOutstandingMessages. - NumGoroutines int -} - -// DefaultReceiveSettings holds the default values for ReceiveSettings. -var DefaultReceiveSettings = ReceiveSettings{ - MaxExtension: 10 * time.Minute, - MaxOutstandingMessages: 1000, - MaxOutstandingBytes: 1e9, // 1G - NumGoroutines: 1, -} - -// Delete deletes the subscription. -func (s *Subscription) Delete(ctx context.Context) error { - return s.c.subc.DeleteSubscription(ctx, &pb.DeleteSubscriptionRequest{Subscription: s.name}) -} - -// Exists reports whether the subscription exists on the server. -func (s *Subscription) Exists(ctx context.Context) (bool, error) { - _, err := s.c.subc.GetSubscription(ctx, &pb.GetSubscriptionRequest{Subscription: s.name}) - if err == nil { - return true, nil - } - if grpc.Code(err) == codes.NotFound { - return false, nil - } - return false, err -} - -// Config fetches the current configuration for the subscription. -func (s *Subscription) Config(ctx context.Context) (SubscriptionConfig, error) { - pbSub, err := s.c.subc.GetSubscription(ctx, &pb.GetSubscriptionRequest{Subscription: s.name}) - if err != nil { - return SubscriptionConfig{}, err - } - cfg, err := protoToSubscriptionConfig(pbSub, s.c) - if err != nil { - return SubscriptionConfig{}, err - } - return cfg, nil -} - -// SubscriptionConfigToUpdate describes how to update a subscription. -type SubscriptionConfigToUpdate struct { - // If non-nil, the push config is changed. - PushConfig *PushConfig - - // If non-zero, the ack deadline is changed. - AckDeadline time.Duration - - // If set, RetainAckedMessages is changed. - RetainAckedMessages optional.Bool - - // If non-zero, RetentionDuration is changed. - RetentionDuration time.Duration -} - -// Update changes an existing subscription according to the fields set in cfg. -// It returns the new SubscriptionConfig. -// -// Update returns an error if no fields were modified. -func (s *Subscription) Update(ctx context.Context, cfg SubscriptionConfigToUpdate) (SubscriptionConfig, error) { - req := s.updateRequest(&cfg) - if len(req.UpdateMask.Paths) == 0 { - return SubscriptionConfig{}, errors.New("pubsub: UpdateSubscription call with nothing to update") - } - rpsub, err := s.c.subc.UpdateSubscription(ctx, req) - if err != nil { - return SubscriptionConfig{}, err - } - return protoToSubscriptionConfig(rpsub, s.c) -} - -func (s *Subscription) updateRequest(cfg *SubscriptionConfigToUpdate) *pb.UpdateSubscriptionRequest { - psub := &pb.Subscription{Name: s.name} - var paths []string - if cfg.PushConfig != nil { - psub.PushConfig = cfg.PushConfig.toProto() - paths = append(paths, "push_config") - } - if cfg.AckDeadline != 0 { - psub.AckDeadlineSeconds = trunc32(int64(cfg.AckDeadline.Seconds())) - paths = append(paths, "ack_deadline_seconds") - } - if cfg.RetainAckedMessages != nil { - psub.RetainAckedMessages = optional.ToBool(cfg.RetainAckedMessages) - paths = append(paths, "retain_acked_messages") - } - if cfg.RetentionDuration != 0 { - psub.MessageRetentionDuration = ptypes.DurationProto(cfg.RetentionDuration) - paths = append(paths, "message_retention_duration") - } - return &pb.UpdateSubscriptionRequest{ - Subscription: psub, - UpdateMask: &fmpb.FieldMask{Paths: paths}, - } -} - -func (s *Subscription) IAM() *iam.Handle { - return iam.InternalNewHandle(s.c.subc.Connection(), s.name) -} - -// CreateSubscription creates a new subscription on a topic. -// -// id is the name of the subscription to create. It must start with a letter, -// and contain only letters ([A-Za-z]), numbers ([0-9]), dashes (-), -// underscores (_), periods (.), tildes (~), plus (+) or percent signs (%). It -// must be between 3 and 255 characters in length, and must not start with -// "goog". -// -// cfg.Topic is the topic from which the subscription should receive messages. It -// need not belong to the same project as the subscription. This field is required. -// -// cfg.AckDeadline is the maximum time after a subscriber receives a message before -// the subscriber should acknowledge the message. It must be between 10 and 600 -// seconds (inclusive), and is rounded down to the nearest second. If the -// provided ackDeadline is 0, then the default value of 10 seconds is used. -// Note: messages which are obtained via Subscription.Receive need not be -// acknowledged within this deadline, as the deadline will be automatically -// extended. -// -// cfg.PushConfig may be set to configure this subscription for push delivery. -// -// If the subscription already exists an error will be returned. -func (c *Client) CreateSubscription(ctx context.Context, id string, cfg SubscriptionConfig) (*Subscription, error) { - if cfg.Topic == nil { - return nil, errors.New("pubsub: require non-nil Topic") - } - if cfg.AckDeadline == 0 { - cfg.AckDeadline = 10 * time.Second - } - if d := cfg.AckDeadline; d < 10*time.Second || d > 600*time.Second { - return nil, fmt.Errorf("ack deadline must be between 10 and 600 seconds; got: %v", d) - } - - sub := c.Subscription(id) - _, err := c.subc.CreateSubscription(ctx, cfg.toProto(sub.name)) - if err != nil { - return nil, err - } - return sub, nil -} - -var errReceiveInProgress = errors.New("pubsub: Receive already in progress for this subscription") - -// Receive calls f with the outstanding messages from the subscription. -// It blocks until ctx is done, or the service returns a non-retryable error. -// -// The standard way to terminate a Receive is to cancel its context: -// -// cctx, cancel := context.WithCancel(ctx) -// err := sub.Receive(cctx, callback) -// // Call cancel from callback, or another goroutine. -// -// If the service returns a non-retryable error, Receive returns that error after -// all of the outstanding calls to f have returned. If ctx is done, Receive -// returns nil after all of the outstanding calls to f have returned and -// all messages have been acknowledged or have expired. -// -// Receive calls f concurrently from multiple goroutines. It is encouraged to -// process messages synchronously in f, even if that processing is relatively -// time-consuming; Receive will spawn new goroutines for incoming messages, -// limited by MaxOutstandingMessages and MaxOutstandingBytes in ReceiveSettings. -// -// The context passed to f will be canceled when ctx is Done or there is a -// fatal service error. -// -// Receive will automatically extend the ack deadline of all fetched Messages for the -// period specified by s.ReceiveSettings.MaxExtension. -// -// Each Subscription may have only one invocation of Receive active at a time. -func (s *Subscription) Receive(ctx context.Context, f func(context.Context, *Message)) error { - s.mu.Lock() - if s.receiveActive { - s.mu.Unlock() - return errReceiveInProgress - } - s.receiveActive = true - s.mu.Unlock() - defer func() { s.mu.Lock(); s.receiveActive = false; s.mu.Unlock() }() - - config, err := s.Config(ctx) - if err != nil { - if grpc.Code(err) == codes.Canceled { - return nil - } - return err - } - maxCount := s.ReceiveSettings.MaxOutstandingMessages - if maxCount == 0 { - maxCount = DefaultReceiveSettings.MaxOutstandingMessages - } - maxBytes := s.ReceiveSettings.MaxOutstandingBytes - if maxBytes == 0 { - maxBytes = DefaultReceiveSettings.MaxOutstandingBytes - } - maxExt := s.ReceiveSettings.MaxExtension - if maxExt == 0 { - maxExt = DefaultReceiveSettings.MaxExtension - } else if maxExt < 0 { - // If MaxExtension is negative, disable automatic extension. - maxExt = 0 - } - numGoroutines := s.ReceiveSettings.NumGoroutines - if numGoroutines < 1 { - numGoroutines = DefaultReceiveSettings.NumGoroutines - } - // TODO(jba): add tests that verify that ReceiveSettings are correctly processed. - po := &pullOptions{ - maxExtension: maxExt, - maxPrefetch: trunc32(int64(maxCount)), - ackDeadline: config.AckDeadline, - } - fc := newFlowController(maxCount, maxBytes) - - // Wait for all goroutines started by Receive to return, so instead of an - // obscure goroutine leak we have an obvious blocked call to Receive. - group, gctx := errgroup.WithContext(ctx) - for i := 0; i < numGoroutines; i++ { - group.Go(func() error { - return s.receive(gctx, po, fc, f) - }) - } - return group.Wait() -} - -func (s *Subscription) receive(ctx context.Context, po *pullOptions, fc *flowController, f func(context.Context, *Message)) error { - // Cancel a sub-context when we return, to kick the context-aware callbacks - // and the goroutine below. - ctx2, cancel := context.WithCancel(ctx) - // Call stop when Receive's context is done. - // Stop will block until all outstanding messages have been acknowledged - // or there was a fatal service error. - // The iterator does not use the context passed to Receive. If it did, canceling - // that context would immediately stop the iterator without waiting for unacked - // messages. - iter := newMessageIterator(context.Background(), s.c.subc, s.name, po) - - // We cannot use errgroup from Receive here. Receive might already be calling group.Wait, - // and group.Wait cannot be called concurrently with group.Go. We give each receive() its - // own WaitGroup instead. - // Since wg.Add is only called from the main goroutine, wg.Wait is guaranteed - // to be called after all Adds. - var wg sync.WaitGroup - wg.Add(1) - go func() { - <-ctx2.Done() - iter.stop() - wg.Done() - }() - defer wg.Wait() - - defer cancel() - for { - msgs, err := iter.receive() - if err == io.EOF { - return nil - } - if err != nil { - return err - } - for i, msg := range msgs { - msg := msg - // TODO(jba): call acquire closer to when the message is allocated. - if err := fc.acquire(ctx, len(msg.Data)); err != nil { - // TODO(jba): test that these "orphaned" messages are nacked immediately when ctx is done. - for _, m := range msgs[i:] { - m.Nack() - } - return nil - } - old := msg.doneFunc - msgLen := len(msg.Data) - msg.doneFunc = func(ackID string, ack bool, receiveTime time.Time) { - defer fc.release(msgLen) - old(ackID, ack, receiveTime) - } - wg.Add(1) - go func() { - defer wg.Done() - f(ctx2, msg) - }() - } - } -} - -// TODO(jba): remove when we delete messageIterator. -type pullOptions struct { - maxExtension time.Duration - maxPrefetch int32 - // ackDeadline is the default ack deadline for the subscription. Not - // configurable. - ackDeadline time.Duration -} diff --git a/vendor/cloud.google.com/go/pubsub/topic.go b/vendor/cloud.google.com/go/pubsub/topic.go deleted file mode 100644 index 388d4510e6b..00000000000 --- a/vendor/cloud.google.com/go/pubsub/topic.go +++ /dev/null @@ -1,397 +0,0 @@ -// Copyright 2016 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 pubsub - -import ( - "errors" - "fmt" - "runtime" - "strings" - "sync" - "time" - - "cloud.google.com/go/iam" - "github.com/golang/protobuf/proto" - gax "github.com/googleapis/gax-go" - "golang.org/x/net/context" - "google.golang.org/api/support/bundler" - pb "google.golang.org/genproto/googleapis/pubsub/v1" - "google.golang.org/grpc" - "google.golang.org/grpc/codes" -) - -const ( - // The maximum number of messages that can be in a single publish request, as - // determined by the PubSub service. - MaxPublishRequestCount = 1000 - - // The maximum size of a single publish request in bytes, as determined by the PubSub service. - MaxPublishRequestBytes = 1e7 - - maxInt = int(^uint(0) >> 1) -) - -// ErrOversizedMessage indicates that a message's size exceeds MaxPublishRequestBytes. -var ErrOversizedMessage = bundler.ErrOversizedItem - -// Topic is a reference to a PubSub topic. -// -// The methods of Topic are safe for use by multiple goroutines. -type Topic struct { - c *Client - // The fully qualified identifier for the topic, in the format "projects//topics/" - name string - - // Settings for publishing messages. All changes must be made before the - // first call to Publish. The default is DefaultPublishSettings. - PublishSettings PublishSettings - - mu sync.RWMutex - stopped bool - bundler *bundler.Bundler - - wg sync.WaitGroup - - // Channel for message bundles to be published. Close to indicate that Stop was called. - bundlec chan []*bundledMessage -} - -// PublishSettings control the bundling of published messages. -type PublishSettings struct { - - // Publish a non-empty batch after this delay has passed. - DelayThreshold time.Duration - - // Publish a batch when it has this many messages. The maximum is - // MaxPublishRequestCount. - CountThreshold int - - // Publish a batch when its size in bytes reaches this value. - ByteThreshold int - - // The number of goroutines that invoke the Publish RPC concurrently. - // Defaults to a multiple of GOMAXPROCS. - NumGoroutines int - - // The maximum time that the client will attempt to publish a bundle of messages. - Timeout time.Duration -} - -// DefaultPublishSettings holds the default values for topics' PublishSettings. -var DefaultPublishSettings = PublishSettings{ - DelayThreshold: 1 * time.Millisecond, - CountThreshold: 100, - ByteThreshold: 1e6, - Timeout: 60 * time.Second, -} - -// CreateTopic creates a new topic. -// The specified topic ID must start with a letter, and contain only letters -// ([A-Za-z]), numbers ([0-9]), dashes (-), underscores (_), periods (.), -// tildes (~), plus (+) or percent signs (%). It must be between 3 and 255 -// characters in length, and must not start with "goog". -// If the topic already exists an error will be returned. -func (c *Client) CreateTopic(ctx context.Context, id string) (*Topic, error) { - t := c.Topic(id) - _, err := c.pubc.CreateTopic(ctx, &pb.Topic{Name: t.name}) - if err != nil { - return nil, err - } - return t, nil -} - -// Topic creates a reference to a topic in the client's project. -// -// If a Topic's Publish method is called, it has background goroutines -// associated with it. Clean them up by calling Topic.Stop. -// -// Avoid creating many Topic instances if you use them to publish. -func (c *Client) Topic(id string) *Topic { - return c.TopicInProject(id, c.projectID) -} - -// TopicInProject creates a reference to a topic in the given project. -// -// If a Topic's Publish method is called, it has background goroutines -// associated with it. Clean them up by calling Topic.Stop. -// -// Avoid creating many Topic instances if you use them to publish. -func (c *Client) TopicInProject(id, projectID string) *Topic { - return newTopic(c, fmt.Sprintf("projects/%s/topics/%s", projectID, id)) -} - -func newTopic(c *Client, name string) *Topic { - // bundlec is unbuffered. A buffer would occupy memory not - // accounted for by the bundler, so BufferedByteLimit would be a lie: - // the actual memory consumed would be higher. - return &Topic{ - c: c, - name: name, - PublishSettings: DefaultPublishSettings, - bundlec: make(chan []*bundledMessage), - } -} - -// Topics returns an iterator which returns all of the topics for the client's project. -func (c *Client) Topics(ctx context.Context) *TopicIterator { - it := c.pubc.ListTopics(ctx, &pb.ListTopicsRequest{Project: c.fullyQualifiedProjectName()}) - return &TopicIterator{ - c: c, - next: func() (string, error) { - topic, err := it.Next() - if err != nil { - return "", err - } - return topic.Name, nil - }, - } -} - -// TopicIterator is an iterator that returns a series of topics. -type TopicIterator struct { - c *Client - next func() (string, error) -} - -// Next returns the next topic. If there are no more topics, iterator.Done will be returned. -func (tps *TopicIterator) Next() (*Topic, error) { - topicName, err := tps.next() - if err != nil { - return nil, err - } - return newTopic(tps.c, topicName), nil -} - -// ID returns the unique idenfier of the topic within its project. -func (t *Topic) ID() string { - slash := strings.LastIndex(t.name, "/") - if slash == -1 { - // name is not a fully-qualified name. - panic("bad topic name") - } - return t.name[slash+1:] -} - -// String returns the printable globally unique name for the topic. -func (t *Topic) String() string { - return t.name -} - -// Delete deletes the topic. -func (t *Topic) Delete(ctx context.Context) error { - return t.c.pubc.DeleteTopic(ctx, &pb.DeleteTopicRequest{Topic: t.name}) -} - -// Exists reports whether the topic exists on the server. -func (t *Topic) Exists(ctx context.Context) (bool, error) { - if t.name == "_deleted-topic_" { - return false, nil - } - _, err := t.c.pubc.GetTopic(ctx, &pb.GetTopicRequest{Topic: t.name}) - if err == nil { - return true, nil - } - if grpc.Code(err) == codes.NotFound { - return false, nil - } - return false, err -} - -func (t *Topic) IAM() *iam.Handle { - return iam.InternalNewHandle(t.c.pubc.Connection(), t.name) -} - -// Subscriptions returns an iterator which returns the subscriptions for this topic. -// -// Some of the returned subscriptions may belong to a project other than t. -func (t *Topic) Subscriptions(ctx context.Context) *SubscriptionIterator { - it := t.c.pubc.ListTopicSubscriptions(ctx, &pb.ListTopicSubscriptionsRequest{ - Topic: t.name, - }) - return &SubscriptionIterator{ - c: t.c, - next: it.Next, - } -} - -var errTopicStopped = errors.New("pubsub: Stop has been called for this topic") - -// Publish publishes msg to the topic asynchronously. Messages are batched and -// sent according to the topic's PublishSettings. Publish never blocks. -// -// Publish returns a non-nil PublishResult which will be ready when the -// message has been sent (or has failed to be sent) to the server. -// -// Publish creates goroutines for batching and sending messages. These goroutines -// need to be stopped by calling t.Stop(). Once stopped, future calls to Publish -// will immediately return a PublishResult with an error. -func (t *Topic) Publish(ctx context.Context, msg *Message) *PublishResult { - // TODO(jba): if this turns out to take significant time, try to approximate it. - // Or, convert the messages to protos in Publish, instead of in the service. - msg.size = proto.Size(&pb.PubsubMessage{ - Data: msg.Data, - Attributes: msg.Attributes, - }) - r := &PublishResult{ready: make(chan struct{})} - t.initBundler() - t.mu.RLock() - defer t.mu.RUnlock() - // TODO(aboulhosn) [from bcmills] consider changing the semantics of bundler to perform this logic so we don't have to do it here - if t.stopped { - r.set("", errTopicStopped) - return r - } - - // TODO(jba) [from bcmills] consider using a shared channel per bundle - // (requires Bundler API changes; would reduce allocations) - // The call to Add should never return an error because the bundler's - // BufferedByteLimit is set to maxInt; we do not perform any flow - // control in the client. - err := t.bundler.Add(&bundledMessage{msg, r}, msg.size) - if err != nil { - r.set("", err) - } - return r -} - -// Send all remaining published messages and stop goroutines created for handling -// publishing. Returns once all outstanding messages have been sent or have -// failed to be sent. -func (t *Topic) Stop() { - t.mu.Lock() - noop := t.stopped || t.bundler == nil - t.stopped = true - t.mu.Unlock() - if noop { - return - } - t.bundler.Flush() - // At this point, all pending bundles have been published and the bundler's - // goroutines have exited, so it is OK for this goroutine to close bundlec. - close(t.bundlec) - t.wg.Wait() -} - -// A PublishResult holds the result from a call to Publish. -type PublishResult struct { - ready chan struct{} - serverID string - err error -} - -// Ready returns a channel that is closed when the result is ready. -// When the Ready channel is closed, Get is guaranteed not to block. -func (r *PublishResult) Ready() <-chan struct{} { return r.ready } - -// Get returns the server-generated message ID and/or error result of a Publish call. -// Get blocks until the Publish call completes or the context is done. -func (r *PublishResult) Get(ctx context.Context) (serverID string, err error) { - // If the result is already ready, return it even if the context is done. - select { - case <-r.Ready(): - return r.serverID, r.err - default: - } - select { - case <-ctx.Done(): - return "", ctx.Err() - case <-r.Ready(): - return r.serverID, r.err - } -} - -func (r *PublishResult) set(sid string, err error) { - r.serverID = sid - r.err = err - close(r.ready) -} - -type bundledMessage struct { - msg *Message - res *PublishResult -} - -func (t *Topic) initBundler() { - t.mu.RLock() - noop := t.stopped || t.bundler != nil - t.mu.RUnlock() - if noop { - return - } - t.mu.Lock() - defer t.mu.Unlock() - // Must re-check, since we released the lock. - if t.stopped || t.bundler != nil { - return - } - - // TODO(jba): use a context detached from the one passed to NewClient. - ctx := context.TODO() - // Unless overridden, run several goroutines per CPU to call the Publish RPC. - n := t.PublishSettings.NumGoroutines - if n <= 0 { - n = 25 * runtime.GOMAXPROCS(0) - } - timeout := t.PublishSettings.Timeout - t.wg.Add(n) - for i := 0; i < n; i++ { - go func() { - defer t.wg.Done() - for b := range t.bundlec { - bctx := ctx - cancel := func() {} - if timeout != 0 { - bctx, cancel = context.WithTimeout(ctx, timeout) - } - t.publishMessageBundle(bctx, b) - cancel() - } - }() - } - t.bundler = bundler.NewBundler(&bundledMessage{}, func(items interface{}) { - t.bundlec <- items.([]*bundledMessage) - - }) - t.bundler.DelayThreshold = t.PublishSettings.DelayThreshold - t.bundler.BundleCountThreshold = t.PublishSettings.CountThreshold - if t.bundler.BundleCountThreshold > MaxPublishRequestCount { - t.bundler.BundleCountThreshold = MaxPublishRequestCount - } - t.bundler.BundleByteThreshold = t.PublishSettings.ByteThreshold - t.bundler.BufferedByteLimit = maxInt - t.bundler.BundleByteLimit = MaxPublishRequestBytes -} - -func (t *Topic) publishMessageBundle(ctx context.Context, bms []*bundledMessage) { - pbMsgs := make([]*pb.PubsubMessage, len(bms)) - for i, bm := range bms { - pbMsgs[i] = &pb.PubsubMessage{ - Data: bm.msg.Data, - Attributes: bm.msg.Attributes, - } - bm.msg = nil // release bm.msg for GC - } - res, err := t.c.pubc.Publish(ctx, &pb.PublishRequest{ - Topic: t.name, - Messages: pbMsgs, - }, gax.WithGRPCOptions(grpc.MaxCallSendMsgSize(maxSendRecvBytes))) - for i, bm := range bms { - if err != nil { - bm.res.set("", err) - } else { - bm.res.set(res.MessageIds[i], nil) - } - } -} diff --git a/vendor/github.com/Shopify/sarama/LICENSE b/vendor/github.com/Shopify/sarama/LICENSE deleted file mode 100644 index d2bf4352f4c..00000000000 --- a/vendor/github.com/Shopify/sarama/LICENSE +++ /dev/null @@ -1,20 +0,0 @@ -Copyright (c) 2013 Shopify - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/Shopify/sarama/acl_bindings.go b/vendor/github.com/Shopify/sarama/acl_bindings.go deleted file mode 100644 index 51517359abc..00000000000 --- a/vendor/github.com/Shopify/sarama/acl_bindings.go +++ /dev/null @@ -1,119 +0,0 @@ -package sarama - -type Resource struct { - ResourceType AclResourceType - ResourceName string -} - -func (r *Resource) encode(pe packetEncoder) error { - pe.putInt8(int8(r.ResourceType)) - - if err := pe.putString(r.ResourceName); err != nil { - return err - } - - return nil -} - -func (r *Resource) decode(pd packetDecoder, version int16) (err error) { - resourceType, err := pd.getInt8() - if err != nil { - return err - } - r.ResourceType = AclResourceType(resourceType) - - if r.ResourceName, err = pd.getString(); err != nil { - return err - } - - return nil -} - -type Acl struct { - Principal string - Host string - Operation AclOperation - PermissionType AclPermissionType -} - -func (a *Acl) encode(pe packetEncoder) error { - if err := pe.putString(a.Principal); err != nil { - return err - } - - if err := pe.putString(a.Host); err != nil { - return err - } - - pe.putInt8(int8(a.Operation)) - pe.putInt8(int8(a.PermissionType)) - - return nil -} - -func (a *Acl) decode(pd packetDecoder, version int16) (err error) { - if a.Principal, err = pd.getString(); err != nil { - return err - } - - if a.Host, err = pd.getString(); err != nil { - return err - } - - operation, err := pd.getInt8() - if err != nil { - return err - } - a.Operation = AclOperation(operation) - - permissionType, err := pd.getInt8() - if err != nil { - return err - } - a.PermissionType = AclPermissionType(permissionType) - - return nil -} - -type ResourceAcls struct { - Resource - Acls []*Acl -} - -func (r *ResourceAcls) encode(pe packetEncoder) error { - if err := r.Resource.encode(pe); err != nil { - return err - } - - if err := pe.putArrayLength(len(r.Acls)); err != nil { - return err - } - for _, acl := range r.Acls { - if err := acl.encode(pe); err != nil { - return err - } - } - - return nil -} - -func (r *ResourceAcls) decode(pd packetDecoder, version int16) error { - if err := r.Resource.decode(pd, version); err != nil { - return err - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - r.Acls = make([]*Acl, n) - for i := 0; i < n; i++ { - r.Acls[i] = new(Acl) - if err := r.Acls[i].decode(pd, version); err != nil { - return err - } - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/acl_create_request.go b/vendor/github.com/Shopify/sarama/acl_create_request.go deleted file mode 100644 index 0b6ecbec3e1..00000000000 --- a/vendor/github.com/Shopify/sarama/acl_create_request.go +++ /dev/null @@ -1,76 +0,0 @@ -package sarama - -type CreateAclsRequest struct { - AclCreations []*AclCreation -} - -func (c *CreateAclsRequest) encode(pe packetEncoder) error { - if err := pe.putArrayLength(len(c.AclCreations)); err != nil { - return err - } - - for _, aclCreation := range c.AclCreations { - if err := aclCreation.encode(pe); err != nil { - return err - } - } - - return nil -} - -func (c *CreateAclsRequest) decode(pd packetDecoder, version int16) (err error) { - n, err := pd.getArrayLength() - if err != nil { - return err - } - - c.AclCreations = make([]*AclCreation, n) - - for i := 0; i < n; i++ { - c.AclCreations[i] = new(AclCreation) - if err := c.AclCreations[i].decode(pd, version); err != nil { - return err - } - } - - return nil -} - -func (d *CreateAclsRequest) key() int16 { - return 30 -} - -func (d *CreateAclsRequest) version() int16 { - return 0 -} - -func (d *CreateAclsRequest) requiredVersion() KafkaVersion { - return V0_11_0_0 -} - -type AclCreation struct { - Resource - Acl -} - -func (a *AclCreation) encode(pe packetEncoder) error { - if err := a.Resource.encode(pe); err != nil { - return err - } - if err := a.Acl.encode(pe); err != nil { - return err - } - - return nil -} - -func (a *AclCreation) decode(pd packetDecoder, version int16) (err error) { - if err := a.Resource.decode(pd, version); err != nil { - return err - } - if err := a.Acl.decode(pd, version); err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/acl_create_response.go b/vendor/github.com/Shopify/sarama/acl_create_response.go deleted file mode 100644 index 8a56f357354..00000000000 --- a/vendor/github.com/Shopify/sarama/acl_create_response.go +++ /dev/null @@ -1,88 +0,0 @@ -package sarama - -import "time" - -type CreateAclsResponse struct { - ThrottleTime time.Duration - AclCreationResponses []*AclCreationResponse -} - -func (c *CreateAclsResponse) encode(pe packetEncoder) error { - pe.putInt32(int32(c.ThrottleTime / time.Millisecond)) - - if err := pe.putArrayLength(len(c.AclCreationResponses)); err != nil { - return err - } - - for _, aclCreationResponse := range c.AclCreationResponses { - if err := aclCreationResponse.encode(pe); err != nil { - return err - } - } - - return nil -} - -func (c *CreateAclsResponse) decode(pd packetDecoder, version int16) (err error) { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - c.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - c.AclCreationResponses = make([]*AclCreationResponse, n) - for i := 0; i < n; i++ { - c.AclCreationResponses[i] = new(AclCreationResponse) - if err := c.AclCreationResponses[i].decode(pd, version); err != nil { - return err - } - } - - return nil -} - -func (d *CreateAclsResponse) key() int16 { - return 30 -} - -func (d *CreateAclsResponse) version() int16 { - return 0 -} - -func (d *CreateAclsResponse) requiredVersion() KafkaVersion { - return V0_11_0_0 -} - -type AclCreationResponse struct { - Err KError - ErrMsg *string -} - -func (a *AclCreationResponse) encode(pe packetEncoder) error { - pe.putInt16(int16(a.Err)) - - if err := pe.putNullableString(a.ErrMsg); err != nil { - return err - } - - return nil -} - -func (a *AclCreationResponse) decode(pd packetDecoder, version int16) (err error) { - kerr, err := pd.getInt16() - if err != nil { - return err - } - a.Err = KError(kerr) - - if a.ErrMsg, err = pd.getNullableString(); err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/acl_delete_request.go b/vendor/github.com/Shopify/sarama/acl_delete_request.go deleted file mode 100644 index 4133dceab71..00000000000 --- a/vendor/github.com/Shopify/sarama/acl_delete_request.go +++ /dev/null @@ -1,48 +0,0 @@ -package sarama - -type DeleteAclsRequest struct { - Filters []*AclFilter -} - -func (d *DeleteAclsRequest) encode(pe packetEncoder) error { - if err := pe.putArrayLength(len(d.Filters)); err != nil { - return err - } - - for _, filter := range d.Filters { - if err := filter.encode(pe); err != nil { - return err - } - } - - return nil -} - -func (d *DeleteAclsRequest) decode(pd packetDecoder, version int16) (err error) { - n, err := pd.getArrayLength() - if err != nil { - return err - } - - d.Filters = make([]*AclFilter, n) - for i := 0; i < n; i++ { - d.Filters[i] = new(AclFilter) - if err := d.Filters[i].decode(pd, version); err != nil { - return err - } - } - - return nil -} - -func (d *DeleteAclsRequest) key() int16 { - return 31 -} - -func (d *DeleteAclsRequest) version() int16 { - return 0 -} - -func (d *DeleteAclsRequest) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/acl_delete_response.go b/vendor/github.com/Shopify/sarama/acl_delete_response.go deleted file mode 100644 index b5e1c45eb5d..00000000000 --- a/vendor/github.com/Shopify/sarama/acl_delete_response.go +++ /dev/null @@ -1,155 +0,0 @@ -package sarama - -import "time" - -type DeleteAclsResponse struct { - ThrottleTime time.Duration - FilterResponses []*FilterResponse -} - -func (a *DeleteAclsResponse) encode(pe packetEncoder) error { - pe.putInt32(int32(a.ThrottleTime / time.Millisecond)) - - if err := pe.putArrayLength(len(a.FilterResponses)); err != nil { - return err - } - - for _, filterResponse := range a.FilterResponses { - if err := filterResponse.encode(pe); err != nil { - return err - } - } - - return nil -} - -func (a *DeleteAclsResponse) decode(pd packetDecoder, version int16) (err error) { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - a.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - n, err := pd.getArrayLength() - if err != nil { - return err - } - a.FilterResponses = make([]*FilterResponse, n) - - for i := 0; i < n; i++ { - a.FilterResponses[i] = new(FilterResponse) - if err := a.FilterResponses[i].decode(pd, version); err != nil { - return err - } - } - - return nil -} - -func (d *DeleteAclsResponse) key() int16 { - return 31 -} - -func (d *DeleteAclsResponse) version() int16 { - return 0 -} - -func (d *DeleteAclsResponse) requiredVersion() KafkaVersion { - return V0_11_0_0 -} - -type FilterResponse struct { - Err KError - ErrMsg *string - MatchingAcls []*MatchingAcl -} - -func (f *FilterResponse) encode(pe packetEncoder) error { - pe.putInt16(int16(f.Err)) - if err := pe.putNullableString(f.ErrMsg); err != nil { - return err - } - - if err := pe.putArrayLength(len(f.MatchingAcls)); err != nil { - return err - } - for _, matchingAcl := range f.MatchingAcls { - if err := matchingAcl.encode(pe); err != nil { - return err - } - } - - return nil -} - -func (f *FilterResponse) decode(pd packetDecoder, version int16) (err error) { - kerr, err := pd.getInt16() - if err != nil { - return err - } - f.Err = KError(kerr) - - if f.ErrMsg, err = pd.getNullableString(); err != nil { - return err - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - f.MatchingAcls = make([]*MatchingAcl, n) - for i := 0; i < n; i++ { - f.MatchingAcls[i] = new(MatchingAcl) - if err := f.MatchingAcls[i].decode(pd, version); err != nil { - return err - } - } - - return nil -} - -type MatchingAcl struct { - Err KError - ErrMsg *string - Resource - Acl -} - -func (m *MatchingAcl) encode(pe packetEncoder) error { - pe.putInt16(int16(m.Err)) - if err := pe.putNullableString(m.ErrMsg); err != nil { - return err - } - - if err := m.Resource.encode(pe); err != nil { - return err - } - - if err := m.Acl.encode(pe); err != nil { - return err - } - - return nil -} - -func (m *MatchingAcl) decode(pd packetDecoder, version int16) (err error) { - kerr, err := pd.getInt16() - if err != nil { - return err - } - m.Err = KError(kerr) - - if m.ErrMsg, err = pd.getNullableString(); err != nil { - return err - } - - if err := m.Resource.decode(pd, version); err != nil { - return err - } - - if err := m.Acl.decode(pd, version); err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/acl_describe_request.go b/vendor/github.com/Shopify/sarama/acl_describe_request.go deleted file mode 100644 index 02a5a1f0e22..00000000000 --- a/vendor/github.com/Shopify/sarama/acl_describe_request.go +++ /dev/null @@ -1,25 +0,0 @@ -package sarama - -type DescribeAclsRequest struct { - AclFilter -} - -func (d *DescribeAclsRequest) encode(pe packetEncoder) error { - return d.AclFilter.encode(pe) -} - -func (d *DescribeAclsRequest) decode(pd packetDecoder, version int16) (err error) { - return d.AclFilter.decode(pd, version) -} - -func (d *DescribeAclsRequest) key() int16 { - return 29 -} - -func (d *DescribeAclsRequest) version() int16 { - return 0 -} - -func (d *DescribeAclsRequest) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/acl_describe_response.go b/vendor/github.com/Shopify/sarama/acl_describe_response.go deleted file mode 100644 index 5bc9497f4c5..00000000000 --- a/vendor/github.com/Shopify/sarama/acl_describe_response.go +++ /dev/null @@ -1,80 +0,0 @@ -package sarama - -import "time" - -type DescribeAclsResponse struct { - ThrottleTime time.Duration - Err KError - ErrMsg *string - ResourceAcls []*ResourceAcls -} - -func (d *DescribeAclsResponse) encode(pe packetEncoder) error { - pe.putInt32(int32(d.ThrottleTime / time.Millisecond)) - pe.putInt16(int16(d.Err)) - - if err := pe.putNullableString(d.ErrMsg); err != nil { - return err - } - - if err := pe.putArrayLength(len(d.ResourceAcls)); err != nil { - return err - } - - for _, resourceAcl := range d.ResourceAcls { - if err := resourceAcl.encode(pe); err != nil { - return err - } - } - - return nil -} - -func (d *DescribeAclsResponse) decode(pd packetDecoder, version int16) (err error) { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - d.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - kerr, err := pd.getInt16() - if err != nil { - return err - } - d.Err = KError(kerr) - - errmsg, err := pd.getString() - if err != nil { - return err - } - if errmsg != "" { - d.ErrMsg = &errmsg - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - d.ResourceAcls = make([]*ResourceAcls, n) - - for i := 0; i < n; i++ { - d.ResourceAcls[i] = new(ResourceAcls) - if err := d.ResourceAcls[i].decode(pd, version); err != nil { - return err - } - } - - return nil -} - -func (d *DescribeAclsResponse) key() int16 { - return 29 -} - -func (d *DescribeAclsResponse) version() int16 { - return 0 -} - -func (d *DescribeAclsResponse) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/acl_filter.go b/vendor/github.com/Shopify/sarama/acl_filter.go deleted file mode 100644 index 97063542198..00000000000 --- a/vendor/github.com/Shopify/sarama/acl_filter.go +++ /dev/null @@ -1,61 +0,0 @@ -package sarama - -type AclFilter struct { - ResourceType AclResourceType - ResourceName *string - Principal *string - Host *string - Operation AclOperation - PermissionType AclPermissionType -} - -func (a *AclFilter) encode(pe packetEncoder) error { - pe.putInt8(int8(a.ResourceType)) - if err := pe.putNullableString(a.ResourceName); err != nil { - return err - } - if err := pe.putNullableString(a.Principal); err != nil { - return err - } - if err := pe.putNullableString(a.Host); err != nil { - return err - } - pe.putInt8(int8(a.Operation)) - pe.putInt8(int8(a.PermissionType)) - - return nil -} - -func (a *AclFilter) decode(pd packetDecoder, version int16) (err error) { - resourceType, err := pd.getInt8() - if err != nil { - return err - } - a.ResourceType = AclResourceType(resourceType) - - if a.ResourceName, err = pd.getNullableString(); err != nil { - return err - } - - if a.Principal, err = pd.getNullableString(); err != nil { - return err - } - - if a.Host, err = pd.getNullableString(); err != nil { - return err - } - - operation, err := pd.getInt8() - if err != nil { - return err - } - a.Operation = AclOperation(operation) - - permissionType, err := pd.getInt8() - if err != nil { - return err - } - a.PermissionType = AclPermissionType(permissionType) - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/acl_types.go b/vendor/github.com/Shopify/sarama/acl_types.go deleted file mode 100644 index 19da6f2f451..00000000000 --- a/vendor/github.com/Shopify/sarama/acl_types.go +++ /dev/null @@ -1,42 +0,0 @@ -package sarama - -type AclOperation int - -// ref: https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/acl/AclOperation.java -const ( - AclOperationUnknown AclOperation = 0 - AclOperationAny AclOperation = 1 - AclOperationAll AclOperation = 2 - AclOperationRead AclOperation = 3 - AclOperationWrite AclOperation = 4 - AclOperationCreate AclOperation = 5 - AclOperationDelete AclOperation = 6 - AclOperationAlter AclOperation = 7 - AclOperationDescribe AclOperation = 8 - AclOperationClusterAction AclOperation = 9 - AclOperationDescribeConfigs AclOperation = 10 - AclOperationAlterConfigs AclOperation = 11 - AclOperationIdempotentWrite AclOperation = 12 -) - -type AclPermissionType int - -// ref: https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/acl/AclPermissionType.java -const ( - AclPermissionUnknown AclPermissionType = 0 - AclPermissionAny AclPermissionType = 1 - AclPermissionDeny AclPermissionType = 2 - AclPermissionAllow AclPermissionType = 3 -) - -type AclResourceType int - -// ref: https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/resource/ResourceType.java -const ( - AclResourceUnknown AclResourceType = 0 - AclResourceAny AclResourceType = 1 - AclResourceTopic AclResourceType = 2 - AclResourceGroup AclResourceType = 3 - AclResourceCluster AclResourceType = 4 - AclResourceTransactionalID AclResourceType = 5 -) diff --git a/vendor/github.com/Shopify/sarama/add_offsets_to_txn_request.go b/vendor/github.com/Shopify/sarama/add_offsets_to_txn_request.go deleted file mode 100644 index 6da166c634b..00000000000 --- a/vendor/github.com/Shopify/sarama/add_offsets_to_txn_request.go +++ /dev/null @@ -1,52 +0,0 @@ -package sarama - -type AddOffsetsToTxnRequest struct { - TransactionalID string - ProducerID int64 - ProducerEpoch int16 - GroupID string -} - -func (a *AddOffsetsToTxnRequest) encode(pe packetEncoder) error { - if err := pe.putString(a.TransactionalID); err != nil { - return err - } - - pe.putInt64(a.ProducerID) - - pe.putInt16(a.ProducerEpoch) - - if err := pe.putString(a.GroupID); err != nil { - return err - } - - return nil -} - -func (a *AddOffsetsToTxnRequest) decode(pd packetDecoder, version int16) (err error) { - if a.TransactionalID, err = pd.getString(); err != nil { - return err - } - if a.ProducerID, err = pd.getInt64(); err != nil { - return err - } - if a.ProducerEpoch, err = pd.getInt16(); err != nil { - return err - } - if a.GroupID, err = pd.getString(); err != nil { - return err - } - return nil -} - -func (a *AddOffsetsToTxnRequest) key() int16 { - return 25 -} - -func (a *AddOffsetsToTxnRequest) version() int16 { - return 0 -} - -func (a *AddOffsetsToTxnRequest) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/add_offsets_to_txn_response.go b/vendor/github.com/Shopify/sarama/add_offsets_to_txn_response.go deleted file mode 100644 index 3a46151a050..00000000000 --- a/vendor/github.com/Shopify/sarama/add_offsets_to_txn_response.go +++ /dev/null @@ -1,44 +0,0 @@ -package sarama - -import ( - "time" -) - -type AddOffsetsToTxnResponse struct { - ThrottleTime time.Duration - Err KError -} - -func (a *AddOffsetsToTxnResponse) encode(pe packetEncoder) error { - pe.putInt32(int32(a.ThrottleTime / time.Millisecond)) - pe.putInt16(int16(a.Err)) - return nil -} - -func (a *AddOffsetsToTxnResponse) decode(pd packetDecoder, version int16) (err error) { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - a.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - kerr, err := pd.getInt16() - if err != nil { - return err - } - a.Err = KError(kerr) - - return nil -} - -func (a *AddOffsetsToTxnResponse) key() int16 { - return 25 -} - -func (a *AddOffsetsToTxnResponse) version() int16 { - return 0 -} - -func (a *AddOffsetsToTxnResponse) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/add_partitions_to_txn_request.go b/vendor/github.com/Shopify/sarama/add_partitions_to_txn_request.go deleted file mode 100644 index a8a59225e4d..00000000000 --- a/vendor/github.com/Shopify/sarama/add_partitions_to_txn_request.go +++ /dev/null @@ -1,76 +0,0 @@ -package sarama - -type AddPartitionsToTxnRequest struct { - TransactionalID string - ProducerID int64 - ProducerEpoch int16 - TopicPartitions map[string][]int32 -} - -func (a *AddPartitionsToTxnRequest) encode(pe packetEncoder) error { - if err := pe.putString(a.TransactionalID); err != nil { - return err - } - pe.putInt64(a.ProducerID) - pe.putInt16(a.ProducerEpoch) - - if err := pe.putArrayLength(len(a.TopicPartitions)); err != nil { - return err - } - for topic, partitions := range a.TopicPartitions { - if err := pe.putString(topic); err != nil { - return err - } - if err := pe.putInt32Array(partitions); err != nil { - return err - } - } - - return nil -} - -func (a *AddPartitionsToTxnRequest) decode(pd packetDecoder, version int16) (err error) { - if a.TransactionalID, err = pd.getString(); err != nil { - return err - } - if a.ProducerID, err = pd.getInt64(); err != nil { - return err - } - if a.ProducerEpoch, err = pd.getInt16(); err != nil { - return err - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - a.TopicPartitions = make(map[string][]int32) - for i := 0; i < n; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - - partitions, err := pd.getInt32Array() - if err != nil { - return err - } - - a.TopicPartitions[topic] = partitions - } - - return nil -} - -func (a *AddPartitionsToTxnRequest) key() int16 { - return 24 -} - -func (a *AddPartitionsToTxnRequest) version() int16 { - return 0 -} - -func (a *AddPartitionsToTxnRequest) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/add_partitions_to_txn_response.go b/vendor/github.com/Shopify/sarama/add_partitions_to_txn_response.go deleted file mode 100644 index 581c556c5ce..00000000000 --- a/vendor/github.com/Shopify/sarama/add_partitions_to_txn_response.go +++ /dev/null @@ -1,108 +0,0 @@ -package sarama - -import ( - "time" -) - -type AddPartitionsToTxnResponse struct { - ThrottleTime time.Duration - Errors map[string][]*PartitionError -} - -func (a *AddPartitionsToTxnResponse) encode(pe packetEncoder) error { - pe.putInt32(int32(a.ThrottleTime / time.Millisecond)) - if err := pe.putArrayLength(len(a.Errors)); err != nil { - return err - } - - for topic, e := range a.Errors { - if err := pe.putString(topic); err != nil { - return err - } - if err := pe.putArrayLength(len(e)); err != nil { - return err - } - for _, partitionError := range e { - if err := partitionError.encode(pe); err != nil { - return err - } - } - } - - return nil -} - -func (a *AddPartitionsToTxnResponse) decode(pd packetDecoder, version int16) (err error) { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - a.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - a.Errors = make(map[string][]*PartitionError) - - for i := 0; i < n; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - - m, err := pd.getArrayLength() - if err != nil { - return err - } - - a.Errors[topic] = make([]*PartitionError, m) - - for j := 0; j < m; j++ { - a.Errors[topic][j] = new(PartitionError) - if err := a.Errors[topic][j].decode(pd, version); err != nil { - return err - } - } - } - - return nil -} - -func (a *AddPartitionsToTxnResponse) key() int16 { - return 24 -} - -func (a *AddPartitionsToTxnResponse) version() int16 { - return 0 -} - -func (a *AddPartitionsToTxnResponse) requiredVersion() KafkaVersion { - return V0_11_0_0 -} - -type PartitionError struct { - Partition int32 - Err KError -} - -func (p *PartitionError) encode(pe packetEncoder) error { - pe.putInt32(p.Partition) - pe.putInt16(int16(p.Err)) - return nil -} - -func (p *PartitionError) decode(pd packetDecoder, version int16) (err error) { - if p.Partition, err = pd.getInt32(); err != nil { - return err - } - - kerr, err := pd.getInt16() - if err != nil { - return err - } - p.Err = KError(kerr) - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/admin.go b/vendor/github.com/Shopify/sarama/admin.go deleted file mode 100644 index 68284641c82..00000000000 --- a/vendor/github.com/Shopify/sarama/admin.go +++ /dev/null @@ -1,375 +0,0 @@ -package sarama - -import "errors" - -// ClusterAdmin is the administrative client for Kafka, which supports managing and inspecting topics, -// brokers, configurations and ACLs. The minimum broker version required is 0.10.0.0. -// Methods with stricter requirements will specify the minimum broker version required. -// You MUST call Close() on a client to avoid leaks -type ClusterAdmin interface { - // Creates a new topic. This operation is supported by brokers with version 0.10.1.0 or higher. - // It may take several seconds after CreateTopic returns success for all the brokers - // to become aware that the topic has been created. During this time, listTopics - // may not return information about the new topic.The validateOnly option is supported from version 0.10.2.0. - CreateTopic(topic string, detail *TopicDetail, validateOnly bool) error - - // Delete a topic. It may take several seconds after the DeleteTopic to returns success - // and for all the brokers to become aware that the topics are gone. - // During this time, listTopics may continue to return information about the deleted topic. - // If delete.topic.enable is false on the brokers, deleteTopic will mark - // the topic for deletion, but not actually delete them. - // This operation is supported by brokers with version 0.10.1.0 or higher. - DeleteTopic(topic string) error - - // Increase the number of partitions of the topics according to the corresponding values. - // If partitions are increased for a topic that has a key, the partition logic or ordering of - // the messages will be affected. It may take several seconds after this method returns - // success for all the brokers to become aware that the partitions have been created. - // During this time, ClusterAdmin#describeTopics may not return information about the - // new partitions. This operation is supported by brokers with version 1.0.0 or higher. - CreatePartitions(topic string, count int32, assignment [][]int32, validateOnly bool) error - - // Delete records whose offset is smaller than the given offset of the corresponding partition. - // This operation is supported by brokers with version 0.11.0.0 or higher. - DeleteRecords(topic string, partitionOffsets map[int32]int64) error - - // Get the configuration for the specified resources. - // The returned configuration includes default values and the Default is true - // can be used to distinguish them from user supplied values. - // Config entries where ReadOnly is true cannot be updated. - // The value of config entries where Sensitive is true is always nil so - // sensitive information is not disclosed. - // This operation is supported by brokers with version 0.11.0.0 or higher. - DescribeConfig(resource ConfigResource) ([]ConfigEntry, error) - - // Update the configuration for the specified resources with the default options. - // This operation is supported by brokers with version 0.11.0.0 or higher. - // The resources with their configs (topic is the only resource type with configs - // that can be updated currently Updates are not transactional so they may succeed - // for some resources while fail for others. The configs for a particular resource are updated automatically. - AlterConfig(resourceType ConfigResourceType, name string, entries map[string]*string, validateOnly bool) error - - // Creates access control lists (ACLs) which are bound to specific resources. - // This operation is not transactional so it may succeed for some ACLs while fail for others. - // If you attempt to add an ACL that duplicates an existing ACL, no error will be raised, but - // no changes will be made. This operation is supported by brokers with version 0.11.0.0 or higher. - CreateACL(resource Resource, acl Acl) error - - // Lists access control lists (ACLs) according to the supplied filter. - // it may take some time for changes made by createAcls or deleteAcls to be reflected in the output of ListAcls - // This operation is supported by brokers with version 0.11.0.0 or higher. - ListAcls(filter AclFilter) ([]ResourceAcls, error) - - // Deletes access control lists (ACLs) according to the supplied filters. - // This operation is not transactional so it may succeed for some ACLs while fail for others. - // This operation is supported by brokers with version 0.11.0.0 or higher. - DeleteACL(filter AclFilter, validateOnly bool) ([]MatchingAcl, error) - - // Close shuts down the admin and closes underlying client. - Close() error -} - -type clusterAdmin struct { - client Client - conf *Config -} - -// NewClusterAdmin creates a new ClusterAdmin using the given broker addresses and configuration. -func NewClusterAdmin(addrs []string, conf *Config) (ClusterAdmin, error) { - client, err := NewClient(addrs, conf) - if err != nil { - return nil, err - } - - //make sure we can retrieve the controller - _, err = client.Controller() - if err != nil { - return nil, err - } - - ca := &clusterAdmin{ - client: client, - conf: client.Config(), - } - return ca, nil -} - -func (ca *clusterAdmin) Close() error { - return ca.client.Close() -} - -func (ca *clusterAdmin) Controller() (*Broker, error) { - return ca.client.Controller() -} - -func (ca *clusterAdmin) CreateTopic(topic string, detail *TopicDetail, validateOnly bool) error { - - if topic == "" { - return ErrInvalidTopic - } - - if detail == nil { - return errors.New("You must specify topic details") - } - - topicDetails := make(map[string]*TopicDetail) - topicDetails[topic] = detail - - request := &CreateTopicsRequest{ - TopicDetails: topicDetails, - ValidateOnly: validateOnly, - } - - if ca.conf.Version.IsAtLeast(V0_11_0_0) { - request.Version = 1 - } - if ca.conf.Version.IsAtLeast(V1_0_0_0) { - request.Version = 2 - } - - b, err := ca.Controller() - if err != nil { - return err - } - - rsp, err := b.CreateTopics(request) - if err != nil { - return err - } - - topicErr, ok := rsp.TopicErrors[topic] - if !ok { - return ErrIncompleteResponse - } - - if topicErr.Err != ErrNoError { - return topicErr.Err - } - - return nil -} - -func (ca *clusterAdmin) DeleteTopic(topic string) error { - - if topic == "" { - return ErrInvalidTopic - } - - request := &DeleteTopicsRequest{Topics: []string{topic}} - - if ca.conf.Version.IsAtLeast(V0_11_0_0) { - request.Version = 1 - } - - b, err := ca.Controller() - if err != nil { - return err - } - - rsp, err := b.DeleteTopics(request) - if err != nil { - return err - } - - topicErr, ok := rsp.TopicErrorCodes[topic] - if !ok { - return ErrIncompleteResponse - } - - if topicErr != ErrNoError { - return topicErr - } - return nil -} - -func (ca *clusterAdmin) CreatePartitions(topic string, count int32, assignment [][]int32, validateOnly bool) error { - if topic == "" { - return ErrInvalidTopic - } - - topicPartitions := make(map[string]*TopicPartition) - topicPartitions[topic] = &TopicPartition{Count: count, Assignment: assignment} - - request := &CreatePartitionsRequest{ - TopicPartitions: topicPartitions, - } - - b, err := ca.Controller() - if err != nil { - return err - } - - rsp, err := b.CreatePartitions(request) - if err != nil { - return err - } - - topicErr, ok := rsp.TopicPartitionErrors[topic] - if !ok { - return ErrIncompleteResponse - } - - if topicErr.Err != ErrNoError { - return topicErr.Err - } - - return nil -} - -func (ca *clusterAdmin) DeleteRecords(topic string, partitionOffsets map[int32]int64) error { - - if topic == "" { - return ErrInvalidTopic - } - - topics := make(map[string]*DeleteRecordsRequestTopic) - topics[topic] = &DeleteRecordsRequestTopic{PartitionOffsets: partitionOffsets} - request := &DeleteRecordsRequest{ - Topics: topics} - - b, err := ca.Controller() - if err != nil { - return err - } - - rsp, err := b.DeleteRecords(request) - if err != nil { - return err - } - - _, ok := rsp.Topics[topic] - if !ok { - return ErrIncompleteResponse - } - - //todo since we are dealing with couple of partitions it would be good if we return slice of errors - //for each partition instead of one error - return nil -} - -func (ca *clusterAdmin) DescribeConfig(resource ConfigResource) ([]ConfigEntry, error) { - - var entries []ConfigEntry - var resources []*ConfigResource - resources = append(resources, &resource) - - request := &DescribeConfigsRequest{ - Resources: resources, - } - - b, err := ca.Controller() - if err != nil { - return nil, err - } - - rsp, err := b.DescribeConfigs(request) - if err != nil { - return nil, err - } - - for _, rspResource := range rsp.Resources { - if rspResource.Name == resource.Name { - if rspResource.ErrorMsg != "" { - return nil, errors.New(rspResource.ErrorMsg) - } - for _, cfgEntry := range rspResource.Configs { - entries = append(entries, *cfgEntry) - } - } - } - return entries, nil -} - -func (ca *clusterAdmin) AlterConfig(resourceType ConfigResourceType, name string, entries map[string]*string, validateOnly bool) error { - - var resources []*AlterConfigsResource - resources = append(resources, &AlterConfigsResource{ - Type: resourceType, - Name: name, - ConfigEntries: entries, - }) - - request := &AlterConfigsRequest{ - Resources: resources, - ValidateOnly: validateOnly, - } - - b, err := ca.Controller() - if err != nil { - return err - } - - rsp, err := b.AlterConfigs(request) - if err != nil { - return err - } - - for _, rspResource := range rsp.Resources { - if rspResource.Name == name { - if rspResource.ErrorMsg != "" { - return errors.New(rspResource.ErrorMsg) - } - } - } - return nil -} - -func (ca *clusterAdmin) CreateACL(resource Resource, acl Acl) error { - var acls []*AclCreation - acls = append(acls, &AclCreation{resource, acl}) - request := &CreateAclsRequest{AclCreations: acls} - - b, err := ca.Controller() - if err != nil { - return err - } - - _, err = b.CreateAcls(request) - return err -} - -func (ca *clusterAdmin) ListAcls(filter AclFilter) ([]ResourceAcls, error) { - - request := &DescribeAclsRequest{AclFilter: filter} - - b, err := ca.Controller() - if err != nil { - return nil, err - } - - rsp, err := b.DescribeAcls(request) - if err != nil { - return nil, err - } - - var lAcls []ResourceAcls - for _, rAcl := range rsp.ResourceAcls { - lAcls = append(lAcls, *rAcl) - } - return lAcls, nil -} - -func (ca *clusterAdmin) DeleteACL(filter AclFilter, validateOnly bool) ([]MatchingAcl, error) { - var filters []*AclFilter - filters = append(filters, &filter) - request := &DeleteAclsRequest{Filters: filters} - - b, err := ca.Controller() - if err != nil { - return nil, err - } - - rsp, err := b.DeleteAcls(request) - if err != nil { - return nil, err - } - - var mAcls []MatchingAcl - for _, fr := range rsp.FilterResponses { - for _, mACL := range fr.MatchingAcls { - mAcls = append(mAcls, *mACL) - } - - } - return mAcls, nil -} diff --git a/vendor/github.com/Shopify/sarama/alter_configs_request.go b/vendor/github.com/Shopify/sarama/alter_configs_request.go deleted file mode 100644 index 48c44ead67a..00000000000 --- a/vendor/github.com/Shopify/sarama/alter_configs_request.go +++ /dev/null @@ -1,120 +0,0 @@ -package sarama - -type AlterConfigsRequest struct { - Resources []*AlterConfigsResource - ValidateOnly bool -} - -type AlterConfigsResource struct { - Type ConfigResourceType - Name string - ConfigEntries map[string]*string -} - -func (acr *AlterConfigsRequest) encode(pe packetEncoder) error { - if err := pe.putArrayLength(len(acr.Resources)); err != nil { - return err - } - - for _, r := range acr.Resources { - if err := r.encode(pe); err != nil { - return err - } - } - - pe.putBool(acr.ValidateOnly) - return nil -} - -func (acr *AlterConfigsRequest) decode(pd packetDecoder, version int16) error { - resourceCount, err := pd.getArrayLength() - if err != nil { - return err - } - - acr.Resources = make([]*AlterConfigsResource, resourceCount) - for i := range acr.Resources { - r := &AlterConfigsResource{} - err = r.decode(pd, version) - if err != nil { - return err - } - acr.Resources[i] = r - } - - validateOnly, err := pd.getBool() - if err != nil { - return err - } - - acr.ValidateOnly = validateOnly - - return nil -} - -func (ac *AlterConfigsResource) encode(pe packetEncoder) error { - pe.putInt8(int8(ac.Type)) - - if err := pe.putString(ac.Name); err != nil { - return err - } - - if err := pe.putArrayLength(len(ac.ConfigEntries)); err != nil { - return err - } - for configKey, configValue := range ac.ConfigEntries { - if err := pe.putString(configKey); err != nil { - return err - } - if err := pe.putNullableString(configValue); err != nil { - return err - } - } - - return nil -} - -func (ac *AlterConfigsResource) decode(pd packetDecoder, version int16) error { - t, err := pd.getInt8() - if err != nil { - return err - } - ac.Type = ConfigResourceType(t) - - name, err := pd.getString() - if err != nil { - return err - } - ac.Name = name - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - if n > 0 { - ac.ConfigEntries = make(map[string]*string, n) - for i := 0; i < n; i++ { - configKey, err := pd.getString() - if err != nil { - return err - } - if ac.ConfigEntries[configKey], err = pd.getNullableString(); err != nil { - return err - } - } - } - return err -} - -func (acr *AlterConfigsRequest) key() int16 { - return 33 -} - -func (acr *AlterConfigsRequest) version() int16 { - return 0 -} - -func (acr *AlterConfigsRequest) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/alter_configs_response.go b/vendor/github.com/Shopify/sarama/alter_configs_response.go deleted file mode 100644 index 29b09e1ff84..00000000000 --- a/vendor/github.com/Shopify/sarama/alter_configs_response.go +++ /dev/null @@ -1,95 +0,0 @@ -package sarama - -import "time" - -type AlterConfigsResponse struct { - ThrottleTime time.Duration - Resources []*AlterConfigsResourceResponse -} - -type AlterConfigsResourceResponse struct { - ErrorCode int16 - ErrorMsg string - Type ConfigResourceType - Name string -} - -func (ct *AlterConfigsResponse) encode(pe packetEncoder) error { - pe.putInt32(int32(ct.ThrottleTime / time.Millisecond)) - - if err := pe.putArrayLength(len(ct.Resources)); err != nil { - return err - } - - for i := range ct.Resources { - pe.putInt16(ct.Resources[i].ErrorCode) - err := pe.putString(ct.Resources[i].ErrorMsg) - if err != nil { - return nil - } - pe.putInt8(int8(ct.Resources[i].Type)) - err = pe.putString(ct.Resources[i].Name) - if err != nil { - return nil - } - } - - return nil -} - -func (acr *AlterConfigsResponse) decode(pd packetDecoder, version int16) error { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - acr.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - responseCount, err := pd.getArrayLength() - if err != nil { - return err - } - - acr.Resources = make([]*AlterConfigsResourceResponse, responseCount) - - for i := range acr.Resources { - acr.Resources[i] = new(AlterConfigsResourceResponse) - - errCode, err := pd.getInt16() - if err != nil { - return err - } - acr.Resources[i].ErrorCode = errCode - - e, err := pd.getString() - if err != nil { - return err - } - acr.Resources[i].ErrorMsg = e - - t, err := pd.getInt8() - if err != nil { - return err - } - acr.Resources[i].Type = ConfigResourceType(t) - - name, err := pd.getString() - if err != nil { - return err - } - acr.Resources[i].Name = name - } - - return nil -} - -func (r *AlterConfigsResponse) key() int16 { - return 32 -} - -func (r *AlterConfigsResponse) version() int16 { - return 0 -} - -func (r *AlterConfigsResponse) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/api_versions_request.go b/vendor/github.com/Shopify/sarama/api_versions_request.go deleted file mode 100644 index ab65f01ccff..00000000000 --- a/vendor/github.com/Shopify/sarama/api_versions_request.go +++ /dev/null @@ -1,24 +0,0 @@ -package sarama - -type ApiVersionsRequest struct { -} - -func (r *ApiVersionsRequest) encode(pe packetEncoder) error { - return nil -} - -func (r *ApiVersionsRequest) decode(pd packetDecoder, version int16) (err error) { - return nil -} - -func (r *ApiVersionsRequest) key() int16 { - return 18 -} - -func (r *ApiVersionsRequest) version() int16 { - return 0 -} - -func (r *ApiVersionsRequest) requiredVersion() KafkaVersion { - return V0_10_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/api_versions_response.go b/vendor/github.com/Shopify/sarama/api_versions_response.go deleted file mode 100644 index 23bc326e15f..00000000000 --- a/vendor/github.com/Shopify/sarama/api_versions_response.go +++ /dev/null @@ -1,87 +0,0 @@ -package sarama - -type ApiVersionsResponseBlock struct { - ApiKey int16 - MinVersion int16 - MaxVersion int16 -} - -func (b *ApiVersionsResponseBlock) encode(pe packetEncoder) error { - pe.putInt16(b.ApiKey) - pe.putInt16(b.MinVersion) - pe.putInt16(b.MaxVersion) - return nil -} - -func (b *ApiVersionsResponseBlock) decode(pd packetDecoder) error { - var err error - - if b.ApiKey, err = pd.getInt16(); err != nil { - return err - } - - if b.MinVersion, err = pd.getInt16(); err != nil { - return err - } - - if b.MaxVersion, err = pd.getInt16(); err != nil { - return err - } - - return nil -} - -type ApiVersionsResponse struct { - Err KError - ApiVersions []*ApiVersionsResponseBlock -} - -func (r *ApiVersionsResponse) encode(pe packetEncoder) error { - pe.putInt16(int16(r.Err)) - if err := pe.putArrayLength(len(r.ApiVersions)); err != nil { - return err - } - for _, apiVersion := range r.ApiVersions { - if err := apiVersion.encode(pe); err != nil { - return err - } - } - return nil -} - -func (r *ApiVersionsResponse) decode(pd packetDecoder, version int16) error { - kerr, err := pd.getInt16() - if err != nil { - return err - } - - r.Err = KError(kerr) - - numBlocks, err := pd.getArrayLength() - if err != nil { - return err - } - - r.ApiVersions = make([]*ApiVersionsResponseBlock, numBlocks) - for i := 0; i < numBlocks; i++ { - block := new(ApiVersionsResponseBlock) - if err := block.decode(pd); err != nil { - return err - } - r.ApiVersions[i] = block - } - - return nil -} - -func (r *ApiVersionsResponse) key() int16 { - return 18 -} - -func (r *ApiVersionsResponse) version() int16 { - return 0 -} - -func (r *ApiVersionsResponse) requiredVersion() KafkaVersion { - return V0_10_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/async_producer.go b/vendor/github.com/Shopify/sarama/async_producer.go deleted file mode 100644 index 89722554092..00000000000 --- a/vendor/github.com/Shopify/sarama/async_producer.go +++ /dev/null @@ -1,932 +0,0 @@ -package sarama - -import ( - "encoding/binary" - "fmt" - "sync" - "time" - - "github.com/eapache/go-resiliency/breaker" - "github.com/eapache/queue" -) - -// AsyncProducer publishes Kafka messages using a non-blocking API. It routes messages -// to the correct broker for the provided topic-partition, refreshing metadata as appropriate, -// and parses responses for errors. You must read from the Errors() channel or the -// producer will deadlock. You must call Close() or AsyncClose() on a producer to avoid -// leaks: it will not be garbage-collected automatically when it passes out of -// scope. -type AsyncProducer interface { - - // AsyncClose triggers a shutdown of the producer. The shutdown has completed - // when both the Errors and Successes channels have been closed. When calling - // AsyncClose, you *must* continue to read from those channels in order to - // drain the results of any messages in flight. - AsyncClose() - - // Close shuts down the producer and waits for any buffered messages to be - // flushed. You must call this function before a producer object passes out of - // scope, as it may otherwise leak memory. You must call this before calling - // Close on the underlying client. - Close() error - - // Input is the input channel for the user to write messages to that they - // wish to send. - Input() chan<- *ProducerMessage - - // Successes is the success output channel back to the user when Return.Successes is - // enabled. If Return.Successes is true, you MUST read from this channel or the - // Producer will deadlock. It is suggested that you send and read messages - // together in a single select statement. - Successes() <-chan *ProducerMessage - - // Errors is the error output channel back to the user. You MUST read from this - // channel or the Producer will deadlock when the channel is full. Alternatively, - // you can set Producer.Return.Errors in your config to false, which prevents - // errors to be returned. - Errors() <-chan *ProducerError -} - -type asyncProducer struct { - client Client - conf *Config - ownClient bool - - errors chan *ProducerError - input, successes, retries chan *ProducerMessage - inFlight sync.WaitGroup - - brokers map[*Broker]chan<- *ProducerMessage - brokerRefs map[chan<- *ProducerMessage]int - brokerLock sync.Mutex -} - -// NewAsyncProducer creates a new AsyncProducer using the given broker addresses and configuration. -func NewAsyncProducer(addrs []string, conf *Config) (AsyncProducer, error) { - client, err := NewClient(addrs, conf) - if err != nil { - return nil, err - } - - p, err := NewAsyncProducerFromClient(client) - if err != nil { - return nil, err - } - p.(*asyncProducer).ownClient = true - return p, nil -} - -// NewAsyncProducerFromClient creates a new Producer using the given client. It is still -// necessary to call Close() on the underlying client when shutting down this producer. -func NewAsyncProducerFromClient(client Client) (AsyncProducer, error) { - // Check that we are not dealing with a closed Client before processing any other arguments - if client.Closed() { - return nil, ErrClosedClient - } - - p := &asyncProducer{ - client: client, - conf: client.Config(), - errors: make(chan *ProducerError), - input: make(chan *ProducerMessage), - successes: make(chan *ProducerMessage), - retries: make(chan *ProducerMessage), - brokers: make(map[*Broker]chan<- *ProducerMessage), - brokerRefs: make(map[chan<- *ProducerMessage]int), - } - - // launch our singleton dispatchers - go withRecover(p.dispatcher) - go withRecover(p.retryHandler) - - return p, nil -} - -type flagSet int8 - -const ( - syn flagSet = 1 << iota // first message from partitionProducer to brokerProducer - fin // final message from partitionProducer to brokerProducer and back - shutdown // start the shutdown process -) - -// ProducerMessage is the collection of elements passed to the Producer in order to send a message. -type ProducerMessage struct { - Topic string // The Kafka topic for this message. - // The partitioning key for this message. Pre-existing Encoders include - // StringEncoder and ByteEncoder. - Key Encoder - // The actual message to store in Kafka. Pre-existing Encoders include - // StringEncoder and ByteEncoder. - Value Encoder - - // The headers are key-value pairs that are transparently passed - // by Kafka between producers and consumers. - Headers []RecordHeader - - // This field is used to hold arbitrary data you wish to include so it - // will be available when receiving on the Successes and Errors channels. - // Sarama completely ignores this field and is only to be used for - // pass-through data. - Metadata interface{} - - // Below this point are filled in by the producer as the message is processed - - // Offset is the offset of the message stored on the broker. This is only - // guaranteed to be defined if the message was successfully delivered and - // RequiredAcks is not NoResponse. - Offset int64 - // Partition is the partition that the message was sent to. This is only - // guaranteed to be defined if the message was successfully delivered. - Partition int32 - // Timestamp is the timestamp assigned to the message by the broker. This - // is only guaranteed to be defined if the message was successfully - // delivered, RequiredAcks is not NoResponse, and the Kafka broker is at - // least version 0.10.0. - Timestamp time.Time - - retries int - flags flagSet - expectation chan *ProducerError -} - -const producerMessageOverhead = 26 // the metadata overhead of CRC, flags, etc. - -func (m *ProducerMessage) byteSize(version int) int { - var size int - if version >= 2 { - size = maximumRecordOverhead - for _, h := range m.Headers { - size += len(h.Key) + len(h.Value) + 2*binary.MaxVarintLen32 - } - } else { - size = producerMessageOverhead - } - if m.Key != nil { - size += m.Key.Length() - } - if m.Value != nil { - size += m.Value.Length() - } - return size -} - -func (m *ProducerMessage) clear() { - m.flags = 0 - m.retries = 0 -} - -// ProducerError is the type of error generated when the producer fails to deliver a message. -// It contains the original ProducerMessage as well as the actual error value. -type ProducerError struct { - Msg *ProducerMessage - Err error -} - -func (pe ProducerError) Error() string { - return fmt.Sprintf("kafka: Failed to produce message to topic %s: %s", pe.Msg.Topic, pe.Err) -} - -// ProducerErrors is a type that wraps a batch of "ProducerError"s and implements the Error interface. -// It can be returned from the Producer's Close method to avoid the need to manually drain the Errors channel -// when closing a producer. -type ProducerErrors []*ProducerError - -func (pe ProducerErrors) Error() string { - return fmt.Sprintf("kafka: Failed to deliver %d messages.", len(pe)) -} - -func (p *asyncProducer) Errors() <-chan *ProducerError { - return p.errors -} - -func (p *asyncProducer) Successes() <-chan *ProducerMessage { - return p.successes -} - -func (p *asyncProducer) Input() chan<- *ProducerMessage { - return p.input -} - -func (p *asyncProducer) Close() error { - p.AsyncClose() - - if p.conf.Producer.Return.Successes { - go withRecover(func() { - for range p.successes { - } - }) - } - - var errors ProducerErrors - if p.conf.Producer.Return.Errors { - for event := range p.errors { - errors = append(errors, event) - } - } else { - <-p.errors - } - - if len(errors) > 0 { - return errors - } - return nil -} - -func (p *asyncProducer) AsyncClose() { - go withRecover(p.shutdown) -} - -// singleton -// dispatches messages by topic -func (p *asyncProducer) dispatcher() { - handlers := make(map[string]chan<- *ProducerMessage) - shuttingDown := false - - for msg := range p.input { - if msg == nil { - Logger.Println("Something tried to send a nil message, it was ignored.") - continue - } - - if msg.flags&shutdown != 0 { - shuttingDown = true - p.inFlight.Done() - continue - } else if msg.retries == 0 { - if shuttingDown { - // we can't just call returnError here because that decrements the wait group, - // which hasn't been incremented yet for this message, and shouldn't be - pErr := &ProducerError{Msg: msg, Err: ErrShuttingDown} - if p.conf.Producer.Return.Errors { - p.errors <- pErr - } else { - Logger.Println(pErr) - } - continue - } - p.inFlight.Add(1) - } - - version := 1 - if p.conf.Version.IsAtLeast(V0_11_0_0) { - version = 2 - } else if msg.Headers != nil { - p.returnError(msg, ConfigurationError("Producing headers requires Kafka at least v0.11")) - continue - } - if msg.byteSize(version) > p.conf.Producer.MaxMessageBytes { - p.returnError(msg, ErrMessageSizeTooLarge) - continue - } - - handler := handlers[msg.Topic] - if handler == nil { - handler = p.newTopicProducer(msg.Topic) - handlers[msg.Topic] = handler - } - - handler <- msg - } - - for _, handler := range handlers { - close(handler) - } -} - -// one per topic -// partitions messages, then dispatches them by partition -type topicProducer struct { - parent *asyncProducer - topic string - input <-chan *ProducerMessage - - breaker *breaker.Breaker - handlers map[int32]chan<- *ProducerMessage - partitioner Partitioner -} - -func (p *asyncProducer) newTopicProducer(topic string) chan<- *ProducerMessage { - input := make(chan *ProducerMessage, p.conf.ChannelBufferSize) - tp := &topicProducer{ - parent: p, - topic: topic, - input: input, - breaker: breaker.New(3, 1, 10*time.Second), - handlers: make(map[int32]chan<- *ProducerMessage), - partitioner: p.conf.Producer.Partitioner(topic), - } - go withRecover(tp.dispatch) - return input -} - -func (tp *topicProducer) dispatch() { - for msg := range tp.input { - if msg.retries == 0 { - if err := tp.partitionMessage(msg); err != nil { - tp.parent.returnError(msg, err) - continue - } - } - - handler := tp.handlers[msg.Partition] - if handler == nil { - handler = tp.parent.newPartitionProducer(msg.Topic, msg.Partition) - tp.handlers[msg.Partition] = handler - } - - handler <- msg - } - - for _, handler := range tp.handlers { - close(handler) - } -} - -func (tp *topicProducer) partitionMessage(msg *ProducerMessage) error { - var partitions []int32 - - err := tp.breaker.Run(func() (err error) { - var requiresConsistency = false - if ep, ok := tp.partitioner.(DynamicConsistencyPartitioner); ok { - requiresConsistency = ep.MessageRequiresConsistency(msg) - } else { - requiresConsistency = tp.partitioner.RequiresConsistency() - } - - if requiresConsistency { - partitions, err = tp.parent.client.Partitions(msg.Topic) - } else { - partitions, err = tp.parent.client.WritablePartitions(msg.Topic) - } - return - }) - - if err != nil { - return err - } - - numPartitions := int32(len(partitions)) - - if numPartitions == 0 { - return ErrLeaderNotAvailable - } - - choice, err := tp.partitioner.Partition(msg, numPartitions) - - if err != nil { - return err - } else if choice < 0 || choice >= numPartitions { - return ErrInvalidPartition - } - - msg.Partition = partitions[choice] - - return nil -} - -// one per partition per topic -// dispatches messages to the appropriate broker -// also responsible for maintaining message order during retries -type partitionProducer struct { - parent *asyncProducer - topic string - partition int32 - input <-chan *ProducerMessage - - leader *Broker - breaker *breaker.Breaker - output chan<- *ProducerMessage - - // highWatermark tracks the "current" retry level, which is the only one where we actually let messages through, - // all other messages get buffered in retryState[msg.retries].buf to preserve ordering - // retryState[msg.retries].expectChaser simply tracks whether we've seen a fin message for a given level (and - // therefore whether our buffer is complete and safe to flush) - highWatermark int - retryState []partitionRetryState -} - -type partitionRetryState struct { - buf []*ProducerMessage - expectChaser bool -} - -func (p *asyncProducer) newPartitionProducer(topic string, partition int32) chan<- *ProducerMessage { - input := make(chan *ProducerMessage, p.conf.ChannelBufferSize) - pp := &partitionProducer{ - parent: p, - topic: topic, - partition: partition, - input: input, - - breaker: breaker.New(3, 1, 10*time.Second), - retryState: make([]partitionRetryState, p.conf.Producer.Retry.Max+1), - } - go withRecover(pp.dispatch) - return input -} - -func (pp *partitionProducer) dispatch() { - // try to prefetch the leader; if this doesn't work, we'll do a proper call to `updateLeader` - // on the first message - pp.leader, _ = pp.parent.client.Leader(pp.topic, pp.partition) - if pp.leader != nil { - pp.output = pp.parent.getBrokerProducer(pp.leader) - pp.parent.inFlight.Add(1) // we're generating a syn message; track it so we don't shut down while it's still inflight - pp.output <- &ProducerMessage{Topic: pp.topic, Partition: pp.partition, flags: syn} - } - - for msg := range pp.input { - if msg.retries > pp.highWatermark { - // a new, higher, retry level; handle it and then back off - pp.newHighWatermark(msg.retries) - time.Sleep(pp.parent.conf.Producer.Retry.Backoff) - } else if pp.highWatermark > 0 { - // we are retrying something (else highWatermark would be 0) but this message is not a *new* retry level - if msg.retries < pp.highWatermark { - // in fact this message is not even the current retry level, so buffer it for now (unless it's a just a fin) - if msg.flags&fin == fin { - pp.retryState[msg.retries].expectChaser = false - pp.parent.inFlight.Done() // this fin is now handled and will be garbage collected - } else { - pp.retryState[msg.retries].buf = append(pp.retryState[msg.retries].buf, msg) - } - continue - } else if msg.flags&fin == fin { - // this message is of the current retry level (msg.retries == highWatermark) and the fin flag is set, - // meaning this retry level is done and we can go down (at least) one level and flush that - pp.retryState[pp.highWatermark].expectChaser = false - pp.flushRetryBuffers() - pp.parent.inFlight.Done() // this fin is now handled and will be garbage collected - continue - } - } - - // if we made it this far then the current msg contains real data, and can be sent to the next goroutine - // without breaking any of our ordering guarantees - - if pp.output == nil { - if err := pp.updateLeader(); err != nil { - pp.parent.returnError(msg, err) - time.Sleep(pp.parent.conf.Producer.Retry.Backoff) - continue - } - Logger.Printf("producer/leader/%s/%d selected broker %d\n", pp.topic, pp.partition, pp.leader.ID()) - } - - pp.output <- msg - } - - if pp.output != nil { - pp.parent.unrefBrokerProducer(pp.leader, pp.output) - } -} - -func (pp *partitionProducer) newHighWatermark(hwm int) { - Logger.Printf("producer/leader/%s/%d state change to [retrying-%d]\n", pp.topic, pp.partition, hwm) - pp.highWatermark = hwm - - // send off a fin so that we know when everything "in between" has made it - // back to us and we can safely flush the backlog (otherwise we risk re-ordering messages) - pp.retryState[pp.highWatermark].expectChaser = true - pp.parent.inFlight.Add(1) // we're generating a fin message; track it so we don't shut down while it's still inflight - pp.output <- &ProducerMessage{Topic: pp.topic, Partition: pp.partition, flags: fin, retries: pp.highWatermark - 1} - - // a new HWM means that our current broker selection is out of date - Logger.Printf("producer/leader/%s/%d abandoning broker %d\n", pp.topic, pp.partition, pp.leader.ID()) - pp.parent.unrefBrokerProducer(pp.leader, pp.output) - pp.output = nil -} - -func (pp *partitionProducer) flushRetryBuffers() { - Logger.Printf("producer/leader/%s/%d state change to [flushing-%d]\n", pp.topic, pp.partition, pp.highWatermark) - for { - pp.highWatermark-- - - if pp.output == nil { - if err := pp.updateLeader(); err != nil { - pp.parent.returnErrors(pp.retryState[pp.highWatermark].buf, err) - goto flushDone - } - Logger.Printf("producer/leader/%s/%d selected broker %d\n", pp.topic, pp.partition, pp.leader.ID()) - } - - for _, msg := range pp.retryState[pp.highWatermark].buf { - pp.output <- msg - } - - flushDone: - pp.retryState[pp.highWatermark].buf = nil - if pp.retryState[pp.highWatermark].expectChaser { - Logger.Printf("producer/leader/%s/%d state change to [retrying-%d]\n", pp.topic, pp.partition, pp.highWatermark) - break - } else if pp.highWatermark == 0 { - Logger.Printf("producer/leader/%s/%d state change to [normal]\n", pp.topic, pp.partition) - break - } - } -} - -func (pp *partitionProducer) updateLeader() error { - return pp.breaker.Run(func() (err error) { - if err = pp.parent.client.RefreshMetadata(pp.topic); err != nil { - return err - } - - if pp.leader, err = pp.parent.client.Leader(pp.topic, pp.partition); err != nil { - return err - } - - pp.output = pp.parent.getBrokerProducer(pp.leader) - pp.parent.inFlight.Add(1) // we're generating a syn message; track it so we don't shut down while it's still inflight - pp.output <- &ProducerMessage{Topic: pp.topic, Partition: pp.partition, flags: syn} - - return nil - }) -} - -// one per broker; also constructs an associated flusher -func (p *asyncProducer) newBrokerProducer(broker *Broker) chan<- *ProducerMessage { - var ( - input = make(chan *ProducerMessage) - bridge = make(chan *produceSet) - responses = make(chan *brokerProducerResponse) - ) - - bp := &brokerProducer{ - parent: p, - broker: broker, - input: input, - output: bridge, - responses: responses, - buffer: newProduceSet(p), - currentRetries: make(map[string]map[int32]error), - } - go withRecover(bp.run) - - // minimal bridge to make the network response `select`able - go withRecover(func() { - for set := range bridge { - request := set.buildRequest() - - response, err := broker.Produce(request) - - responses <- &brokerProducerResponse{ - set: set, - err: err, - res: response, - } - } - close(responses) - }) - - return input -} - -type brokerProducerResponse struct { - set *produceSet - err error - res *ProduceResponse -} - -// groups messages together into appropriately-sized batches for sending to the broker -// handles state related to retries etc -type brokerProducer struct { - parent *asyncProducer - broker *Broker - - input <-chan *ProducerMessage - output chan<- *produceSet - responses <-chan *brokerProducerResponse - - buffer *produceSet - timer <-chan time.Time - timerFired bool - - closing error - currentRetries map[string]map[int32]error -} - -func (bp *brokerProducer) run() { - var output chan<- *produceSet - Logger.Printf("producer/broker/%d starting up\n", bp.broker.ID()) - - for { - select { - case msg := <-bp.input: - if msg == nil { - bp.shutdown() - return - } - - if msg.flags&syn == syn { - Logger.Printf("producer/broker/%d state change to [open] on %s/%d\n", - bp.broker.ID(), msg.Topic, msg.Partition) - if bp.currentRetries[msg.Topic] == nil { - bp.currentRetries[msg.Topic] = make(map[int32]error) - } - bp.currentRetries[msg.Topic][msg.Partition] = nil - bp.parent.inFlight.Done() - continue - } - - if reason := bp.needsRetry(msg); reason != nil { - bp.parent.retryMessage(msg, reason) - - if bp.closing == nil && msg.flags&fin == fin { - // we were retrying this partition but we can start processing again - delete(bp.currentRetries[msg.Topic], msg.Partition) - Logger.Printf("producer/broker/%d state change to [closed] on %s/%d\n", - bp.broker.ID(), msg.Topic, msg.Partition) - } - - continue - } - - if bp.buffer.wouldOverflow(msg) { - if err := bp.waitForSpace(msg); err != nil { - bp.parent.retryMessage(msg, err) - continue - } - } - - if err := bp.buffer.add(msg); err != nil { - bp.parent.returnError(msg, err) - continue - } - - if bp.parent.conf.Producer.Flush.Frequency > 0 && bp.timer == nil { - bp.timer = time.After(bp.parent.conf.Producer.Flush.Frequency) - } - case <-bp.timer: - bp.timerFired = true - case output <- bp.buffer: - bp.rollOver() - case response := <-bp.responses: - bp.handleResponse(response) - } - - if bp.timerFired || bp.buffer.readyToFlush() { - output = bp.output - } else { - output = nil - } - } -} - -func (bp *brokerProducer) shutdown() { - for !bp.buffer.empty() { - select { - case response := <-bp.responses: - bp.handleResponse(response) - case bp.output <- bp.buffer: - bp.rollOver() - } - } - close(bp.output) - for response := range bp.responses { - bp.handleResponse(response) - } - - Logger.Printf("producer/broker/%d shut down\n", bp.broker.ID()) -} - -func (bp *brokerProducer) needsRetry(msg *ProducerMessage) error { - if bp.closing != nil { - return bp.closing - } - - return bp.currentRetries[msg.Topic][msg.Partition] -} - -func (bp *brokerProducer) waitForSpace(msg *ProducerMessage) error { - Logger.Printf("producer/broker/%d maximum request accumulated, waiting for space\n", bp.broker.ID()) - - for { - select { - case response := <-bp.responses: - bp.handleResponse(response) - // handling a response can change our state, so re-check some things - if reason := bp.needsRetry(msg); reason != nil { - return reason - } else if !bp.buffer.wouldOverflow(msg) { - return nil - } - case bp.output <- bp.buffer: - bp.rollOver() - return nil - } - } -} - -func (bp *brokerProducer) rollOver() { - bp.timer = nil - bp.timerFired = false - bp.buffer = newProduceSet(bp.parent) -} - -func (bp *brokerProducer) handleResponse(response *brokerProducerResponse) { - if response.err != nil { - bp.handleError(response.set, response.err) - } else { - bp.handleSuccess(response.set, response.res) - } - - if bp.buffer.empty() { - bp.rollOver() // this can happen if the response invalidated our buffer - } -} - -func (bp *brokerProducer) handleSuccess(sent *produceSet, response *ProduceResponse) { - // we iterate through the blocks in the request set, not the response, so that we notice - // if the response is missing a block completely - sent.eachPartition(func(topic string, partition int32, msgs []*ProducerMessage) { - if response == nil { - // this only happens when RequiredAcks is NoResponse, so we have to assume success - bp.parent.returnSuccesses(msgs) - return - } - - block := response.GetBlock(topic, partition) - if block == nil { - bp.parent.returnErrors(msgs, ErrIncompleteResponse) - return - } - - switch block.Err { - // Success - case ErrNoError: - if bp.parent.conf.Version.IsAtLeast(V0_10_0_0) && !block.Timestamp.IsZero() { - for _, msg := range msgs { - msg.Timestamp = block.Timestamp - } - } - for i, msg := range msgs { - msg.Offset = block.Offset + int64(i) - } - bp.parent.returnSuccesses(msgs) - // Retriable errors - case ErrInvalidMessage, ErrUnknownTopicOrPartition, ErrLeaderNotAvailable, ErrNotLeaderForPartition, - ErrRequestTimedOut, ErrNotEnoughReplicas, ErrNotEnoughReplicasAfterAppend: - Logger.Printf("producer/broker/%d state change to [retrying] on %s/%d because %v\n", - bp.broker.ID(), topic, partition, block.Err) - bp.currentRetries[topic][partition] = block.Err - bp.parent.retryMessages(msgs, block.Err) - bp.parent.retryMessages(bp.buffer.dropPartition(topic, partition), block.Err) - // Other non-retriable errors - default: - bp.parent.returnErrors(msgs, block.Err) - } - }) -} - -func (bp *brokerProducer) handleError(sent *produceSet, err error) { - switch err.(type) { - case PacketEncodingError: - sent.eachPartition(func(topic string, partition int32, msgs []*ProducerMessage) { - bp.parent.returnErrors(msgs, err) - }) - default: - Logger.Printf("producer/broker/%d state change to [closing] because %s\n", bp.broker.ID(), err) - bp.parent.abandonBrokerConnection(bp.broker) - _ = bp.broker.Close() - bp.closing = err - sent.eachPartition(func(topic string, partition int32, msgs []*ProducerMessage) { - bp.parent.retryMessages(msgs, err) - }) - bp.buffer.eachPartition(func(topic string, partition int32, msgs []*ProducerMessage) { - bp.parent.retryMessages(msgs, err) - }) - bp.rollOver() - } -} - -// singleton -// effectively a "bridge" between the flushers and the dispatcher in order to avoid deadlock -// based on https://godoc.org/github.com/eapache/channels#InfiniteChannel -func (p *asyncProducer) retryHandler() { - var msg *ProducerMessage - buf := queue.New() - - for { - if buf.Length() == 0 { - msg = <-p.retries - } else { - select { - case msg = <-p.retries: - case p.input <- buf.Peek().(*ProducerMessage): - buf.Remove() - continue - } - } - - if msg == nil { - return - } - - buf.Add(msg) - } -} - -// utility functions - -func (p *asyncProducer) shutdown() { - Logger.Println("Producer shutting down.") - p.inFlight.Add(1) - p.input <- &ProducerMessage{flags: shutdown} - - p.inFlight.Wait() - - if p.ownClient { - err := p.client.Close() - if err != nil { - Logger.Println("producer/shutdown failed to close the embedded client:", err) - } - } - - close(p.input) - close(p.retries) - close(p.errors) - close(p.successes) -} - -func (p *asyncProducer) returnError(msg *ProducerMessage, err error) { - msg.clear() - pErr := &ProducerError{Msg: msg, Err: err} - if p.conf.Producer.Return.Errors { - p.errors <- pErr - } else { - Logger.Println(pErr) - } - p.inFlight.Done() -} - -func (p *asyncProducer) returnErrors(batch []*ProducerMessage, err error) { - for _, msg := range batch { - p.returnError(msg, err) - } -} - -func (p *asyncProducer) returnSuccesses(batch []*ProducerMessage) { - for _, msg := range batch { - if p.conf.Producer.Return.Successes { - msg.clear() - p.successes <- msg - } - p.inFlight.Done() - } -} - -func (p *asyncProducer) retryMessage(msg *ProducerMessage, err error) { - if msg.retries >= p.conf.Producer.Retry.Max { - p.returnError(msg, err) - } else { - msg.retries++ - p.retries <- msg - } -} - -func (p *asyncProducer) retryMessages(batch []*ProducerMessage, err error) { - for _, msg := range batch { - p.retryMessage(msg, err) - } -} - -func (p *asyncProducer) getBrokerProducer(broker *Broker) chan<- *ProducerMessage { - p.brokerLock.Lock() - defer p.brokerLock.Unlock() - - bp := p.brokers[broker] - - if bp == nil { - bp = p.newBrokerProducer(broker) - p.brokers[broker] = bp - p.brokerRefs[bp] = 0 - } - - p.brokerRefs[bp]++ - - return bp -} - -func (p *asyncProducer) unrefBrokerProducer(broker *Broker, bp chan<- *ProducerMessage) { - p.brokerLock.Lock() - defer p.brokerLock.Unlock() - - p.brokerRefs[bp]-- - if p.brokerRefs[bp] == 0 { - close(bp) - delete(p.brokerRefs, bp) - - if p.brokers[broker] == bp { - delete(p.brokers, broker) - } - } -} - -func (p *asyncProducer) abandonBrokerConnection(broker *Broker) { - p.brokerLock.Lock() - defer p.brokerLock.Unlock() - - delete(p.brokers, broker) -} diff --git a/vendor/github.com/Shopify/sarama/broker.go b/vendor/github.com/Shopify/sarama/broker.go deleted file mode 100644 index 6430fd999fa..00000000000 --- a/vendor/github.com/Shopify/sarama/broker.go +++ /dev/null @@ -1,883 +0,0 @@ -package sarama - -import ( - "crypto/tls" - "encoding/binary" - "fmt" - "io" - "net" - "strconv" - "sync" - "sync/atomic" - "time" - - "github.com/rcrowley/go-metrics" -) - -// Broker represents a single Kafka broker connection. All operations on this object are entirely concurrency-safe. -type Broker struct { - id int32 - addr string - rack *string - - conf *Config - correlationID int32 - conn net.Conn - connErr error - lock sync.Mutex - opened int32 - - responses chan responsePromise - done chan bool - - incomingByteRate metrics.Meter - requestRate metrics.Meter - requestSize metrics.Histogram - requestLatency metrics.Histogram - outgoingByteRate metrics.Meter - responseRate metrics.Meter - responseSize metrics.Histogram - brokerIncomingByteRate metrics.Meter - brokerRequestRate metrics.Meter - brokerRequestSize metrics.Histogram - brokerRequestLatency metrics.Histogram - brokerOutgoingByteRate metrics.Meter - brokerResponseRate metrics.Meter - brokerResponseSize metrics.Histogram -} - -type responsePromise struct { - requestTime time.Time - correlationID int32 - packets chan []byte - errors chan error -} - -// NewBroker creates and returns a Broker targeting the given host:port address. -// This does not attempt to actually connect, you have to call Open() for that. -func NewBroker(addr string) *Broker { - return &Broker{id: -1, addr: addr} -} - -// Open tries to connect to the Broker if it is not already connected or connecting, but does not block -// waiting for the connection to complete. This means that any subsequent operations on the broker will -// block waiting for the connection to succeed or fail. To get the effect of a fully synchronous Open call, -// follow it by a call to Connected(). The only errors Open will return directly are ConfigurationError or -// AlreadyConnected. If conf is nil, the result of NewConfig() is used. -func (b *Broker) Open(conf *Config) error { - if !atomic.CompareAndSwapInt32(&b.opened, 0, 1) { - return ErrAlreadyConnected - } - - if conf == nil { - conf = NewConfig() - } - - err := conf.Validate() - if err != nil { - return err - } - - b.lock.Lock() - - go withRecover(func() { - defer b.lock.Unlock() - - dialer := net.Dialer{ - Timeout: conf.Net.DialTimeout, - KeepAlive: conf.Net.KeepAlive, - } - - if conf.Net.TLS.Enable { - b.conn, b.connErr = tls.DialWithDialer(&dialer, "tcp", b.addr, conf.Net.TLS.Config) - } else { - b.conn, b.connErr = dialer.Dial("tcp", b.addr) - } - if b.connErr != nil { - Logger.Printf("Failed to connect to broker %s: %s\n", b.addr, b.connErr) - b.conn = nil - atomic.StoreInt32(&b.opened, 0) - return - } - b.conn = newBufConn(b.conn) - - b.conf = conf - - // Create or reuse the global metrics shared between brokers - b.incomingByteRate = metrics.GetOrRegisterMeter("incoming-byte-rate", conf.MetricRegistry) - b.requestRate = metrics.GetOrRegisterMeter("request-rate", conf.MetricRegistry) - b.requestSize = getOrRegisterHistogram("request-size", conf.MetricRegistry) - b.requestLatency = getOrRegisterHistogram("request-latency-in-ms", conf.MetricRegistry) - b.outgoingByteRate = metrics.GetOrRegisterMeter("outgoing-byte-rate", conf.MetricRegistry) - b.responseRate = metrics.GetOrRegisterMeter("response-rate", conf.MetricRegistry) - b.responseSize = getOrRegisterHistogram("response-size", conf.MetricRegistry) - // Do not gather metrics for seeded broker (only used during bootstrap) because they share - // the same id (-1) and are already exposed through the global metrics above - if b.id >= 0 { - b.brokerIncomingByteRate = getOrRegisterBrokerMeter("incoming-byte-rate", b, conf.MetricRegistry) - b.brokerRequestRate = getOrRegisterBrokerMeter("request-rate", b, conf.MetricRegistry) - b.brokerRequestSize = getOrRegisterBrokerHistogram("request-size", b, conf.MetricRegistry) - b.brokerRequestLatency = getOrRegisterBrokerHistogram("request-latency-in-ms", b, conf.MetricRegistry) - b.brokerOutgoingByteRate = getOrRegisterBrokerMeter("outgoing-byte-rate", b, conf.MetricRegistry) - b.brokerResponseRate = getOrRegisterBrokerMeter("response-rate", b, conf.MetricRegistry) - b.brokerResponseSize = getOrRegisterBrokerHistogram("response-size", b, conf.MetricRegistry) - } - - if conf.Net.SASL.Enable { - b.connErr = b.sendAndReceiveSASLPlainAuth() - if b.connErr != nil { - err = b.conn.Close() - if err == nil { - Logger.Printf("Closed connection to broker %s\n", b.addr) - } else { - Logger.Printf("Error while closing connection to broker %s: %s\n", b.addr, err) - } - b.conn = nil - atomic.StoreInt32(&b.opened, 0) - return - } - } - - b.done = make(chan bool) - b.responses = make(chan responsePromise, b.conf.Net.MaxOpenRequests-1) - - if b.id >= 0 { - Logger.Printf("Connected to broker at %s (registered as #%d)\n", b.addr, b.id) - } else { - Logger.Printf("Connected to broker at %s (unregistered)\n", b.addr) - } - go withRecover(b.responseReceiver) - }) - - return nil -} - -// Connected returns true if the broker is connected and false otherwise. If the broker is not -// connected but it had tried to connect, the error from that connection attempt is also returned. -func (b *Broker) Connected() (bool, error) { - b.lock.Lock() - defer b.lock.Unlock() - - return b.conn != nil, b.connErr -} - -func (b *Broker) Close() error { - b.lock.Lock() - defer b.lock.Unlock() - - if b.conn == nil { - return ErrNotConnected - } - - close(b.responses) - <-b.done - - err := b.conn.Close() - - b.conn = nil - b.connErr = nil - b.done = nil - b.responses = nil - - if b.id >= 0 { - b.conf.MetricRegistry.Unregister(getMetricNameForBroker("incoming-byte-rate", b)) - b.conf.MetricRegistry.Unregister(getMetricNameForBroker("request-rate", b)) - b.conf.MetricRegistry.Unregister(getMetricNameForBroker("outgoing-byte-rate", b)) - b.conf.MetricRegistry.Unregister(getMetricNameForBroker("response-rate", b)) - } - - if err == nil { - Logger.Printf("Closed connection to broker %s\n", b.addr) - } else { - Logger.Printf("Error while closing connection to broker %s: %s\n", b.addr, err) - } - - atomic.StoreInt32(&b.opened, 0) - - return err -} - -// ID returns the broker ID retrieved from Kafka's metadata, or -1 if that is not known. -func (b *Broker) ID() int32 { - return b.id -} - -// Addr returns the broker address as either retrieved from Kafka's metadata or passed to NewBroker. -func (b *Broker) Addr() string { - return b.addr -} - -func (b *Broker) GetMetadata(request *MetadataRequest) (*MetadataResponse, error) { - response := new(MetadataResponse) - - err := b.sendAndReceive(request, response) - - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) GetConsumerMetadata(request *ConsumerMetadataRequest) (*ConsumerMetadataResponse, error) { - response := new(ConsumerMetadataResponse) - - err := b.sendAndReceive(request, response) - - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) FindCoordinator(request *FindCoordinatorRequest) (*FindCoordinatorResponse, error) { - response := new(FindCoordinatorResponse) - - err := b.sendAndReceive(request, response) - - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) GetAvailableOffsets(request *OffsetRequest) (*OffsetResponse, error) { - response := new(OffsetResponse) - - err := b.sendAndReceive(request, response) - - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) Produce(request *ProduceRequest) (*ProduceResponse, error) { - var response *ProduceResponse - var err error - - if request.RequiredAcks == NoResponse { - err = b.sendAndReceive(request, nil) - } else { - response = new(ProduceResponse) - err = b.sendAndReceive(request, response) - } - - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) Fetch(request *FetchRequest) (*FetchResponse, error) { - response := new(FetchResponse) - - err := b.sendAndReceive(request, response) - - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) CommitOffset(request *OffsetCommitRequest) (*OffsetCommitResponse, error) { - response := new(OffsetCommitResponse) - - err := b.sendAndReceive(request, response) - - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) FetchOffset(request *OffsetFetchRequest) (*OffsetFetchResponse, error) { - response := new(OffsetFetchResponse) - - err := b.sendAndReceive(request, response) - - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) JoinGroup(request *JoinGroupRequest) (*JoinGroupResponse, error) { - response := new(JoinGroupResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) SyncGroup(request *SyncGroupRequest) (*SyncGroupResponse, error) { - response := new(SyncGroupResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) LeaveGroup(request *LeaveGroupRequest) (*LeaveGroupResponse, error) { - response := new(LeaveGroupResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) Heartbeat(request *HeartbeatRequest) (*HeartbeatResponse, error) { - response := new(HeartbeatResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) ListGroups(request *ListGroupsRequest) (*ListGroupsResponse, error) { - response := new(ListGroupsResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) DescribeGroups(request *DescribeGroupsRequest) (*DescribeGroupsResponse, error) { - response := new(DescribeGroupsResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) ApiVersions(request *ApiVersionsRequest) (*ApiVersionsResponse, error) { - response := new(ApiVersionsResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) CreateTopics(request *CreateTopicsRequest) (*CreateTopicsResponse, error) { - response := new(CreateTopicsResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) DeleteTopics(request *DeleteTopicsRequest) (*DeleteTopicsResponse, error) { - response := new(DeleteTopicsResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) CreatePartitions(request *CreatePartitionsRequest) (*CreatePartitionsResponse, error) { - response := new(CreatePartitionsResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) DeleteRecords(request *DeleteRecordsRequest) (*DeleteRecordsResponse, error) { - response := new(DeleteRecordsResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) DescribeAcls(request *DescribeAclsRequest) (*DescribeAclsResponse, error) { - response := new(DescribeAclsResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) CreateAcls(request *CreateAclsRequest) (*CreateAclsResponse, error) { - response := new(CreateAclsResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) DeleteAcls(request *DeleteAclsRequest) (*DeleteAclsResponse, error) { - response := new(DeleteAclsResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) InitProducerID(request *InitProducerIDRequest) (*InitProducerIDResponse, error) { - response := new(InitProducerIDResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) AddPartitionsToTxn(request *AddPartitionsToTxnRequest) (*AddPartitionsToTxnResponse, error) { - response := new(AddPartitionsToTxnResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) AddOffsetsToTxn(request *AddOffsetsToTxnRequest) (*AddOffsetsToTxnResponse, error) { - response := new(AddOffsetsToTxnResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) EndTxn(request *EndTxnRequest) (*EndTxnResponse, error) { - response := new(EndTxnResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) TxnOffsetCommit(request *TxnOffsetCommitRequest) (*TxnOffsetCommitResponse, error) { - response := new(TxnOffsetCommitResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) DescribeConfigs(request *DescribeConfigsRequest) (*DescribeConfigsResponse, error) { - response := new(DescribeConfigsResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) AlterConfigs(request *AlterConfigsRequest) (*AlterConfigsResponse, error) { - response := new(AlterConfigsResponse) - - err := b.sendAndReceive(request, response) - if err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) DeleteGroups(request *DeleteGroupsRequest) (*DeleteGroupsResponse, error) { - response := new(DeleteGroupsResponse) - - if err := b.sendAndReceive(request, response); err != nil { - return nil, err - } - - return response, nil -} - -func (b *Broker) send(rb protocolBody, promiseResponse bool) (*responsePromise, error) { - b.lock.Lock() - defer b.lock.Unlock() - - if b.conn == nil { - if b.connErr != nil { - return nil, b.connErr - } - return nil, ErrNotConnected - } - - if !b.conf.Version.IsAtLeast(rb.requiredVersion()) { - return nil, ErrUnsupportedVersion - } - - req := &request{correlationID: b.correlationID, clientID: b.conf.ClientID, body: rb} - buf, err := encode(req, b.conf.MetricRegistry) - if err != nil { - return nil, err - } - - err = b.conn.SetWriteDeadline(time.Now().Add(b.conf.Net.WriteTimeout)) - if err != nil { - return nil, err - } - - requestTime := time.Now() - bytes, err := b.conn.Write(buf) - b.updateOutgoingCommunicationMetrics(bytes) - if err != nil { - return nil, err - } - b.correlationID++ - - if !promiseResponse { - // Record request latency without the response - b.updateRequestLatencyMetrics(time.Since(requestTime)) - return nil, nil - } - - promise := responsePromise{requestTime, req.correlationID, make(chan []byte), make(chan error)} - b.responses <- promise - - return &promise, nil -} - -func (b *Broker) sendAndReceive(req protocolBody, res versionedDecoder) error { - promise, err := b.send(req, res != nil) - - if err != nil { - return err - } - - if promise == nil { - return nil - } - - select { - case buf := <-promise.packets: - return versionedDecode(buf, res, req.version()) - case err = <-promise.errors: - return err - } -} - -func (b *Broker) decode(pd packetDecoder, version int16) (err error) { - b.id, err = pd.getInt32() - if err != nil { - return err - } - - host, err := pd.getString() - if err != nil { - return err - } - - port, err := pd.getInt32() - if err != nil { - return err - } - - if version >= 1 { - b.rack, err = pd.getNullableString() - if err != nil { - return err - } - } - - b.addr = net.JoinHostPort(host, fmt.Sprint(port)) - if _, _, err := net.SplitHostPort(b.addr); err != nil { - return err - } - - return nil -} - -func (b *Broker) encode(pe packetEncoder, version int16) (err error) { - - host, portstr, err := net.SplitHostPort(b.addr) - if err != nil { - return err - } - port, err := strconv.Atoi(portstr) - if err != nil { - return err - } - - pe.putInt32(b.id) - - err = pe.putString(host) - if err != nil { - return err - } - - pe.putInt32(int32(port)) - - if version >= 1 { - err = pe.putNullableString(b.rack) - if err != nil { - return err - } - } - - return nil -} - -func (b *Broker) responseReceiver() { - var dead error - header := make([]byte, 8) - for response := range b.responses { - if dead != nil { - response.errors <- dead - continue - } - - err := b.conn.SetReadDeadline(time.Now().Add(b.conf.Net.ReadTimeout)) - if err != nil { - dead = err - response.errors <- err - continue - } - - bytesReadHeader, err := io.ReadFull(b.conn, header) - requestLatency := time.Since(response.requestTime) - if err != nil { - b.updateIncomingCommunicationMetrics(bytesReadHeader, requestLatency) - dead = err - response.errors <- err - continue - } - - decodedHeader := responseHeader{} - err = decode(header, &decodedHeader) - if err != nil { - b.updateIncomingCommunicationMetrics(bytesReadHeader, requestLatency) - dead = err - response.errors <- err - continue - } - if decodedHeader.correlationID != response.correlationID { - b.updateIncomingCommunicationMetrics(bytesReadHeader, requestLatency) - // TODO if decoded ID < cur ID, discard until we catch up - // TODO if decoded ID > cur ID, save it so when cur ID catches up we have a response - dead = PacketDecodingError{fmt.Sprintf("correlation ID didn't match, wanted %d, got %d", response.correlationID, decodedHeader.correlationID)} - response.errors <- dead - continue - } - - buf := make([]byte, decodedHeader.length-4) - bytesReadBody, err := io.ReadFull(b.conn, buf) - b.updateIncomingCommunicationMetrics(bytesReadHeader+bytesReadBody, requestLatency) - if err != nil { - dead = err - response.errors <- err - continue - } - - response.packets <- buf - } - close(b.done) -} - -func (b *Broker) sendAndReceiveSASLPlainHandshake() error { - rb := &SaslHandshakeRequest{"PLAIN"} - req := &request{correlationID: b.correlationID, clientID: b.conf.ClientID, body: rb} - buf, err := encode(req, b.conf.MetricRegistry) - if err != nil { - return err - } - - err = b.conn.SetWriteDeadline(time.Now().Add(b.conf.Net.WriteTimeout)) - if err != nil { - return err - } - - requestTime := time.Now() - bytes, err := b.conn.Write(buf) - b.updateOutgoingCommunicationMetrics(bytes) - if err != nil { - Logger.Printf("Failed to send SASL handshake %s: %s\n", b.addr, err.Error()) - return err - } - b.correlationID++ - //wait for the response - header := make([]byte, 8) // response header - _, err = io.ReadFull(b.conn, header) - if err != nil { - Logger.Printf("Failed to read SASL handshake header : %s\n", err.Error()) - return err - } - length := binary.BigEndian.Uint32(header[:4]) - payload := make([]byte, length-4) - n, err := io.ReadFull(b.conn, payload) - if err != nil { - Logger.Printf("Failed to read SASL handshake payload : %s\n", err.Error()) - return err - } - b.updateIncomingCommunicationMetrics(n+8, time.Since(requestTime)) - res := &SaslHandshakeResponse{} - err = versionedDecode(payload, res, 0) - if err != nil { - Logger.Printf("Failed to parse SASL handshake : %s\n", err.Error()) - return err - } - if res.Err != ErrNoError { - Logger.Printf("Invalid SASL Mechanism : %s\n", res.Err.Error()) - return res.Err - } - Logger.Print("Successful SASL handshake") - return nil -} - -// Kafka 0.10.0 plans to support SASL Plain and Kerberos as per PR #812 (KIP-43)/(JIRA KAFKA-3149) -// Some hosted kafka services such as IBM Message Hub already offer SASL/PLAIN auth with Kafka 0.9 -// -// In SASL Plain, Kafka expects the auth header to be in the following format -// Message format (from https://tools.ietf.org/html/rfc4616): -// -// message = [authzid] UTF8NUL authcid UTF8NUL passwd -// authcid = 1*SAFE ; MUST accept up to 255 octets -// authzid = 1*SAFE ; MUST accept up to 255 octets -// passwd = 1*SAFE ; MUST accept up to 255 octets -// UTF8NUL = %x00 ; UTF-8 encoded NUL character -// -// SAFE = UTF1 / UTF2 / UTF3 / UTF4 -// ;; any UTF-8 encoded Unicode character except NUL -// -// When credentials are valid, Kafka returns a 4 byte array of null characters. -// When credentials are invalid, Kafka closes the connection. This does not seem to be the ideal way -// of responding to bad credentials but thats how its being done today. -func (b *Broker) sendAndReceiveSASLPlainAuth() error { - if b.conf.Net.SASL.Handshake { - handshakeErr := b.sendAndReceiveSASLPlainHandshake() - if handshakeErr != nil { - Logger.Printf("Error while performing SASL handshake %s\n", b.addr) - return handshakeErr - } - } - length := 1 + len(b.conf.Net.SASL.User) + 1 + len(b.conf.Net.SASL.Password) - authBytes := make([]byte, length+4) //4 byte length header + auth data - binary.BigEndian.PutUint32(authBytes, uint32(length)) - copy(authBytes[4:], []byte("\x00"+b.conf.Net.SASL.User+"\x00"+b.conf.Net.SASL.Password)) - - err := b.conn.SetWriteDeadline(time.Now().Add(b.conf.Net.WriteTimeout)) - if err != nil { - Logger.Printf("Failed to set write deadline when doing SASL auth with broker %s: %s\n", b.addr, err.Error()) - return err - } - - requestTime := time.Now() - bytesWritten, err := b.conn.Write(authBytes) - b.updateOutgoingCommunicationMetrics(bytesWritten) - if err != nil { - Logger.Printf("Failed to write SASL auth header to broker %s: %s\n", b.addr, err.Error()) - return err - } - - header := make([]byte, 4) - n, err := io.ReadFull(b.conn, header) - b.updateIncomingCommunicationMetrics(n, time.Since(requestTime)) - // If the credentials are valid, we would get a 4 byte response filled with null characters. - // Otherwise, the broker closes the connection and we get an EOF - if err != nil { - Logger.Printf("Failed to read response while authenticating with SASL to broker %s: %s\n", b.addr, err.Error()) - return err - } - - Logger.Printf("SASL authentication successful with broker %s:%v - %v\n", b.addr, n, header) - return nil -} - -func (b *Broker) updateIncomingCommunicationMetrics(bytes int, requestLatency time.Duration) { - b.updateRequestLatencyMetrics(requestLatency) - b.responseRate.Mark(1) - if b.brokerResponseRate != nil { - b.brokerResponseRate.Mark(1) - } - responseSize := int64(bytes) - b.incomingByteRate.Mark(responseSize) - if b.brokerIncomingByteRate != nil { - b.brokerIncomingByteRate.Mark(responseSize) - } - b.responseSize.Update(responseSize) - if b.brokerResponseSize != nil { - b.brokerResponseSize.Update(responseSize) - } -} - -func (b *Broker) updateRequestLatencyMetrics(requestLatency time.Duration) { - requestLatencyInMs := int64(requestLatency / time.Millisecond) - b.requestLatency.Update(requestLatencyInMs) - if b.brokerRequestLatency != nil { - b.brokerRequestLatency.Update(requestLatencyInMs) - } -} - -func (b *Broker) updateOutgoingCommunicationMetrics(bytes int) { - b.requestRate.Mark(1) - if b.brokerRequestRate != nil { - b.brokerRequestRate.Mark(1) - } - requestSize := int64(bytes) - b.outgoingByteRate.Mark(requestSize) - if b.brokerOutgoingByteRate != nil { - b.brokerOutgoingByteRate.Mark(requestSize) - } - b.requestSize.Update(requestSize) - if b.brokerRequestSize != nil { - b.brokerRequestSize.Update(requestSize) - } -} diff --git a/vendor/github.com/Shopify/sarama/client.go b/vendor/github.com/Shopify/sarama/client.go deleted file mode 100644 index 019cb43735a..00000000000 --- a/vendor/github.com/Shopify/sarama/client.go +++ /dev/null @@ -1,846 +0,0 @@ -package sarama - -import ( - "math/rand" - "sort" - "sync" - "time" -) - -// Client is a generic Kafka client. It manages connections to one or more Kafka brokers. -// You MUST call Close() on a client to avoid leaks, it will not be garbage-collected -// automatically when it passes out of scope. It is safe to share a client amongst many -// users, however Kafka will process requests from a single client strictly in serial, -// so it is generally more efficient to use the default one client per producer/consumer. -type Client interface { - // Config returns the Config struct of the client. This struct should not be - // altered after it has been created. - Config() *Config - - // Controller returns the cluster controller broker. - Controller() (*Broker, error) - - // Brokers returns the current set of active brokers as retrieved from cluster metadata. - Brokers() []*Broker - - // Topics returns the set of available topics as retrieved from cluster metadata. - Topics() ([]string, error) - - // Partitions returns the sorted list of all partition IDs for the given topic. - Partitions(topic string) ([]int32, error) - - // WritablePartitions returns the sorted list of all writable partition IDs for - // the given topic, where "writable" means "having a valid leader accepting - // writes". - WritablePartitions(topic string) ([]int32, error) - - // Leader returns the broker object that is the leader of the current - // topic/partition, as determined by querying the cluster metadata. - Leader(topic string, partitionID int32) (*Broker, error) - - // Replicas returns the set of all replica IDs for the given partition. - Replicas(topic string, partitionID int32) ([]int32, error) - - // InSyncReplicas returns the set of all in-sync replica IDs for the given - // partition. In-sync replicas are replicas which are fully caught up with - // the partition leader. - InSyncReplicas(topic string, partitionID int32) ([]int32, error) - - // RefreshMetadata takes a list of topics and queries the cluster to refresh the - // available metadata for those topics. If no topics are provided, it will refresh - // metadata for all topics. - RefreshMetadata(topics ...string) error - - // GetOffset queries the cluster to get the most recent available offset at the - // given time (in milliseconds) on the topic/partition combination. - // Time should be OffsetOldest for the earliest available offset, - // OffsetNewest for the offset of the message that will be produced next, or a time. - GetOffset(topic string, partitionID int32, time int64) (int64, error) - - // Coordinator returns the coordinating broker for a consumer group. It will - // return a locally cached value if it's available. You can call - // RefreshCoordinator to update the cached value. This function only works on - // Kafka 0.8.2 and higher. - Coordinator(consumerGroup string) (*Broker, error) - - // RefreshCoordinator retrieves the coordinator for a consumer group and stores it - // in local cache. This function only works on Kafka 0.8.2 and higher. - RefreshCoordinator(consumerGroup string) error - - // Close shuts down all broker connections managed by this client. It is required - // to call this function before a client object passes out of scope, as it will - // otherwise leak memory. You must close any Producers or Consumers using a client - // before you close the client. - Close() error - - // Closed returns true if the client has already had Close called on it - Closed() bool -} - -const ( - // OffsetNewest stands for the log head offset, i.e. the offset that will be - // assigned to the next message that will be produced to the partition. You - // can send this to a client's GetOffset method to get this offset, or when - // calling ConsumePartition to start consuming new messages. - OffsetNewest int64 = -1 - // OffsetOldest stands for the oldest offset available on the broker for a - // partition. You can send this to a client's GetOffset method to get this - // offset, or when calling ConsumePartition to start consuming from the - // oldest offset that is still available on the broker. - OffsetOldest int64 = -2 -) - -type client struct { - conf *Config - closer, closed chan none // for shutting down background metadata updater - - // the broker addresses given to us through the constructor are not guaranteed to be returned in - // the cluster metadata (I *think* it only returns brokers who are currently leading partitions?) - // so we store them separately - seedBrokers []*Broker - deadSeeds []*Broker - - controllerID int32 // cluster controller broker id - brokers map[int32]*Broker // maps broker ids to brokers - metadata map[string]map[int32]*PartitionMetadata // maps topics to partition ids to metadata - coordinators map[string]int32 // Maps consumer group names to coordinating broker IDs - - // If the number of partitions is large, we can get some churn calling cachedPartitions, - // so the result is cached. It is important to update this value whenever metadata is changed - cachedPartitionsResults map[string][maxPartitionIndex][]int32 - - lock sync.RWMutex // protects access to the maps that hold cluster state. -} - -// NewClient creates a new Client. It connects to one of the given broker addresses -// and uses that broker to automatically fetch metadata on the rest of the kafka cluster. If metadata cannot -// be retrieved from any of the given broker addresses, the client is not created. -func NewClient(addrs []string, conf *Config) (Client, error) { - Logger.Println("Initializing new client") - - if conf == nil { - conf = NewConfig() - } - - if err := conf.Validate(); err != nil { - return nil, err - } - - if len(addrs) < 1 { - return nil, ConfigurationError("You must provide at least one broker address") - } - - client := &client{ - conf: conf, - closer: make(chan none), - closed: make(chan none), - brokers: make(map[int32]*Broker), - metadata: make(map[string]map[int32]*PartitionMetadata), - cachedPartitionsResults: make(map[string][maxPartitionIndex][]int32), - coordinators: make(map[string]int32), - } - - random := rand.New(rand.NewSource(time.Now().UnixNano())) - for _, index := range random.Perm(len(addrs)) { - client.seedBrokers = append(client.seedBrokers, NewBroker(addrs[index])) - } - - if conf.Metadata.Full { - // do an initial fetch of all cluster metadata by specifying an empty list of topics - err := client.RefreshMetadata() - switch err { - case nil: - break - case ErrLeaderNotAvailable, ErrReplicaNotAvailable, ErrTopicAuthorizationFailed, ErrClusterAuthorizationFailed: - // indicates that maybe part of the cluster is down, but is not fatal to creating the client - Logger.Println(err) - default: - close(client.closed) // we haven't started the background updater yet, so we have to do this manually - _ = client.Close() - return nil, err - } - } - go withRecover(client.backgroundMetadataUpdater) - - Logger.Println("Successfully initialized new client") - - return client, nil -} - -func (client *client) Config() *Config { - return client.conf -} - -func (client *client) Brokers() []*Broker { - client.lock.RLock() - defer client.lock.RUnlock() - brokers := make([]*Broker, 0) - for _, broker := range client.brokers { - brokers = append(brokers, broker) - } - return brokers -} - -func (client *client) Close() error { - if client.Closed() { - // Chances are this is being called from a defer() and the error will go unobserved - // so we go ahead and log the event in this case. - Logger.Printf("Close() called on already closed client") - return ErrClosedClient - } - - // shutdown and wait for the background thread before we take the lock, to avoid races - close(client.closer) - <-client.closed - - client.lock.Lock() - defer client.lock.Unlock() - Logger.Println("Closing Client") - - for _, broker := range client.brokers { - safeAsyncClose(broker) - } - - for _, broker := range client.seedBrokers { - safeAsyncClose(broker) - } - - client.brokers = nil - client.metadata = nil - - return nil -} - -func (client *client) Closed() bool { - return client.brokers == nil -} - -func (client *client) Topics() ([]string, error) { - if client.Closed() { - return nil, ErrClosedClient - } - - client.lock.RLock() - defer client.lock.RUnlock() - - ret := make([]string, 0, len(client.metadata)) - for topic := range client.metadata { - ret = append(ret, topic) - } - - return ret, nil -} - -func (client *client) Partitions(topic string) ([]int32, error) { - if client.Closed() { - return nil, ErrClosedClient - } - - partitions := client.cachedPartitions(topic, allPartitions) - - if len(partitions) == 0 { - err := client.RefreshMetadata(topic) - if err != nil { - return nil, err - } - partitions = client.cachedPartitions(topic, allPartitions) - } - - if partitions == nil { - return nil, ErrUnknownTopicOrPartition - } - - return partitions, nil -} - -func (client *client) WritablePartitions(topic string) ([]int32, error) { - if client.Closed() { - return nil, ErrClosedClient - } - - partitions := client.cachedPartitions(topic, writablePartitions) - - // len==0 catches when it's nil (no such topic) and the odd case when every single - // partition is undergoing leader election simultaneously. Callers have to be able to handle - // this function returning an empty slice (which is a valid return value) but catching it - // here the first time (note we *don't* catch it below where we return ErrUnknownTopicOrPartition) triggers - // a metadata refresh as a nicety so callers can just try again and don't have to manually - // trigger a refresh (otherwise they'd just keep getting a stale cached copy). - if len(partitions) == 0 { - err := client.RefreshMetadata(topic) - if err != nil { - return nil, err - } - partitions = client.cachedPartitions(topic, writablePartitions) - } - - if partitions == nil { - return nil, ErrUnknownTopicOrPartition - } - - return partitions, nil -} - -func (client *client) Replicas(topic string, partitionID int32) ([]int32, error) { - if client.Closed() { - return nil, ErrClosedClient - } - - metadata := client.cachedMetadata(topic, partitionID) - - if metadata == nil { - err := client.RefreshMetadata(topic) - if err != nil { - return nil, err - } - metadata = client.cachedMetadata(topic, partitionID) - } - - if metadata == nil { - return nil, ErrUnknownTopicOrPartition - } - - if metadata.Err == ErrReplicaNotAvailable { - return dupInt32Slice(metadata.Replicas), metadata.Err - } - return dupInt32Slice(metadata.Replicas), nil -} - -func (client *client) InSyncReplicas(topic string, partitionID int32) ([]int32, error) { - if client.Closed() { - return nil, ErrClosedClient - } - - metadata := client.cachedMetadata(topic, partitionID) - - if metadata == nil { - err := client.RefreshMetadata(topic) - if err != nil { - return nil, err - } - metadata = client.cachedMetadata(topic, partitionID) - } - - if metadata == nil { - return nil, ErrUnknownTopicOrPartition - } - - if metadata.Err == ErrReplicaNotAvailable { - return dupInt32Slice(metadata.Isr), metadata.Err - } - return dupInt32Slice(metadata.Isr), nil -} - -func (client *client) Leader(topic string, partitionID int32) (*Broker, error) { - if client.Closed() { - return nil, ErrClosedClient - } - - leader, err := client.cachedLeader(topic, partitionID) - - if leader == nil { - err = client.RefreshMetadata(topic) - if err != nil { - return nil, err - } - leader, err = client.cachedLeader(topic, partitionID) - } - - return leader, err -} - -func (client *client) RefreshMetadata(topics ...string) error { - if client.Closed() { - return ErrClosedClient - } - - // Prior to 0.8.2, Kafka will throw exceptions on an empty topic and not return a proper - // error. This handles the case by returning an error instead of sending it - // off to Kafka. See: https://github.com/Shopify/sarama/pull/38#issuecomment-26362310 - for _, topic := range topics { - if len(topic) == 0 { - return ErrInvalidTopic // this is the error that 0.8.2 and later correctly return - } - } - - return client.tryRefreshMetadata(topics, client.conf.Metadata.Retry.Max) -} - -func (client *client) GetOffset(topic string, partitionID int32, time int64) (int64, error) { - if client.Closed() { - return -1, ErrClosedClient - } - - offset, err := client.getOffset(topic, partitionID, time) - - if err != nil { - if err := client.RefreshMetadata(topic); err != nil { - return -1, err - } - return client.getOffset(topic, partitionID, time) - } - - return offset, err -} - -func (client *client) Controller() (*Broker, error) { - if client.Closed() { - return nil, ErrClosedClient - } - - controller := client.cachedController() - if controller == nil { - if err := client.refreshMetadata(); err != nil { - return nil, err - } - controller = client.cachedController() - } - - if controller == nil { - return nil, ErrControllerNotAvailable - } - - _ = controller.Open(client.conf) - return controller, nil -} - -func (client *client) Coordinator(consumerGroup string) (*Broker, error) { - if client.Closed() { - return nil, ErrClosedClient - } - - coordinator := client.cachedCoordinator(consumerGroup) - - if coordinator == nil { - if err := client.RefreshCoordinator(consumerGroup); err != nil { - return nil, err - } - coordinator = client.cachedCoordinator(consumerGroup) - } - - if coordinator == nil { - return nil, ErrConsumerCoordinatorNotAvailable - } - - _ = coordinator.Open(client.conf) - return coordinator, nil -} - -func (client *client) RefreshCoordinator(consumerGroup string) error { - if client.Closed() { - return ErrClosedClient - } - - response, err := client.getConsumerMetadata(consumerGroup, client.conf.Metadata.Retry.Max) - if err != nil { - return err - } - - client.lock.Lock() - defer client.lock.Unlock() - client.registerBroker(response.Coordinator) - client.coordinators[consumerGroup] = response.Coordinator.ID() - return nil -} - -// private broker management helpers - -// registerBroker makes sure a broker received by a Metadata or Coordinator request is registered -// in the brokers map. It returns the broker that is registered, which may be the provided broker, -// or a previously registered Broker instance. You must hold the write lock before calling this function. -func (client *client) registerBroker(broker *Broker) { - if client.brokers[broker.ID()] == nil { - client.brokers[broker.ID()] = broker - Logger.Printf("client/brokers registered new broker #%d at %s", broker.ID(), broker.Addr()) - } else if broker.Addr() != client.brokers[broker.ID()].Addr() { - safeAsyncClose(client.brokers[broker.ID()]) - client.brokers[broker.ID()] = broker - Logger.Printf("client/brokers replaced registered broker #%d with %s", broker.ID(), broker.Addr()) - } -} - -// deregisterBroker removes a broker from the seedsBroker list, and if it's -// not the seedbroker, removes it from brokers map completely. -func (client *client) deregisterBroker(broker *Broker) { - client.lock.Lock() - defer client.lock.Unlock() - - if len(client.seedBrokers) > 0 && broker == client.seedBrokers[0] { - client.deadSeeds = append(client.deadSeeds, broker) - client.seedBrokers = client.seedBrokers[1:] - } else { - // we do this so that our loop in `tryRefreshMetadata` doesn't go on forever, - // but we really shouldn't have to; once that loop is made better this case can be - // removed, and the function generally can be renamed from `deregisterBroker` to - // `nextSeedBroker` or something - Logger.Printf("client/brokers deregistered broker #%d at %s", broker.ID(), broker.Addr()) - delete(client.brokers, broker.ID()) - } -} - -func (client *client) resurrectDeadBrokers() { - client.lock.Lock() - defer client.lock.Unlock() - - Logger.Printf("client/brokers resurrecting %d dead seed brokers", len(client.deadSeeds)) - client.seedBrokers = append(client.seedBrokers, client.deadSeeds...) - client.deadSeeds = nil -} - -func (client *client) any() *Broker { - client.lock.RLock() - defer client.lock.RUnlock() - - if len(client.seedBrokers) > 0 { - _ = client.seedBrokers[0].Open(client.conf) - return client.seedBrokers[0] - } - - // not guaranteed to be random *or* deterministic - for _, broker := range client.brokers { - _ = broker.Open(client.conf) - return broker - } - - return nil -} - -// private caching/lazy metadata helpers - -type partitionType int - -const ( - allPartitions partitionType = iota - writablePartitions - // If you add any more types, update the partition cache in update() - - // Ensure this is the last partition type value - maxPartitionIndex -) - -func (client *client) cachedMetadata(topic string, partitionID int32) *PartitionMetadata { - client.lock.RLock() - defer client.lock.RUnlock() - - partitions := client.metadata[topic] - if partitions != nil { - return partitions[partitionID] - } - - return nil -} - -func (client *client) cachedPartitions(topic string, partitionSet partitionType) []int32 { - client.lock.RLock() - defer client.lock.RUnlock() - - partitions, exists := client.cachedPartitionsResults[topic] - - if !exists { - return nil - } - return partitions[partitionSet] -} - -func (client *client) setPartitionCache(topic string, partitionSet partitionType) []int32 { - partitions := client.metadata[topic] - - if partitions == nil { - return nil - } - - ret := make([]int32, 0, len(partitions)) - for _, partition := range partitions { - if partitionSet == writablePartitions && partition.Err == ErrLeaderNotAvailable { - continue - } - ret = append(ret, partition.ID) - } - - sort.Sort(int32Slice(ret)) - return ret -} - -func (client *client) cachedLeader(topic string, partitionID int32) (*Broker, error) { - client.lock.RLock() - defer client.lock.RUnlock() - - partitions := client.metadata[topic] - if partitions != nil { - metadata, ok := partitions[partitionID] - if ok { - if metadata.Err == ErrLeaderNotAvailable { - return nil, ErrLeaderNotAvailable - } - b := client.brokers[metadata.Leader] - if b == nil { - return nil, ErrLeaderNotAvailable - } - _ = b.Open(client.conf) - return b, nil - } - } - - return nil, ErrUnknownTopicOrPartition -} - -func (client *client) getOffset(topic string, partitionID int32, time int64) (int64, error) { - broker, err := client.Leader(topic, partitionID) - if err != nil { - return -1, err - } - - request := &OffsetRequest{} - if client.conf.Version.IsAtLeast(V0_10_1_0) { - request.Version = 1 - } - request.AddBlock(topic, partitionID, time, 1) - - response, err := broker.GetAvailableOffsets(request) - if err != nil { - _ = broker.Close() - return -1, err - } - - block := response.GetBlock(topic, partitionID) - if block == nil { - _ = broker.Close() - return -1, ErrIncompleteResponse - } - if block.Err != ErrNoError { - return -1, block.Err - } - if len(block.Offsets) != 1 { - return -1, ErrOffsetOutOfRange - } - - return block.Offsets[0], nil -} - -// core metadata update logic - -func (client *client) backgroundMetadataUpdater() { - defer close(client.closed) - - if client.conf.Metadata.RefreshFrequency == time.Duration(0) { - return - } - - ticker := time.NewTicker(client.conf.Metadata.RefreshFrequency) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - if err := client.refreshMetadata(); err != nil { - Logger.Println("Client background metadata update:", err) - } - case <-client.closer: - return - } - } -} - -func (client *client) refreshMetadata() error { - topics := []string{} - - if !client.conf.Metadata.Full { - if specificTopics, err := client.Topics(); err != nil { - return err - } else if len(specificTopics) == 0 { - return ErrNoTopicsToUpdateMetadata - } else { - topics = specificTopics - } - } - - if err := client.RefreshMetadata(topics...); err != nil { - return err - } - - return nil -} - -func (client *client) tryRefreshMetadata(topics []string, attemptsRemaining int) error { - retry := func(err error) error { - if attemptsRemaining > 0 { - Logger.Printf("client/metadata retrying after %dms... (%d attempts remaining)\n", client.conf.Metadata.Retry.Backoff/time.Millisecond, attemptsRemaining) - time.Sleep(client.conf.Metadata.Retry.Backoff) - return client.tryRefreshMetadata(topics, attemptsRemaining-1) - } - return err - } - - for broker := client.any(); broker != nil; broker = client.any() { - if len(topics) > 0 { - Logger.Printf("client/metadata fetching metadata for %v from broker %s\n", topics, broker.addr) - } else { - Logger.Printf("client/metadata fetching metadata for all topics from broker %s\n", broker.addr) - } - - req := &MetadataRequest{Topics: topics} - if client.conf.Version.IsAtLeast(V0_10_0_0) { - req.Version = 1 - } - response, err := broker.GetMetadata(req) - - switch err.(type) { - case nil: - allKnownMetaData := len(topics) == 0 - // valid response, use it - shouldRetry, err := client.updateMetadata(response, allKnownMetaData) - if shouldRetry { - Logger.Println("client/metadata found some partitions to be leaderless") - return retry(err) // note: err can be nil - } - return err - - case PacketEncodingError: - // didn't even send, return the error - return err - default: - // some other error, remove that broker and try again - Logger.Println("client/metadata got error from broker while fetching metadata:", err) - _ = broker.Close() - client.deregisterBroker(broker) - } - } - - Logger.Println("client/metadata no available broker to send metadata request to") - client.resurrectDeadBrokers() - return retry(ErrOutOfBrokers) -} - -// if no fatal error, returns a list of topics that need retrying due to ErrLeaderNotAvailable -func (client *client) updateMetadata(data *MetadataResponse, allKnownMetaData bool) (retry bool, err error) { - client.lock.Lock() - defer client.lock.Unlock() - - // For all the brokers we received: - // - if it is a new ID, save it - // - if it is an existing ID, but the address we have is stale, discard the old one and save it - // - otherwise ignore it, replacing our existing one would just bounce the connection - for _, broker := range data.Brokers { - client.registerBroker(broker) - } - - client.controllerID = data.ControllerID - - if allKnownMetaData { - client.metadata = make(map[string]map[int32]*PartitionMetadata) - client.cachedPartitionsResults = make(map[string][maxPartitionIndex][]int32) - } - for _, topic := range data.Topics { - delete(client.metadata, topic.Name) - delete(client.cachedPartitionsResults, topic.Name) - - switch topic.Err { - case ErrNoError: - break - case ErrInvalidTopic, ErrTopicAuthorizationFailed: // don't retry, don't store partial results - err = topic.Err - continue - case ErrUnknownTopicOrPartition: // retry, do not store partial partition results - err = topic.Err - retry = true - continue - case ErrLeaderNotAvailable: // retry, but store partial partition results - retry = true - break - default: // don't retry, don't store partial results - Logger.Printf("Unexpected topic-level metadata error: %s", topic.Err) - err = topic.Err - continue - } - - client.metadata[topic.Name] = make(map[int32]*PartitionMetadata, len(topic.Partitions)) - for _, partition := range topic.Partitions { - client.metadata[topic.Name][partition.ID] = partition - if partition.Err == ErrLeaderNotAvailable { - retry = true - } - } - - var partitionCache [maxPartitionIndex][]int32 - partitionCache[allPartitions] = client.setPartitionCache(topic.Name, allPartitions) - partitionCache[writablePartitions] = client.setPartitionCache(topic.Name, writablePartitions) - client.cachedPartitionsResults[topic.Name] = partitionCache - } - - return -} - -func (client *client) cachedCoordinator(consumerGroup string) *Broker { - client.lock.RLock() - defer client.lock.RUnlock() - if coordinatorID, ok := client.coordinators[consumerGroup]; ok { - return client.brokers[coordinatorID] - } - return nil -} - -func (client *client) cachedController() *Broker { - client.lock.RLock() - defer client.lock.RUnlock() - - return client.brokers[client.controllerID] -} - -func (client *client) getConsumerMetadata(consumerGroup string, attemptsRemaining int) (*FindCoordinatorResponse, error) { - retry := func(err error) (*FindCoordinatorResponse, error) { - if attemptsRemaining > 0 { - Logger.Printf("client/coordinator retrying after %dms... (%d attempts remaining)\n", client.conf.Metadata.Retry.Backoff/time.Millisecond, attemptsRemaining) - time.Sleep(client.conf.Metadata.Retry.Backoff) - return client.getConsumerMetadata(consumerGroup, attemptsRemaining-1) - } - return nil, err - } - - for broker := client.any(); broker != nil; broker = client.any() { - Logger.Printf("client/coordinator requesting coordinator for consumergroup %s from %s\n", consumerGroup, broker.Addr()) - - request := new(FindCoordinatorRequest) - request.CoordinatorKey = consumerGroup - request.CoordinatorType = CoordinatorGroup - - response, err := broker.FindCoordinator(request) - - if err != nil { - Logger.Printf("client/coordinator request to broker %s failed: %s\n", broker.Addr(), err) - - switch err.(type) { - case PacketEncodingError: - return nil, err - default: - _ = broker.Close() - client.deregisterBroker(broker) - continue - } - } - - switch response.Err { - case ErrNoError: - Logger.Printf("client/coordinator coordinator for consumergroup %s is #%d (%s)\n", consumerGroup, response.Coordinator.ID(), response.Coordinator.Addr()) - return response, nil - - case ErrConsumerCoordinatorNotAvailable: - Logger.Printf("client/coordinator coordinator for consumer group %s is not available\n", consumerGroup) - - // This is very ugly, but this scenario will only happen once per cluster. - // The __consumer_offsets topic only has to be created one time. - // The number of partitions not configurable, but partition 0 should always exist. - if _, err := client.Leader("__consumer_offsets", 0); err != nil { - Logger.Printf("client/coordinator the __consumer_offsets topic is not initialized completely yet. Waiting 2 seconds...\n") - time.Sleep(2 * time.Second) - } - - return retry(ErrConsumerCoordinatorNotAvailable) - default: - return nil, response.Err - } - } - - Logger.Println("client/coordinator no available broker to send consumer metadata request to") - client.resurrectDeadBrokers() - return retry(ErrOutOfBrokers) -} diff --git a/vendor/github.com/Shopify/sarama/config.go b/vendor/github.com/Shopify/sarama/config.go deleted file mode 100644 index a564b5c23e4..00000000000 --- a/vendor/github.com/Shopify/sarama/config.go +++ /dev/null @@ -1,458 +0,0 @@ -package sarama - -import ( - "compress/gzip" - "crypto/tls" - "fmt" - "io/ioutil" - "regexp" - "time" - - "github.com/rcrowley/go-metrics" -) - -const defaultClientID = "sarama" - -var validID = regexp.MustCompile(`\A[A-Za-z0-9._-]+\z`) - -// Config is used to pass multiple configuration options to Sarama's constructors. -type Config struct { - // Net is the namespace for network-level properties used by the Broker, and - // shared by the Client/Producer/Consumer. - Net struct { - // How many outstanding requests a connection is allowed to have before - // sending on it blocks (default 5). - MaxOpenRequests int - - // All three of the below configurations are similar to the - // `socket.timeout.ms` setting in JVM kafka. All of them default - // to 30 seconds. - DialTimeout time.Duration // How long to wait for the initial connection. - ReadTimeout time.Duration // How long to wait for a response. - WriteTimeout time.Duration // How long to wait for a transmit. - - TLS struct { - // Whether or not to use TLS when connecting to the broker - // (defaults to false). - Enable bool - // The TLS configuration to use for secure connections if - // enabled (defaults to nil). - Config *tls.Config - } - - // SASL based authentication with broker. While there are multiple SASL authentication methods - // the current implementation is limited to plaintext (SASL/PLAIN) authentication - SASL struct { - // Whether or not to use SASL authentication when connecting to the broker - // (defaults to false). - Enable bool - // Whether or not to send the Kafka SASL handshake first if enabled - // (defaults to true). You should only set this to false if you're using - // a non-Kafka SASL proxy. - Handshake bool - //username and password for SASL/PLAIN authentication - User string - Password string - } - - // KeepAlive specifies the keep-alive period for an active network connection. - // If zero, keep-alives are disabled. (default is 0: disabled). - KeepAlive time.Duration - } - - // Metadata is the namespace for metadata management properties used by the - // Client, and shared by the Producer/Consumer. - Metadata struct { - Retry struct { - // The total number of times to retry a metadata request when the - // cluster is in the middle of a leader election (default 3). - Max int - // How long to wait for leader election to occur before retrying - // (default 250ms). Similar to the JVM's `retry.backoff.ms`. - Backoff time.Duration - } - // How frequently to refresh the cluster metadata in the background. - // Defaults to 10 minutes. Set to 0 to disable. Similar to - // `topic.metadata.refresh.interval.ms` in the JVM version. - RefreshFrequency time.Duration - - // Whether to maintain a full set of metadata for all topics, or just - // the minimal set that has been necessary so far. The full set is simpler - // and usually more convenient, but can take up a substantial amount of - // memory if you have many topics and partitions. Defaults to true. - Full bool - } - - // Producer is the namespace for configuration related to producing messages, - // used by the Producer. - Producer struct { - // The maximum permitted size of a message (defaults to 1000000). Should be - // set equal to or smaller than the broker's `message.max.bytes`. - MaxMessageBytes int - // The level of acknowledgement reliability needed from the broker (defaults - // to WaitForLocal). Equivalent to the `request.required.acks` setting of the - // JVM producer. - RequiredAcks RequiredAcks - // The maximum duration the broker will wait the receipt of the number of - // RequiredAcks (defaults to 10 seconds). This is only relevant when - // RequiredAcks is set to WaitForAll or a number > 1. Only supports - // millisecond resolution, nanoseconds will be truncated. Equivalent to - // the JVM producer's `request.timeout.ms` setting. - Timeout time.Duration - // The type of compression to use on messages (defaults to no compression). - // Similar to `compression.codec` setting of the JVM producer. - Compression CompressionCodec - // The level of compression to use on messages. The meaning depends - // on the actual compression type used and defaults to default compression - // level for the codec. - CompressionLevel int - // Generates partitioners for choosing the partition to send messages to - // (defaults to hashing the message key). Similar to the `partitioner.class` - // setting for the JVM producer. - Partitioner PartitionerConstructor - - // Return specifies what channels will be populated. If they are set to true, - // you must read from the respective channels to prevent deadlock. If, - // however, this config is used to create a `SyncProducer`, both must be set - // to true and you shall not read from the channels since the producer does - // this internally. - Return struct { - // If enabled, successfully delivered messages will be returned on the - // Successes channel (default disabled). - Successes bool - - // If enabled, messages that failed to deliver will be returned on the - // Errors channel, including error (default enabled). - Errors bool - } - - // The following config options control how often messages are batched up and - // sent to the broker. By default, messages are sent as fast as possible, and - // all messages received while the current batch is in-flight are placed - // into the subsequent batch. - Flush struct { - // The best-effort number of bytes needed to trigger a flush. Use the - // global sarama.MaxRequestSize to set a hard upper limit. - Bytes int - // The best-effort number of messages needed to trigger a flush. Use - // `MaxMessages` to set a hard upper limit. - Messages int - // The best-effort frequency of flushes. Equivalent to - // `queue.buffering.max.ms` setting of JVM producer. - Frequency time.Duration - // The maximum number of messages the producer will send in a single - // broker request. Defaults to 0 for unlimited. Similar to - // `queue.buffering.max.messages` in the JVM producer. - MaxMessages int - } - - Retry struct { - // The total number of times to retry sending a message (default 3). - // Similar to the `message.send.max.retries` setting of the JVM producer. - Max int - // How long to wait for the cluster to settle between retries - // (default 100ms). Similar to the `retry.backoff.ms` setting of the - // JVM producer. - Backoff time.Duration - } - } - - // Consumer is the namespace for configuration related to consuming messages, - // used by the Consumer. - // - // Note that Sarama's Consumer type does not currently support automatic - // consumer-group rebalancing and offset tracking. For Zookeeper-based - // tracking (Kafka 0.8.2 and earlier), the https://github.com/wvanbergen/kafka - // library builds on Sarama to add this support. For Kafka-based tracking - // (Kafka 0.9 and later), the https://github.com/bsm/sarama-cluster library - // builds on Sarama to add this support. - Consumer struct { - Retry struct { - // How long to wait after a failing to read from a partition before - // trying again (default 2s). - Backoff time.Duration - } - - // Fetch is the namespace for controlling how many bytes are retrieved by any - // given request. - Fetch struct { - // The minimum number of message bytes to fetch in a request - the broker - // will wait until at least this many are available. The default is 1, - // as 0 causes the consumer to spin when no messages are available. - // Equivalent to the JVM's `fetch.min.bytes`. - Min int32 - // The default number of message bytes to fetch from the broker in each - // request (default 1MB). This should be larger than the majority of - // your messages, or else the consumer will spend a lot of time - // negotiating sizes and not actually consuming. Similar to the JVM's - // `fetch.message.max.bytes`. - Default int32 - // The maximum number of message bytes to fetch from the broker in a - // single request. Messages larger than this will return - // ErrMessageTooLarge and will not be consumable, so you must be sure - // this is at least as large as your largest message. Defaults to 0 - // (no limit). Similar to the JVM's `fetch.message.max.bytes`. The - // global `sarama.MaxResponseSize` still applies. - Max int32 - } - // The maximum amount of time the broker will wait for Consumer.Fetch.Min - // bytes to become available before it returns fewer than that anyways. The - // default is 250ms, since 0 causes the consumer to spin when no events are - // available. 100-500ms is a reasonable range for most cases. Kafka only - // supports precision up to milliseconds; nanoseconds will be truncated. - // Equivalent to the JVM's `fetch.wait.max.ms`. - MaxWaitTime time.Duration - - // The maximum amount of time the consumer expects a message takes to - // process for the user. If writing to the Messages channel takes longer - // than this, that partition will stop fetching more messages until it - // can proceed again. - // Note that, since the Messages channel is buffered, the actual grace time is - // (MaxProcessingTime * ChanneBufferSize). Defaults to 100ms. - // If a message is not written to the Messages channel between two ticks - // of the expiryTicker then a timeout is detected. - // Using a ticker instead of a timer to detect timeouts should typically - // result in many fewer calls to Timer functions which may result in a - // significant performance improvement if many messages are being sent - // and timeouts are infrequent. - // The disadvantage of using a ticker instead of a timer is that - // timeouts will be less accurate. That is, the effective timeout could - // be between `MaxProcessingTime` and `2 * MaxProcessingTime`. For - // example, if `MaxProcessingTime` is 100ms then a delay of 180ms - // between two messages being sent may not be recognized as a timeout. - MaxProcessingTime time.Duration - - // Return specifies what channels will be populated. If they are set to true, - // you must read from them to prevent deadlock. - Return struct { - // If enabled, any errors that occurred while consuming are returned on - // the Errors channel (default disabled). - Errors bool - } - - // Offsets specifies configuration for how and when to commit consumed - // offsets. This currently requires the manual use of an OffsetManager - // but will eventually be automated. - Offsets struct { - // How frequently to commit updated offsets. Defaults to 1s. - CommitInterval time.Duration - - // The initial offset to use if no offset was previously committed. - // Should be OffsetNewest or OffsetOldest. Defaults to OffsetNewest. - Initial int64 - - // The retention duration for committed offsets. If zero, disabled - // (in which case the `offsets.retention.minutes` option on the - // broker will be used). Kafka only supports precision up to - // milliseconds; nanoseconds will be truncated. Requires Kafka - // broker version 0.9.0 or later. - // (default is 0: disabled). - Retention time.Duration - } - } - - // A user-provided string sent with every request to the brokers for logging, - // debugging, and auditing purposes. Defaults to "sarama", but you should - // probably set it to something specific to your application. - ClientID string - // The number of events to buffer in internal and external channels. This - // permits the producer and consumer to continue processing some messages - // in the background while user code is working, greatly improving throughput. - // Defaults to 256. - ChannelBufferSize int - // The version of Kafka that Sarama will assume it is running against. - // Defaults to the oldest supported stable version. Since Kafka provides - // backwards-compatibility, setting it to a version older than you have - // will not break anything, although it may prevent you from using the - // latest features. Setting it to a version greater than you are actually - // running may lead to random breakage. - Version KafkaVersion - // The registry to define metrics into. - // Defaults to a local registry. - // If you want to disable metrics gathering, set "metrics.UseNilMetrics" to "true" - // prior to starting Sarama. - // See Examples on how to use the metrics registry - MetricRegistry metrics.Registry -} - -// NewConfig returns a new configuration instance with sane defaults. -func NewConfig() *Config { - c := &Config{} - - c.Net.MaxOpenRequests = 5 - c.Net.DialTimeout = 30 * time.Second - c.Net.ReadTimeout = 30 * time.Second - c.Net.WriteTimeout = 30 * time.Second - c.Net.SASL.Handshake = true - - c.Metadata.Retry.Max = 3 - c.Metadata.Retry.Backoff = 250 * time.Millisecond - c.Metadata.RefreshFrequency = 10 * time.Minute - c.Metadata.Full = true - - c.Producer.MaxMessageBytes = 1000000 - c.Producer.RequiredAcks = WaitForLocal - c.Producer.Timeout = 10 * time.Second - c.Producer.Partitioner = NewHashPartitioner - c.Producer.Retry.Max = 3 - c.Producer.Retry.Backoff = 100 * time.Millisecond - c.Producer.Return.Errors = true - c.Producer.CompressionLevel = CompressionLevelDefault - - c.Consumer.Fetch.Min = 1 - c.Consumer.Fetch.Default = 1024 * 1024 - c.Consumer.Retry.Backoff = 2 * time.Second - c.Consumer.MaxWaitTime = 250 * time.Millisecond - c.Consumer.MaxProcessingTime = 100 * time.Millisecond - c.Consumer.Return.Errors = false - c.Consumer.Offsets.CommitInterval = 1 * time.Second - c.Consumer.Offsets.Initial = OffsetNewest - - c.ClientID = defaultClientID - c.ChannelBufferSize = 256 - c.Version = MinVersion - c.MetricRegistry = metrics.NewRegistry() - - return c -} - -// Validate checks a Config instance. It will return a -// ConfigurationError if the specified values don't make sense. -func (c *Config) Validate() error { - // some configuration values should be warned on but not fail completely, do those first - if c.Net.TLS.Enable == false && c.Net.TLS.Config != nil { - Logger.Println("Net.TLS is disabled but a non-nil configuration was provided.") - } - if c.Net.SASL.Enable == false { - if c.Net.SASL.User != "" { - Logger.Println("Net.SASL is disabled but a non-empty username was provided.") - } - if c.Net.SASL.Password != "" { - Logger.Println("Net.SASL is disabled but a non-empty password was provided.") - } - } - if c.Producer.RequiredAcks > 1 { - Logger.Println("Producer.RequiredAcks > 1 is deprecated and will raise an exception with kafka >= 0.8.2.0.") - } - if c.Producer.MaxMessageBytes >= int(MaxRequestSize) { - Logger.Println("Producer.MaxMessageBytes must be smaller than MaxRequestSize; it will be ignored.") - } - if c.Producer.Flush.Bytes >= int(MaxRequestSize) { - Logger.Println("Producer.Flush.Bytes must be smaller than MaxRequestSize; it will be ignored.") - } - if (c.Producer.Flush.Bytes > 0 || c.Producer.Flush.Messages > 0) && c.Producer.Flush.Frequency == 0 { - Logger.Println("Producer.Flush: Bytes or Messages are set, but Frequency is not; messages may not get flushed.") - } - if c.Producer.Timeout%time.Millisecond != 0 { - Logger.Println("Producer.Timeout only supports millisecond resolution; nanoseconds will be truncated.") - } - if c.Consumer.MaxWaitTime < 100*time.Millisecond { - Logger.Println("Consumer.MaxWaitTime is very low, which can cause high CPU and network usage. See documentation for details.") - } - if c.Consumer.MaxWaitTime%time.Millisecond != 0 { - Logger.Println("Consumer.MaxWaitTime only supports millisecond precision; nanoseconds will be truncated.") - } - if c.Consumer.Offsets.Retention%time.Millisecond != 0 { - Logger.Println("Consumer.Offsets.Retention only supports millisecond precision; nanoseconds will be truncated.") - } - if c.ClientID == defaultClientID { - Logger.Println("ClientID is the default of 'sarama', you should consider setting it to something application-specific.") - } - - // validate Net values - switch { - case c.Net.MaxOpenRequests <= 0: - return ConfigurationError("Net.MaxOpenRequests must be > 0") - case c.Net.DialTimeout <= 0: - return ConfigurationError("Net.DialTimeout must be > 0") - case c.Net.ReadTimeout <= 0: - return ConfigurationError("Net.ReadTimeout must be > 0") - case c.Net.WriteTimeout <= 0: - return ConfigurationError("Net.WriteTimeout must be > 0") - case c.Net.KeepAlive < 0: - return ConfigurationError("Net.KeepAlive must be >= 0") - case c.Net.SASL.Enable == true && c.Net.SASL.User == "": - return ConfigurationError("Net.SASL.User must not be empty when SASL is enabled") - case c.Net.SASL.Enable == true && c.Net.SASL.Password == "": - return ConfigurationError("Net.SASL.Password must not be empty when SASL is enabled") - } - - // validate the Metadata values - switch { - case c.Metadata.Retry.Max < 0: - return ConfigurationError("Metadata.Retry.Max must be >= 0") - case c.Metadata.Retry.Backoff < 0: - return ConfigurationError("Metadata.Retry.Backoff must be >= 0") - case c.Metadata.RefreshFrequency < 0: - return ConfigurationError("Metadata.RefreshFrequency must be >= 0") - } - - // validate the Producer values - switch { - case c.Producer.MaxMessageBytes <= 0: - return ConfigurationError("Producer.MaxMessageBytes must be > 0") - case c.Producer.RequiredAcks < -1: - return ConfigurationError("Producer.RequiredAcks must be >= -1") - case c.Producer.Timeout <= 0: - return ConfigurationError("Producer.Timeout must be > 0") - case c.Producer.Partitioner == nil: - return ConfigurationError("Producer.Partitioner must not be nil") - case c.Producer.Flush.Bytes < 0: - return ConfigurationError("Producer.Flush.Bytes must be >= 0") - case c.Producer.Flush.Messages < 0: - return ConfigurationError("Producer.Flush.Messages must be >= 0") - case c.Producer.Flush.Frequency < 0: - return ConfigurationError("Producer.Flush.Frequency must be >= 0") - case c.Producer.Flush.MaxMessages < 0: - return ConfigurationError("Producer.Flush.MaxMessages must be >= 0") - case c.Producer.Flush.MaxMessages > 0 && c.Producer.Flush.MaxMessages < c.Producer.Flush.Messages: - return ConfigurationError("Producer.Flush.MaxMessages must be >= Producer.Flush.Messages when set") - case c.Producer.Retry.Max < 0: - return ConfigurationError("Producer.Retry.Max must be >= 0") - case c.Producer.Retry.Backoff < 0: - return ConfigurationError("Producer.Retry.Backoff must be >= 0") - } - - if c.Producer.Compression == CompressionLZ4 && !c.Version.IsAtLeast(V0_10_0_0) { - return ConfigurationError("lz4 compression requires Version >= V0_10_0_0") - } - - if c.Producer.Compression == CompressionGZIP { - if c.Producer.CompressionLevel != CompressionLevelDefault { - if _, err := gzip.NewWriterLevel(ioutil.Discard, c.Producer.CompressionLevel); err != nil { - return ConfigurationError(fmt.Sprintf("gzip compression does not work with level %d: %v", c.Producer.CompressionLevel, err)) - } - } - } - - // validate the Consumer values - switch { - case c.Consumer.Fetch.Min <= 0: - return ConfigurationError("Consumer.Fetch.Min must be > 0") - case c.Consumer.Fetch.Default <= 0: - return ConfigurationError("Consumer.Fetch.Default must be > 0") - case c.Consumer.Fetch.Max < 0: - return ConfigurationError("Consumer.Fetch.Max must be >= 0") - case c.Consumer.MaxWaitTime < 1*time.Millisecond: - return ConfigurationError("Consumer.MaxWaitTime must be >= 1ms") - case c.Consumer.MaxProcessingTime <= 0: - return ConfigurationError("Consumer.MaxProcessingTime must be > 0") - case c.Consumer.Retry.Backoff < 0: - return ConfigurationError("Consumer.Retry.Backoff must be >= 0") - case c.Consumer.Offsets.CommitInterval <= 0: - return ConfigurationError("Consumer.Offsets.CommitInterval must be > 0") - case c.Consumer.Offsets.Initial != OffsetOldest && c.Consumer.Offsets.Initial != OffsetNewest: - return ConfigurationError("Consumer.Offsets.Initial must be OffsetOldest or OffsetNewest") - - } - - // validate misc shared values - switch { - case c.ChannelBufferSize < 0: - return ConfigurationError("ChannelBufferSize must be >= 0") - case !validID.MatchString(c.ClientID): - return ConfigurationError("ClientID is invalid") - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/config_resource_type.go b/vendor/github.com/Shopify/sarama/config_resource_type.go deleted file mode 100644 index 848cc9c90c5..00000000000 --- a/vendor/github.com/Shopify/sarama/config_resource_type.go +++ /dev/null @@ -1,15 +0,0 @@ -package sarama - -type ConfigResourceType int8 - -// Taken from : -// https://cwiki.apache.org/confluence/display/KAFKA/KIP-133%3A+Describe+and+Alter+Configs+Admin+APIs#KIP-133:DescribeandAlterConfigsAdminAPIs-WireFormattypes - -const ( - UnknownResource ConfigResourceType = 0 - AnyResource ConfigResourceType = 1 - TopicResource ConfigResourceType = 2 - GroupResource ConfigResourceType = 3 - ClusterResource ConfigResourceType = 4 - BrokerResource ConfigResourceType = 5 -) diff --git a/vendor/github.com/Shopify/sarama/consumer.go b/vendor/github.com/Shopify/sarama/consumer.go deleted file mode 100644 index 33d9d143f91..00000000000 --- a/vendor/github.com/Shopify/sarama/consumer.go +++ /dev/null @@ -1,807 +0,0 @@ -package sarama - -import ( - "errors" - "fmt" - "sync" - "sync/atomic" - "time" -) - -// ConsumerMessage encapsulates a Kafka message returned by the consumer. -type ConsumerMessage struct { - Key, Value []byte - Topic string - Partition int32 - Offset int64 - Timestamp time.Time // only set if kafka is version 0.10+, inner message timestamp - BlockTimestamp time.Time // only set if kafka is version 0.10+, outer (compressed) block timestamp - Headers []*RecordHeader // only set if kafka is version 0.11+ -} - -// ConsumerError is what is provided to the user when an error occurs. -// It wraps an error and includes the topic and partition. -type ConsumerError struct { - Topic string - Partition int32 - Err error -} - -func (ce ConsumerError) Error() string { - return fmt.Sprintf("kafka: error while consuming %s/%d: %s", ce.Topic, ce.Partition, ce.Err) -} - -// ConsumerErrors is a type that wraps a batch of errors and implements the Error interface. -// It can be returned from the PartitionConsumer's Close methods to avoid the need to manually drain errors -// when stopping. -type ConsumerErrors []*ConsumerError - -func (ce ConsumerErrors) Error() string { - return fmt.Sprintf("kafka: %d errors while consuming", len(ce)) -} - -// Consumer manages PartitionConsumers which process Kafka messages from brokers. You MUST call Close() -// on a consumer to avoid leaks, it will not be garbage-collected automatically when it passes out of -// scope. -// -// Sarama's Consumer type does not currently support automatic consumer-group rebalancing and offset tracking. -// For Zookeeper-based tracking (Kafka 0.8.2 and earlier), the https://github.com/wvanbergen/kafka library -// builds on Sarama to add this support. For Kafka-based tracking (Kafka 0.9 and later), the -// https://github.com/bsm/sarama-cluster library builds on Sarama to add this support. -type Consumer interface { - - // Topics returns the set of available topics as retrieved from the cluster - // metadata. This method is the same as Client.Topics(), and is provided for - // convenience. - Topics() ([]string, error) - - // Partitions returns the sorted list of all partition IDs for the given topic. - // This method is the same as Client.Partitions(), and is provided for convenience. - Partitions(topic string) ([]int32, error) - - // ConsumePartition creates a PartitionConsumer on the given topic/partition with - // the given offset. It will return an error if this Consumer is already consuming - // on the given topic/partition. Offset can be a literal offset, or OffsetNewest - // or OffsetOldest - ConsumePartition(topic string, partition int32, offset int64) (PartitionConsumer, error) - - // HighWaterMarks returns the current high water marks for each topic and partition. - // Consistency between partitions is not guaranteed since high water marks are updated separately. - HighWaterMarks() map[string]map[int32]int64 - - // Close shuts down the consumer. It must be called after all child - // PartitionConsumers have already been closed. - Close() error -} - -type consumer struct { - client Client - conf *Config - ownClient bool - - lock sync.Mutex - children map[string]map[int32]*partitionConsumer - brokerConsumers map[*Broker]*brokerConsumer -} - -// NewConsumer creates a new consumer using the given broker addresses and configuration. -func NewConsumer(addrs []string, config *Config) (Consumer, error) { - client, err := NewClient(addrs, config) - if err != nil { - return nil, err - } - - c, err := NewConsumerFromClient(client) - if err != nil { - return nil, err - } - c.(*consumer).ownClient = true - return c, nil -} - -// NewConsumerFromClient creates a new consumer using the given client. It is still -// necessary to call Close() on the underlying client when shutting down this consumer. -func NewConsumerFromClient(client Client) (Consumer, error) { - // Check that we are not dealing with a closed Client before processing any other arguments - if client.Closed() { - return nil, ErrClosedClient - } - - c := &consumer{ - client: client, - conf: client.Config(), - children: make(map[string]map[int32]*partitionConsumer), - brokerConsumers: make(map[*Broker]*brokerConsumer), - } - - return c, nil -} - -func (c *consumer) Close() error { - if c.ownClient { - return c.client.Close() - } - return nil -} - -func (c *consumer) Topics() ([]string, error) { - return c.client.Topics() -} - -func (c *consumer) Partitions(topic string) ([]int32, error) { - return c.client.Partitions(topic) -} - -func (c *consumer) ConsumePartition(topic string, partition int32, offset int64) (PartitionConsumer, error) { - child := &partitionConsumer{ - consumer: c, - conf: c.conf, - topic: topic, - partition: partition, - messages: make(chan *ConsumerMessage, c.conf.ChannelBufferSize), - errors: make(chan *ConsumerError, c.conf.ChannelBufferSize), - feeder: make(chan *FetchResponse, 1), - trigger: make(chan none, 1), - dying: make(chan none), - fetchSize: c.conf.Consumer.Fetch.Default, - } - - if err := child.chooseStartingOffset(offset); err != nil { - return nil, err - } - - var leader *Broker - var err error - if leader, err = c.client.Leader(child.topic, child.partition); err != nil { - return nil, err - } - - if err := c.addChild(child); err != nil { - return nil, err - } - - go withRecover(child.dispatcher) - go withRecover(child.responseFeeder) - - child.broker = c.refBrokerConsumer(leader) - child.broker.input <- child - - return child, nil -} - -func (c *consumer) HighWaterMarks() map[string]map[int32]int64 { - c.lock.Lock() - defer c.lock.Unlock() - - hwms := make(map[string]map[int32]int64) - for topic, p := range c.children { - hwm := make(map[int32]int64, len(p)) - for partition, pc := range p { - hwm[partition] = pc.HighWaterMarkOffset() - } - hwms[topic] = hwm - } - - return hwms -} - -func (c *consumer) addChild(child *partitionConsumer) error { - c.lock.Lock() - defer c.lock.Unlock() - - topicChildren := c.children[child.topic] - if topicChildren == nil { - topicChildren = make(map[int32]*partitionConsumer) - c.children[child.topic] = topicChildren - } - - if topicChildren[child.partition] != nil { - return ConfigurationError("That topic/partition is already being consumed") - } - - topicChildren[child.partition] = child - return nil -} - -func (c *consumer) removeChild(child *partitionConsumer) { - c.lock.Lock() - defer c.lock.Unlock() - - delete(c.children[child.topic], child.partition) -} - -func (c *consumer) refBrokerConsumer(broker *Broker) *brokerConsumer { - c.lock.Lock() - defer c.lock.Unlock() - - bc := c.brokerConsumers[broker] - if bc == nil { - bc = c.newBrokerConsumer(broker) - c.brokerConsumers[broker] = bc - } - - bc.refs++ - - return bc -} - -func (c *consumer) unrefBrokerConsumer(brokerWorker *brokerConsumer) { - c.lock.Lock() - defer c.lock.Unlock() - - brokerWorker.refs-- - - if brokerWorker.refs == 0 { - close(brokerWorker.input) - if c.brokerConsumers[brokerWorker.broker] == brokerWorker { - delete(c.brokerConsumers, brokerWorker.broker) - } - } -} - -func (c *consumer) abandonBrokerConsumer(brokerWorker *brokerConsumer) { - c.lock.Lock() - defer c.lock.Unlock() - - delete(c.brokerConsumers, brokerWorker.broker) -} - -// PartitionConsumer - -// PartitionConsumer processes Kafka messages from a given topic and partition. You MUST call one of Close() or -// AsyncClose() on a PartitionConsumer to avoid leaks; it will not be garbage-collected automatically when it passes out -// of scope. -// -// The simplest way of using a PartitionConsumer is to loop over its Messages channel using a for/range -// loop. The PartitionConsumer will only stop itself in one case: when the offset being consumed is reported -// as out of range by the brokers. In this case you should decide what you want to do (try a different offset, -// notify a human, etc) and handle it appropriately. For all other error cases, it will just keep retrying. -// By default, it logs these errors to sarama.Logger; if you want to be notified directly of all errors, set -// your config's Consumer.Return.Errors to true and read from the Errors channel, using a select statement -// or a separate goroutine. Check out the Consumer examples to see implementations of these different approaches. -// -// To terminate such a for/range loop while the loop is executing, call AsyncClose. This will kick off the process of -// consumer tear-down & return imediately. Continue to loop, servicing the Messages channel until the teardown process -// AsyncClose initiated closes it (thus terminating the for/range loop). If you've already ceased reading Messages, call -// Close; this will signal the PartitionConsumer's goroutines to begin shutting down (just like AsyncClose), but will -// also drain the Messages channel, harvest all errors & return them once cleanup has completed. -type PartitionConsumer interface { - - // AsyncClose initiates a shutdown of the PartitionConsumer. This method will return immediately, after which you - // should continue to service the 'Messages' and 'Errors' channels until they are empty. It is required to call this - // function, or Close before a consumer object passes out of scope, as it will otherwise leak memory. You must call - // this before calling Close on the underlying client. - AsyncClose() - - // Close stops the PartitionConsumer from fetching messages. It will initiate a shutdown just like AsyncClose, drain - // the Messages channel, harvest any errors & return them to the caller. Note that if you are continuing to service - // the Messages channel when this function is called, you will be competing with Close for messages; consider - // calling AsyncClose, instead. It is required to call this function (or AsyncClose) before a consumer object passes - // out of scope, as it will otherwise leak memory. You must call this before calling Close on the underlying client. - Close() error - - // Messages returns the read channel for the messages that are returned by - // the broker. - Messages() <-chan *ConsumerMessage - - // Errors returns a read channel of errors that occurred during consuming, if - // enabled. By default, errors are logged and not returned over this channel. - // If you want to implement any custom error handling, set your config's - // Consumer.Return.Errors setting to true, and read from this channel. - Errors() <-chan *ConsumerError - - // HighWaterMarkOffset returns the high water mark offset of the partition, - // i.e. the offset that will be used for the next message that will be produced. - // You can use this to determine how far behind the processing is. - HighWaterMarkOffset() int64 -} - -type partitionConsumer struct { - highWaterMarkOffset int64 // must be at the top of the struct because https://golang.org/pkg/sync/atomic/#pkg-note-BUG - consumer *consumer - conf *Config - topic string - partition int32 - - broker *brokerConsumer - messages chan *ConsumerMessage - errors chan *ConsumerError - feeder chan *FetchResponse - - trigger, dying chan none - responseResult error - closeOnce sync.Once - - fetchSize int32 - offset int64 -} - -var errTimedOut = errors.New("timed out feeding messages to the user") // not user-facing - -func (child *partitionConsumer) sendError(err error) { - cErr := &ConsumerError{ - Topic: child.topic, - Partition: child.partition, - Err: err, - } - - if child.conf.Consumer.Return.Errors { - child.errors <- cErr - } else { - Logger.Println(cErr) - } -} - -func (child *partitionConsumer) dispatcher() { - for range child.trigger { - select { - case <-child.dying: - close(child.trigger) - case <-time.After(child.conf.Consumer.Retry.Backoff): - if child.broker != nil { - child.consumer.unrefBrokerConsumer(child.broker) - child.broker = nil - } - - Logger.Printf("consumer/%s/%d finding new broker\n", child.topic, child.partition) - if err := child.dispatch(); err != nil { - child.sendError(err) - child.trigger <- none{} - } - } - } - - if child.broker != nil { - child.consumer.unrefBrokerConsumer(child.broker) - } - child.consumer.removeChild(child) - close(child.feeder) -} - -func (child *partitionConsumer) dispatch() error { - if err := child.consumer.client.RefreshMetadata(child.topic); err != nil { - return err - } - - var leader *Broker - var err error - if leader, err = child.consumer.client.Leader(child.topic, child.partition); err != nil { - return err - } - - child.broker = child.consumer.refBrokerConsumer(leader) - - child.broker.input <- child - - return nil -} - -func (child *partitionConsumer) chooseStartingOffset(offset int64) error { - newestOffset, err := child.consumer.client.GetOffset(child.topic, child.partition, OffsetNewest) - if err != nil { - return err - } - oldestOffset, err := child.consumer.client.GetOffset(child.topic, child.partition, OffsetOldest) - if err != nil { - return err - } - - switch { - case offset == OffsetNewest: - child.offset = newestOffset - case offset == OffsetOldest: - child.offset = oldestOffset - case offset >= oldestOffset && offset <= newestOffset: - child.offset = offset - default: - return ErrOffsetOutOfRange - } - - return nil -} - -func (child *partitionConsumer) Messages() <-chan *ConsumerMessage { - return child.messages -} - -func (child *partitionConsumer) Errors() <-chan *ConsumerError { - return child.errors -} - -func (child *partitionConsumer) AsyncClose() { - // this triggers whatever broker owns this child to abandon it and close its trigger channel, which causes - // the dispatcher to exit its loop, which removes it from the consumer then closes its 'messages' and - // 'errors' channel (alternatively, if the child is already at the dispatcher for some reason, that will - // also just close itself) - child.closeOnce.Do(func() { - close(child.dying) - }) -} - -func (child *partitionConsumer) Close() error { - child.AsyncClose() - - go withRecover(func() { - for range child.messages { - // drain - } - }) - - var errors ConsumerErrors - for err := range child.errors { - errors = append(errors, err) - } - - if len(errors) > 0 { - return errors - } - return nil -} - -func (child *partitionConsumer) HighWaterMarkOffset() int64 { - return atomic.LoadInt64(&child.highWaterMarkOffset) -} - -func (child *partitionConsumer) responseFeeder() { - var msgs []*ConsumerMessage - expiryTicker := time.NewTicker(child.conf.Consumer.MaxProcessingTime) - firstAttempt := true - -feederLoop: - for response := range child.feeder { - msgs, child.responseResult = child.parseResponse(response) - - for i, msg := range msgs { - messageSelect: - select { - case child.messages <- msg: - firstAttempt = true - case <-expiryTicker.C: - if !firstAttempt { - child.responseResult = errTimedOut - child.broker.acks.Done() - for _, msg = range msgs[i:] { - child.messages <- msg - } - child.broker.input <- child - continue feederLoop - } else { - // current message has not been sent, return to select - // statement - firstAttempt = false - goto messageSelect - } - } - } - - child.broker.acks.Done() - } - - expiryTicker.Stop() - close(child.messages) - close(child.errors) -} - -func (child *partitionConsumer) parseMessages(msgSet *MessageSet) ([]*ConsumerMessage, error) { - var messages []*ConsumerMessage - for _, msgBlock := range msgSet.Messages { - for _, msg := range msgBlock.Messages() { - offset := msg.Offset - if msg.Msg.Version >= 1 { - baseOffset := msgBlock.Offset - msgBlock.Messages()[len(msgBlock.Messages())-1].Offset - offset += baseOffset - } - if offset < child.offset { - continue - } - messages = append(messages, &ConsumerMessage{ - Topic: child.topic, - Partition: child.partition, - Key: msg.Msg.Key, - Value: msg.Msg.Value, - Offset: offset, - Timestamp: msg.Msg.Timestamp, - BlockTimestamp: msgBlock.Msg.Timestamp, - }) - child.offset = offset + 1 - } - } - if len(messages) == 0 { - return nil, ErrIncompleteResponse - } - return messages, nil -} - -func (child *partitionConsumer) parseRecords(batch *RecordBatch) ([]*ConsumerMessage, error) { - var messages []*ConsumerMessage - for _, rec := range batch.Records { - offset := batch.FirstOffset + rec.OffsetDelta - if offset < child.offset { - continue - } - messages = append(messages, &ConsumerMessage{ - Topic: child.topic, - Partition: child.partition, - Key: rec.Key, - Value: rec.Value, - Offset: offset, - Timestamp: batch.FirstTimestamp.Add(rec.TimestampDelta), - Headers: rec.Headers, - }) - child.offset = offset + 1 - } - if len(messages) == 0 { - child.offset += 1 - } - return messages, nil -} - -func (child *partitionConsumer) parseResponse(response *FetchResponse) ([]*ConsumerMessage, error) { - block := response.GetBlock(child.topic, child.partition) - if block == nil { - return nil, ErrIncompleteResponse - } - - if block.Err != ErrNoError { - return nil, block.Err - } - - nRecs, err := block.numRecords() - if err != nil { - return nil, err - } - if nRecs == 0 { - partialTrailingMessage, err := block.isPartial() - if err != nil { - return nil, err - } - // We got no messages. If we got a trailing one then we need to ask for more data. - // Otherwise we just poll again and wait for one to be produced... - if partialTrailingMessage { - if child.conf.Consumer.Fetch.Max > 0 && child.fetchSize == child.conf.Consumer.Fetch.Max { - // we can't ask for more data, we've hit the configured limit - child.sendError(ErrMessageTooLarge) - child.offset++ // skip this one so we can keep processing future messages - } else { - child.fetchSize *= 2 - if child.conf.Consumer.Fetch.Max > 0 && child.fetchSize > child.conf.Consumer.Fetch.Max { - child.fetchSize = child.conf.Consumer.Fetch.Max - } - } - } - - return nil, nil - } - - // we got messages, reset our fetch size in case it was increased for a previous request - child.fetchSize = child.conf.Consumer.Fetch.Default - atomic.StoreInt64(&child.highWaterMarkOffset, block.HighWaterMarkOffset) - - messages := []*ConsumerMessage{} - for _, records := range block.RecordsSet { - switch records.recordsType { - case legacyRecords: - messageSetMessages, err := child.parseMessages(records.MsgSet) - if err != nil { - return nil, err - } - - messages = append(messages, messageSetMessages...) - case defaultRecords: - recordBatchMessages, err := child.parseRecords(records.RecordBatch) - if err != nil { - return nil, err - } - if control, err := records.isControl(); err != nil || control { - continue - } - - messages = append(messages, recordBatchMessages...) - default: - return nil, fmt.Errorf("unknown records type: %v", records.recordsType) - } - } - - return messages, nil -} - -// brokerConsumer - -type brokerConsumer struct { - consumer *consumer - broker *Broker - input chan *partitionConsumer - newSubscriptions chan []*partitionConsumer - wait chan none - subscriptions map[*partitionConsumer]none - acks sync.WaitGroup - refs int -} - -func (c *consumer) newBrokerConsumer(broker *Broker) *brokerConsumer { - bc := &brokerConsumer{ - consumer: c, - broker: broker, - input: make(chan *partitionConsumer), - newSubscriptions: make(chan []*partitionConsumer), - wait: make(chan none), - subscriptions: make(map[*partitionConsumer]none), - refs: 0, - } - - go withRecover(bc.subscriptionManager) - go withRecover(bc.subscriptionConsumer) - - return bc -} - -func (bc *brokerConsumer) subscriptionManager() { - var buffer []*partitionConsumer - - // The subscriptionManager constantly accepts new subscriptions on `input` (even when the main subscriptionConsumer - // goroutine is in the middle of a network request) and batches it up. The main worker goroutine picks - // up a batch of new subscriptions between every network request by reading from `newSubscriptions`, so we give - // it nil if no new subscriptions are available. We also write to `wait` only when new subscriptions is available, - // so the main goroutine can block waiting for work if it has none. - for { - if len(buffer) > 0 { - select { - case event, ok := <-bc.input: - if !ok { - goto done - } - buffer = append(buffer, event) - case bc.newSubscriptions <- buffer: - buffer = nil - case bc.wait <- none{}: - } - } else { - select { - case event, ok := <-bc.input: - if !ok { - goto done - } - buffer = append(buffer, event) - case bc.newSubscriptions <- nil: - } - } - } - -done: - close(bc.wait) - if len(buffer) > 0 { - bc.newSubscriptions <- buffer - } - close(bc.newSubscriptions) -} - -func (bc *brokerConsumer) subscriptionConsumer() { - <-bc.wait // wait for our first piece of work - - // the subscriptionConsumer ensures we will get nil right away if no new subscriptions is available - for newSubscriptions := range bc.newSubscriptions { - bc.updateSubscriptions(newSubscriptions) - - if len(bc.subscriptions) == 0 { - // We're about to be shut down or we're about to receive more subscriptions. - // Either way, the signal just hasn't propagated to our goroutine yet. - <-bc.wait - continue - } - - response, err := bc.fetchNewMessages() - - if err != nil { - Logger.Printf("consumer/broker/%d disconnecting due to error processing FetchRequest: %s\n", bc.broker.ID(), err) - bc.abort(err) - return - } - - bc.acks.Add(len(bc.subscriptions)) - for child := range bc.subscriptions { - child.feeder <- response - } - bc.acks.Wait() - bc.handleResponses() - } -} - -func (bc *brokerConsumer) updateSubscriptions(newSubscriptions []*partitionConsumer) { - for _, child := range newSubscriptions { - bc.subscriptions[child] = none{} - Logger.Printf("consumer/broker/%d added subscription to %s/%d\n", bc.broker.ID(), child.topic, child.partition) - } - - for child := range bc.subscriptions { - select { - case <-child.dying: - Logger.Printf("consumer/broker/%d closed dead subscription to %s/%d\n", bc.broker.ID(), child.topic, child.partition) - close(child.trigger) - delete(bc.subscriptions, child) - default: - break - } - } -} - -func (bc *brokerConsumer) handleResponses() { - // handles the response codes left for us by our subscriptions, and abandons ones that have been closed - for child := range bc.subscriptions { - result := child.responseResult - child.responseResult = nil - - switch result { - case nil: - break - case errTimedOut: - Logger.Printf("consumer/broker/%d abandoned subscription to %s/%d because consuming was taking too long\n", - bc.broker.ID(), child.topic, child.partition) - delete(bc.subscriptions, child) - case ErrOffsetOutOfRange: - // there's no point in retrying this it will just fail the same way again - // shut it down and force the user to choose what to do - child.sendError(result) - Logger.Printf("consumer/%s/%d shutting down because %s\n", child.topic, child.partition, result) - close(child.trigger) - delete(bc.subscriptions, child) - case ErrUnknownTopicOrPartition, ErrNotLeaderForPartition, ErrLeaderNotAvailable, ErrReplicaNotAvailable: - // not an error, but does need redispatching - Logger.Printf("consumer/broker/%d abandoned subscription to %s/%d because %s\n", - bc.broker.ID(), child.topic, child.partition, result) - child.trigger <- none{} - delete(bc.subscriptions, child) - default: - // dunno, tell the user and try redispatching - child.sendError(result) - Logger.Printf("consumer/broker/%d abandoned subscription to %s/%d because %s\n", - bc.broker.ID(), child.topic, child.partition, result) - child.trigger <- none{} - delete(bc.subscriptions, child) - } - } -} - -func (bc *brokerConsumer) abort(err error) { - bc.consumer.abandonBrokerConsumer(bc) - _ = bc.broker.Close() // we don't care about the error this might return, we already have one - - for child := range bc.subscriptions { - child.sendError(err) - child.trigger <- none{} - } - - for newSubscriptions := range bc.newSubscriptions { - if len(newSubscriptions) == 0 { - <-bc.wait - continue - } - for _, child := range newSubscriptions { - child.sendError(err) - child.trigger <- none{} - } - } -} - -func (bc *brokerConsumer) fetchNewMessages() (*FetchResponse, error) { - request := &FetchRequest{ - MinBytes: bc.consumer.conf.Consumer.Fetch.Min, - MaxWaitTime: int32(bc.consumer.conf.Consumer.MaxWaitTime / time.Millisecond), - } - if bc.consumer.conf.Version.IsAtLeast(V0_10_0_0) { - request.Version = 2 - } - if bc.consumer.conf.Version.IsAtLeast(V0_10_1_0) { - request.Version = 3 - request.MaxBytes = MaxResponseSize - } - if bc.consumer.conf.Version.IsAtLeast(V0_11_0_0) { - request.Version = 4 - request.Isolation = ReadUncommitted // We don't support yet transactions. - } - - for child := range bc.subscriptions { - request.AddBlock(child.topic, child.partition, child.offset, child.fetchSize) - } - - return bc.broker.Fetch(request) -} diff --git a/vendor/github.com/Shopify/sarama/consumer_group_members.go b/vendor/github.com/Shopify/sarama/consumer_group_members.go deleted file mode 100644 index 9d92d350a5d..00000000000 --- a/vendor/github.com/Shopify/sarama/consumer_group_members.go +++ /dev/null @@ -1,94 +0,0 @@ -package sarama - -type ConsumerGroupMemberMetadata struct { - Version int16 - Topics []string - UserData []byte -} - -func (m *ConsumerGroupMemberMetadata) encode(pe packetEncoder) error { - pe.putInt16(m.Version) - - if err := pe.putStringArray(m.Topics); err != nil { - return err - } - - if err := pe.putBytes(m.UserData); err != nil { - return err - } - - return nil -} - -func (m *ConsumerGroupMemberMetadata) decode(pd packetDecoder) (err error) { - if m.Version, err = pd.getInt16(); err != nil { - return - } - - if m.Topics, err = pd.getStringArray(); err != nil { - return - } - - if m.UserData, err = pd.getBytes(); err != nil { - return - } - - return nil -} - -type ConsumerGroupMemberAssignment struct { - Version int16 - Topics map[string][]int32 - UserData []byte -} - -func (m *ConsumerGroupMemberAssignment) encode(pe packetEncoder) error { - pe.putInt16(m.Version) - - if err := pe.putArrayLength(len(m.Topics)); err != nil { - return err - } - - for topic, partitions := range m.Topics { - if err := pe.putString(topic); err != nil { - return err - } - if err := pe.putInt32Array(partitions); err != nil { - return err - } - } - - if err := pe.putBytes(m.UserData); err != nil { - return err - } - - return nil -} - -func (m *ConsumerGroupMemberAssignment) decode(pd packetDecoder) (err error) { - if m.Version, err = pd.getInt16(); err != nil { - return - } - - var topicLen int - if topicLen, err = pd.getArrayLength(); err != nil { - return - } - - m.Topics = make(map[string][]int32, topicLen) - for i := 0; i < topicLen; i++ { - var topic string - if topic, err = pd.getString(); err != nil { - return - } - if m.Topics[topic], err = pd.getInt32Array(); err != nil { - return - } - } - - if m.UserData, err = pd.getBytes(); err != nil { - return - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/consumer_metadata_request.go b/vendor/github.com/Shopify/sarama/consumer_metadata_request.go deleted file mode 100644 index 4de45e7bf50..00000000000 --- a/vendor/github.com/Shopify/sarama/consumer_metadata_request.go +++ /dev/null @@ -1,33 +0,0 @@ -package sarama - -type ConsumerMetadataRequest struct { - ConsumerGroup string -} - -func (r *ConsumerMetadataRequest) encode(pe packetEncoder) error { - tmp := new(FindCoordinatorRequest) - tmp.CoordinatorKey = r.ConsumerGroup - tmp.CoordinatorType = CoordinatorGroup - return tmp.encode(pe) -} - -func (r *ConsumerMetadataRequest) decode(pd packetDecoder, version int16) (err error) { - tmp := new(FindCoordinatorRequest) - if err := tmp.decode(pd, version); err != nil { - return err - } - r.ConsumerGroup = tmp.CoordinatorKey - return nil -} - -func (r *ConsumerMetadataRequest) key() int16 { - return 10 -} - -func (r *ConsumerMetadataRequest) version() int16 { - return 0 -} - -func (r *ConsumerMetadataRequest) requiredVersion() KafkaVersion { - return V0_8_2_0 -} diff --git a/vendor/github.com/Shopify/sarama/consumer_metadata_response.go b/vendor/github.com/Shopify/sarama/consumer_metadata_response.go deleted file mode 100644 index 442cbde7ac0..00000000000 --- a/vendor/github.com/Shopify/sarama/consumer_metadata_response.go +++ /dev/null @@ -1,77 +0,0 @@ -package sarama - -import ( - "net" - "strconv" -) - -type ConsumerMetadataResponse struct { - Err KError - Coordinator *Broker - CoordinatorID int32 // deprecated: use Coordinator.ID() - CoordinatorHost string // deprecated: use Coordinator.Addr() - CoordinatorPort int32 // deprecated: use Coordinator.Addr() -} - -func (r *ConsumerMetadataResponse) decode(pd packetDecoder, version int16) (err error) { - tmp := new(FindCoordinatorResponse) - - if err := tmp.decode(pd, version); err != nil { - return err - } - - r.Err = tmp.Err - - r.Coordinator = tmp.Coordinator - if tmp.Coordinator == nil { - return nil - } - - // this can all go away in 2.0, but we have to fill in deprecated fields to maintain - // backwards compatibility - host, portstr, err := net.SplitHostPort(r.Coordinator.Addr()) - if err != nil { - return err - } - port, err := strconv.ParseInt(portstr, 10, 32) - if err != nil { - return err - } - r.CoordinatorID = r.Coordinator.ID() - r.CoordinatorHost = host - r.CoordinatorPort = int32(port) - - return nil -} - -func (r *ConsumerMetadataResponse) encode(pe packetEncoder) error { - if r.Coordinator == nil { - r.Coordinator = new(Broker) - r.Coordinator.id = r.CoordinatorID - r.Coordinator.addr = net.JoinHostPort(r.CoordinatorHost, strconv.Itoa(int(r.CoordinatorPort))) - } - - tmp := &FindCoordinatorResponse{ - Version: 0, - Err: r.Err, - Coordinator: r.Coordinator, - } - - if err := tmp.encode(pe); err != nil { - return err - } - - return nil -} - -func (r *ConsumerMetadataResponse) key() int16 { - return 10 -} - -func (r *ConsumerMetadataResponse) version() int16 { - return 0 -} - -func (r *ConsumerMetadataResponse) requiredVersion() KafkaVersion { - return V0_8_2_0 -} diff --git a/vendor/github.com/Shopify/sarama/crc32_field.go b/vendor/github.com/Shopify/sarama/crc32_field.go deleted file mode 100644 index 1f144431a8b..00000000000 --- a/vendor/github.com/Shopify/sarama/crc32_field.go +++ /dev/null @@ -1,69 +0,0 @@ -package sarama - -import ( - "encoding/binary" - "fmt" - "hash/crc32" -) - -type crcPolynomial int8 - -const ( - crcIEEE crcPolynomial = iota - crcCastagnoli -) - -var castagnoliTable = crc32.MakeTable(crc32.Castagnoli) - -// crc32Field implements the pushEncoder and pushDecoder interfaces for calculating CRC32s. -type crc32Field struct { - startOffset int - polynomial crcPolynomial -} - -func (c *crc32Field) saveOffset(in int) { - c.startOffset = in -} - -func (c *crc32Field) reserveLength() int { - return 4 -} - -func newCRC32Field(polynomial crcPolynomial) *crc32Field { - return &crc32Field{polynomial: polynomial} -} - -func (c *crc32Field) run(curOffset int, buf []byte) error { - crc, err := c.crc(curOffset, buf) - if err != nil { - return err - } - binary.BigEndian.PutUint32(buf[c.startOffset:], crc) - return nil -} - -func (c *crc32Field) check(curOffset int, buf []byte) error { - crc, err := c.crc(curOffset, buf) - if err != nil { - return err - } - - expected := binary.BigEndian.Uint32(buf[c.startOffset:]) - if crc != expected { - return PacketDecodingError{fmt.Sprintf("CRC didn't match expected %#x got %#x", expected, crc)} - } - - return nil -} -func (c *crc32Field) crc(curOffset int, buf []byte) (uint32, error) { - var tab *crc32.Table - switch c.polynomial { - case crcIEEE: - tab = crc32.IEEETable - case crcCastagnoli: - tab = castagnoliTable - default: - return 0, PacketDecodingError{"invalid CRC type"} - } - return crc32.Checksum(buf[c.startOffset+4:curOffset], tab), nil -} diff --git a/vendor/github.com/Shopify/sarama/create_partitions_request.go b/vendor/github.com/Shopify/sarama/create_partitions_request.go deleted file mode 100644 index af321e99466..00000000000 --- a/vendor/github.com/Shopify/sarama/create_partitions_request.go +++ /dev/null @@ -1,121 +0,0 @@ -package sarama - -import "time" - -type CreatePartitionsRequest struct { - TopicPartitions map[string]*TopicPartition - Timeout time.Duration - ValidateOnly bool -} - -func (c *CreatePartitionsRequest) encode(pe packetEncoder) error { - if err := pe.putArrayLength(len(c.TopicPartitions)); err != nil { - return err - } - - for topic, partition := range c.TopicPartitions { - if err := pe.putString(topic); err != nil { - return err - } - if err := partition.encode(pe); err != nil { - return err - } - } - - pe.putInt32(int32(c.Timeout / time.Millisecond)) - - pe.putBool(c.ValidateOnly) - - return nil -} - -func (c *CreatePartitionsRequest) decode(pd packetDecoder, version int16) (err error) { - n, err := pd.getArrayLength() - if err != nil { - return err - } - c.TopicPartitions = make(map[string]*TopicPartition, n) - for i := 0; i < n; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - c.TopicPartitions[topic] = new(TopicPartition) - if err := c.TopicPartitions[topic].decode(pd, version); err != nil { - return err - } - } - - timeout, err := pd.getInt32() - if err != nil { - return err - } - c.Timeout = time.Duration(timeout) * time.Millisecond - - if c.ValidateOnly, err = pd.getBool(); err != nil { - return err - } - - return nil -} - -func (r *CreatePartitionsRequest) key() int16 { - return 37 -} - -func (r *CreatePartitionsRequest) version() int16 { - return 0 -} - -func (r *CreatePartitionsRequest) requiredVersion() KafkaVersion { - return V1_0_0_0 -} - -type TopicPartition struct { - Count int32 - Assignment [][]int32 -} - -func (t *TopicPartition) encode(pe packetEncoder) error { - pe.putInt32(t.Count) - - if len(t.Assignment) == 0 { - pe.putInt32(-1) - return nil - } - - if err := pe.putArrayLength(len(t.Assignment)); err != nil { - return err - } - - for _, assign := range t.Assignment { - if err := pe.putInt32Array(assign); err != nil { - return err - } - } - - return nil -} - -func (t *TopicPartition) decode(pd packetDecoder, version int16) (err error) { - if t.Count, err = pd.getInt32(); err != nil { - return err - } - - n, err := pd.getInt32() - if err != nil { - return err - } - if n <= 0 { - return nil - } - t.Assignment = make([][]int32, n) - - for i := 0; i < int(n); i++ { - if t.Assignment[i], err = pd.getInt32Array(); err != nil { - return err - } - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/create_partitions_response.go b/vendor/github.com/Shopify/sarama/create_partitions_response.go deleted file mode 100644 index abd621c64ec..00000000000 --- a/vendor/github.com/Shopify/sarama/create_partitions_response.go +++ /dev/null @@ -1,94 +0,0 @@ -package sarama - -import "time" - -type CreatePartitionsResponse struct { - ThrottleTime time.Duration - TopicPartitionErrors map[string]*TopicPartitionError -} - -func (c *CreatePartitionsResponse) encode(pe packetEncoder) error { - pe.putInt32(int32(c.ThrottleTime / time.Millisecond)) - if err := pe.putArrayLength(len(c.TopicPartitionErrors)); err != nil { - return err - } - - for topic, partitionError := range c.TopicPartitionErrors { - if err := pe.putString(topic); err != nil { - return err - } - if err := partitionError.encode(pe); err != nil { - return err - } - } - - return nil -} - -func (c *CreatePartitionsResponse) decode(pd packetDecoder, version int16) (err error) { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - c.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - c.TopicPartitionErrors = make(map[string]*TopicPartitionError, n) - for i := 0; i < n; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - c.TopicPartitionErrors[topic] = new(TopicPartitionError) - if err := c.TopicPartitionErrors[topic].decode(pd, version); err != nil { - return err - } - } - - return nil -} - -func (r *CreatePartitionsResponse) key() int16 { - return 37 -} - -func (r *CreatePartitionsResponse) version() int16 { - return 0 -} - -func (r *CreatePartitionsResponse) requiredVersion() KafkaVersion { - return V1_0_0_0 -} - -type TopicPartitionError struct { - Err KError - ErrMsg *string -} - -func (t *TopicPartitionError) encode(pe packetEncoder) error { - pe.putInt16(int16(t.Err)) - - if err := pe.putNullableString(t.ErrMsg); err != nil { - return err - } - - return nil -} - -func (t *TopicPartitionError) decode(pd packetDecoder, version int16) (err error) { - kerr, err := pd.getInt16() - if err != nil { - return err - } - t.Err = KError(kerr) - - if t.ErrMsg, err = pd.getNullableString(); err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/create_topics_request.go b/vendor/github.com/Shopify/sarama/create_topics_request.go deleted file mode 100644 index 709c0a44e71..00000000000 --- a/vendor/github.com/Shopify/sarama/create_topics_request.go +++ /dev/null @@ -1,174 +0,0 @@ -package sarama - -import ( - "time" -) - -type CreateTopicsRequest struct { - Version int16 - - TopicDetails map[string]*TopicDetail - Timeout time.Duration - ValidateOnly bool -} - -func (c *CreateTopicsRequest) encode(pe packetEncoder) error { - if err := pe.putArrayLength(len(c.TopicDetails)); err != nil { - return err - } - for topic, detail := range c.TopicDetails { - if err := pe.putString(topic); err != nil { - return err - } - if err := detail.encode(pe); err != nil { - return err - } - } - - pe.putInt32(int32(c.Timeout / time.Millisecond)) - - if c.Version >= 1 { - pe.putBool(c.ValidateOnly) - } - - return nil -} - -func (c *CreateTopicsRequest) decode(pd packetDecoder, version int16) (err error) { - n, err := pd.getArrayLength() - if err != nil { - return err - } - - c.TopicDetails = make(map[string]*TopicDetail, n) - - for i := 0; i < n; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - c.TopicDetails[topic] = new(TopicDetail) - if err = c.TopicDetails[topic].decode(pd, version); err != nil { - return err - } - } - - timeout, err := pd.getInt32() - if err != nil { - return err - } - c.Timeout = time.Duration(timeout) * time.Millisecond - - if version >= 1 { - c.ValidateOnly, err = pd.getBool() - if err != nil { - return err - } - - c.Version = version - } - - return nil -} - -func (c *CreateTopicsRequest) key() int16 { - return 19 -} - -func (c *CreateTopicsRequest) version() int16 { - return c.Version -} - -func (c *CreateTopicsRequest) requiredVersion() KafkaVersion { - switch c.Version { - case 2: - return V1_0_0_0 - case 1: - return V0_11_0_0 - default: - return V0_10_1_0 - } -} - -type TopicDetail struct { - NumPartitions int32 - ReplicationFactor int16 - ReplicaAssignment map[int32][]int32 - ConfigEntries map[string]*string -} - -func (t *TopicDetail) encode(pe packetEncoder) error { - pe.putInt32(t.NumPartitions) - pe.putInt16(t.ReplicationFactor) - - if err := pe.putArrayLength(len(t.ReplicaAssignment)); err != nil { - return err - } - for partition, assignment := range t.ReplicaAssignment { - pe.putInt32(partition) - if err := pe.putInt32Array(assignment); err != nil { - return err - } - } - - if err := pe.putArrayLength(len(t.ConfigEntries)); err != nil { - return err - } - for configKey, configValue := range t.ConfigEntries { - if err := pe.putString(configKey); err != nil { - return err - } - if err := pe.putNullableString(configValue); err != nil { - return err - } - } - - return nil -} - -func (t *TopicDetail) decode(pd packetDecoder, version int16) (err error) { - if t.NumPartitions, err = pd.getInt32(); err != nil { - return err - } - if t.ReplicationFactor, err = pd.getInt16(); err != nil { - return err - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - if n > 0 { - t.ReplicaAssignment = make(map[int32][]int32, n) - for i := 0; i < n; i++ { - replica, err := pd.getInt32() - if err != nil { - return err - } - if t.ReplicaAssignment[replica], err = pd.getInt32Array(); err != nil { - return err - } - } - } - - n, err = pd.getArrayLength() - if err != nil { - return err - } - - if n > 0 { - t.ConfigEntries = make(map[string]*string, n) - for i := 0; i < n; i++ { - configKey, err := pd.getString() - if err != nil { - return err - } - if t.ConfigEntries[configKey], err = pd.getNullableString(); err != nil { - return err - } - } - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/create_topics_response.go b/vendor/github.com/Shopify/sarama/create_topics_response.go deleted file mode 100644 index 66207e00c5d..00000000000 --- a/vendor/github.com/Shopify/sarama/create_topics_response.go +++ /dev/null @@ -1,112 +0,0 @@ -package sarama - -import "time" - -type CreateTopicsResponse struct { - Version int16 - ThrottleTime time.Duration - TopicErrors map[string]*TopicError -} - -func (c *CreateTopicsResponse) encode(pe packetEncoder) error { - if c.Version >= 2 { - pe.putInt32(int32(c.ThrottleTime / time.Millisecond)) - } - - if err := pe.putArrayLength(len(c.TopicErrors)); err != nil { - return err - } - for topic, topicError := range c.TopicErrors { - if err := pe.putString(topic); err != nil { - return err - } - if err := topicError.encode(pe, c.Version); err != nil { - return err - } - } - - return nil -} - -func (c *CreateTopicsResponse) decode(pd packetDecoder, version int16) (err error) { - c.Version = version - - if version >= 2 { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - c.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - c.TopicErrors = make(map[string]*TopicError, n) - for i := 0; i < n; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - c.TopicErrors[topic] = new(TopicError) - if err := c.TopicErrors[topic].decode(pd, version); err != nil { - return err - } - } - - return nil -} - -func (c *CreateTopicsResponse) key() int16 { - return 19 -} - -func (c *CreateTopicsResponse) version() int16 { - return c.Version -} - -func (c *CreateTopicsResponse) requiredVersion() KafkaVersion { - switch c.Version { - case 2: - return V1_0_0_0 - case 1: - return V0_11_0_0 - default: - return V0_10_1_0 - } -} - -type TopicError struct { - Err KError - ErrMsg *string -} - -func (t *TopicError) encode(pe packetEncoder, version int16) error { - pe.putInt16(int16(t.Err)) - - if version >= 1 { - if err := pe.putNullableString(t.ErrMsg); err != nil { - return err - } - } - - return nil -} - -func (t *TopicError) decode(pd packetDecoder, version int16) (err error) { - kErr, err := pd.getInt16() - if err != nil { - return err - } - t.Err = KError(kErr) - - if version >= 1 { - if t.ErrMsg, err = pd.getNullableString(); err != nil { - return err - } - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/delete_groups_request.go b/vendor/github.com/Shopify/sarama/delete_groups_request.go deleted file mode 100644 index 305a324ac2d..00000000000 --- a/vendor/github.com/Shopify/sarama/delete_groups_request.go +++ /dev/null @@ -1,30 +0,0 @@ -package sarama - -type DeleteGroupsRequest struct { - Groups []string -} - -func (r *DeleteGroupsRequest) encode(pe packetEncoder) error { - return pe.putStringArray(r.Groups) -} - -func (r *DeleteGroupsRequest) decode(pd packetDecoder, version int16) (err error) { - r.Groups, err = pd.getStringArray() - return -} - -func (r *DeleteGroupsRequest) key() int16 { - return 42 -} - -func (r *DeleteGroupsRequest) version() int16 { - return 0 -} - -func (r *DeleteGroupsRequest) requiredVersion() KafkaVersion { - return V1_1_0_0 -} - -func (r *DeleteGroupsRequest) AddGroup(group string) { - r.Groups = append(r.Groups, group) -} diff --git a/vendor/github.com/Shopify/sarama/delete_groups_response.go b/vendor/github.com/Shopify/sarama/delete_groups_response.go deleted file mode 100644 index c067ebb42b0..00000000000 --- a/vendor/github.com/Shopify/sarama/delete_groups_response.go +++ /dev/null @@ -1,70 +0,0 @@ -package sarama - -import ( - "time" -) - -type DeleteGroupsResponse struct { - ThrottleTime time.Duration - GroupErrorCodes map[string]KError -} - -func (r *DeleteGroupsResponse) encode(pe packetEncoder) error { - pe.putInt32(int32(r.ThrottleTime / time.Millisecond)) - - if err := pe.putArrayLength(len(r.GroupErrorCodes)); err != nil { - return err - } - for groupID, errorCode := range r.GroupErrorCodes { - if err := pe.putString(groupID); err != nil { - return err - } - pe.putInt16(int16(errorCode)) - } - - return nil -} - -func (r *DeleteGroupsResponse) decode(pd packetDecoder, version int16) error { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - r.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - n, err := pd.getArrayLength() - if err != nil { - return err - } - if n == 0 { - return nil - } - - r.GroupErrorCodes = make(map[string]KError, n) - for i := 0; i < n; i++ { - groupID, err := pd.getString() - if err != nil { - return err - } - errorCode, err := pd.getInt16() - if err != nil { - return err - } - - r.GroupErrorCodes[groupID] = KError(errorCode) - } - - return nil -} - -func (r *DeleteGroupsResponse) key() int16 { - return 42 -} - -func (r *DeleteGroupsResponse) version() int16 { - return 0 -} - -func (r *DeleteGroupsResponse) requiredVersion() KafkaVersion { - return V1_1_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/delete_records_request.go b/vendor/github.com/Shopify/sarama/delete_records_request.go deleted file mode 100644 index 93efafd4d0b..00000000000 --- a/vendor/github.com/Shopify/sarama/delete_records_request.go +++ /dev/null @@ -1,126 +0,0 @@ -package sarama - -import ( - "sort" - "time" -) - -// request message format is: -// [topic] timeout(int32) -// where topic is: -// name(string) [partition] -// where partition is: -// id(int32) offset(int64) - -type DeleteRecordsRequest struct { - Topics map[string]*DeleteRecordsRequestTopic - Timeout time.Duration -} - -func (d *DeleteRecordsRequest) encode(pe packetEncoder) error { - if err := pe.putArrayLength(len(d.Topics)); err != nil { - return err - } - keys := make([]string, 0, len(d.Topics)) - for topic := range d.Topics { - keys = append(keys, topic) - } - sort.Strings(keys) - for _, topic := range keys { - if err := pe.putString(topic); err != nil { - return err - } - if err := d.Topics[topic].encode(pe); err != nil { - return err - } - } - pe.putInt32(int32(d.Timeout / time.Millisecond)) - - return nil -} - -func (d *DeleteRecordsRequest) decode(pd packetDecoder, version int16) error { - n, err := pd.getArrayLength() - if err != nil { - return err - } - - if n > 0 { - d.Topics = make(map[string]*DeleteRecordsRequestTopic, n) - for i := 0; i < n; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - details := new(DeleteRecordsRequestTopic) - if err = details.decode(pd, version); err != nil { - return err - } - d.Topics[topic] = details - } - } - - timeout, err := pd.getInt32() - if err != nil { - return err - } - d.Timeout = time.Duration(timeout) * time.Millisecond - - return nil -} - -func (d *DeleteRecordsRequest) key() int16 { - return 21 -} - -func (d *DeleteRecordsRequest) version() int16 { - return 0 -} - -func (d *DeleteRecordsRequest) requiredVersion() KafkaVersion { - return V0_11_0_0 -} - -type DeleteRecordsRequestTopic struct { - PartitionOffsets map[int32]int64 // partition => offset -} - -func (t *DeleteRecordsRequestTopic) encode(pe packetEncoder) error { - if err := pe.putArrayLength(len(t.PartitionOffsets)); err != nil { - return err - } - keys := make([]int32, 0, len(t.PartitionOffsets)) - for partition := range t.PartitionOffsets { - keys = append(keys, partition) - } - sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) - for _, partition := range keys { - pe.putInt32(partition) - pe.putInt64(t.PartitionOffsets[partition]) - } - return nil -} - -func (t *DeleteRecordsRequestTopic) decode(pd packetDecoder, version int16) error { - n, err := pd.getArrayLength() - if err != nil { - return err - } - - if n > 0 { - t.PartitionOffsets = make(map[int32]int64, n) - for i := 0; i < n; i++ { - partition, err := pd.getInt32() - if err != nil { - return err - } - offset, err := pd.getInt64() - if err != nil { - return err - } - t.PartitionOffsets[partition] = offset - } - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/delete_records_response.go b/vendor/github.com/Shopify/sarama/delete_records_response.go deleted file mode 100644 index 733a58b6bc3..00000000000 --- a/vendor/github.com/Shopify/sarama/delete_records_response.go +++ /dev/null @@ -1,158 +0,0 @@ -package sarama - -import ( - "sort" - "time" -) - -// response message format is: -// throttleMs(int32) [topic] -// where topic is: -// name(string) [partition] -// where partition is: -// id(int32) low_watermark(int64) error_code(int16) - -type DeleteRecordsResponse struct { - Version int16 - ThrottleTime time.Duration - Topics map[string]*DeleteRecordsResponseTopic -} - -func (d *DeleteRecordsResponse) encode(pe packetEncoder) error { - pe.putInt32(int32(d.ThrottleTime / time.Millisecond)) - - if err := pe.putArrayLength(len(d.Topics)); err != nil { - return err - } - keys := make([]string, 0, len(d.Topics)) - for topic := range d.Topics { - keys = append(keys, topic) - } - sort.Strings(keys) - for _, topic := range keys { - if err := pe.putString(topic); err != nil { - return err - } - if err := d.Topics[topic].encode(pe); err != nil { - return err - } - } - return nil -} - -func (d *DeleteRecordsResponse) decode(pd packetDecoder, version int16) error { - d.Version = version - - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - d.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - if n > 0 { - d.Topics = make(map[string]*DeleteRecordsResponseTopic, n) - for i := 0; i < n; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - details := new(DeleteRecordsResponseTopic) - if err = details.decode(pd, version); err != nil { - return err - } - d.Topics[topic] = details - } - } - - return nil -} - -func (d *DeleteRecordsResponse) key() int16 { - return 21 -} - -func (d *DeleteRecordsResponse) version() int16 { - return 0 -} - -func (d *DeleteRecordsResponse) requiredVersion() KafkaVersion { - return V0_11_0_0 -} - -type DeleteRecordsResponseTopic struct { - Partitions map[int32]*DeleteRecordsResponsePartition -} - -func (t *DeleteRecordsResponseTopic) encode(pe packetEncoder) error { - if err := pe.putArrayLength(len(t.Partitions)); err != nil { - return err - } - keys := make([]int32, 0, len(t.Partitions)) - for partition := range t.Partitions { - keys = append(keys, partition) - } - sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) - for _, partition := range keys { - pe.putInt32(partition) - if err := t.Partitions[partition].encode(pe); err != nil { - return err - } - } - return nil -} - -func (t *DeleteRecordsResponseTopic) decode(pd packetDecoder, version int16) error { - n, err := pd.getArrayLength() - if err != nil { - return err - } - - if n > 0 { - t.Partitions = make(map[int32]*DeleteRecordsResponsePartition, n) - for i := 0; i < n; i++ { - partition, err := pd.getInt32() - if err != nil { - return err - } - details := new(DeleteRecordsResponsePartition) - if err = details.decode(pd, version); err != nil { - return err - } - t.Partitions[partition] = details - } - } - - return nil -} - -type DeleteRecordsResponsePartition struct { - LowWatermark int64 - Err KError -} - -func (t *DeleteRecordsResponsePartition) encode(pe packetEncoder) error { - pe.putInt64(t.LowWatermark) - pe.putInt16(int16(t.Err)) - return nil -} - -func (t *DeleteRecordsResponsePartition) decode(pd packetDecoder, version int16) error { - lowWatermark, err := pd.getInt64() - if err != nil { - return err - } - t.LowWatermark = lowWatermark - - kErr, err := pd.getInt16() - if err != nil { - return err - } - t.Err = KError(kErr) - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/delete_topics_request.go b/vendor/github.com/Shopify/sarama/delete_topics_request.go deleted file mode 100644 index 911f67d31ba..00000000000 --- a/vendor/github.com/Shopify/sarama/delete_topics_request.go +++ /dev/null @@ -1,48 +0,0 @@ -package sarama - -import "time" - -type DeleteTopicsRequest struct { - Version int16 - Topics []string - Timeout time.Duration -} - -func (d *DeleteTopicsRequest) encode(pe packetEncoder) error { - if err := pe.putStringArray(d.Topics); err != nil { - return err - } - pe.putInt32(int32(d.Timeout / time.Millisecond)) - - return nil -} - -func (d *DeleteTopicsRequest) decode(pd packetDecoder, version int16) (err error) { - if d.Topics, err = pd.getStringArray(); err != nil { - return err - } - timeout, err := pd.getInt32() - if err != nil { - return err - } - d.Timeout = time.Duration(timeout) * time.Millisecond - d.Version = version - return nil -} - -func (d *DeleteTopicsRequest) key() int16 { - return 20 -} - -func (d *DeleteTopicsRequest) version() int16 { - return d.Version -} - -func (d *DeleteTopicsRequest) requiredVersion() KafkaVersion { - switch d.Version { - case 1: - return V0_11_0_0 - default: - return V0_10_1_0 - } -} diff --git a/vendor/github.com/Shopify/sarama/delete_topics_response.go b/vendor/github.com/Shopify/sarama/delete_topics_response.go deleted file mode 100644 index 34225460a31..00000000000 --- a/vendor/github.com/Shopify/sarama/delete_topics_response.go +++ /dev/null @@ -1,78 +0,0 @@ -package sarama - -import "time" - -type DeleteTopicsResponse struct { - Version int16 - ThrottleTime time.Duration - TopicErrorCodes map[string]KError -} - -func (d *DeleteTopicsResponse) encode(pe packetEncoder) error { - if d.Version >= 1 { - pe.putInt32(int32(d.ThrottleTime / time.Millisecond)) - } - - if err := pe.putArrayLength(len(d.TopicErrorCodes)); err != nil { - return err - } - for topic, errorCode := range d.TopicErrorCodes { - if err := pe.putString(topic); err != nil { - return err - } - pe.putInt16(int16(errorCode)) - } - - return nil -} - -func (d *DeleteTopicsResponse) decode(pd packetDecoder, version int16) (err error) { - if version >= 1 { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - d.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - d.Version = version - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - d.TopicErrorCodes = make(map[string]KError, n) - - for i := 0; i < n; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - errorCode, err := pd.getInt16() - if err != nil { - return err - } - - d.TopicErrorCodes[topic] = KError(errorCode) - } - - return nil -} - -func (d *DeleteTopicsResponse) key() int16 { - return 20 -} - -func (d *DeleteTopicsResponse) version() int16 { - return d.Version -} - -func (d *DeleteTopicsResponse) requiredVersion() KafkaVersion { - switch d.Version { - case 1: - return V0_11_0_0 - default: - return V0_10_1_0 - } -} diff --git a/vendor/github.com/Shopify/sarama/describe_configs_request.go b/vendor/github.com/Shopify/sarama/describe_configs_request.go deleted file mode 100644 index 7a7cffc3fb2..00000000000 --- a/vendor/github.com/Shopify/sarama/describe_configs_request.go +++ /dev/null @@ -1,91 +0,0 @@ -package sarama - -type ConfigResource struct { - Type ConfigResourceType - Name string - ConfigNames []string -} - -type DescribeConfigsRequest struct { - Resources []*ConfigResource -} - -func (r *DescribeConfigsRequest) encode(pe packetEncoder) error { - if err := pe.putArrayLength(len(r.Resources)); err != nil { - return err - } - - for _, c := range r.Resources { - pe.putInt8(int8(c.Type)) - if err := pe.putString(c.Name); err != nil { - return err - } - - if len(c.ConfigNames) == 0 { - pe.putInt32(-1) - continue - } - if err := pe.putStringArray(c.ConfigNames); err != nil { - return err - } - } - - return nil -} - -func (r *DescribeConfigsRequest) decode(pd packetDecoder, version int16) (err error) { - n, err := pd.getArrayLength() - if err != nil { - return err - } - - r.Resources = make([]*ConfigResource, n) - - for i := 0; i < n; i++ { - r.Resources[i] = &ConfigResource{} - t, err := pd.getInt8() - if err != nil { - return err - } - r.Resources[i].Type = ConfigResourceType(t) - name, err := pd.getString() - if err != nil { - return err - } - r.Resources[i].Name = name - - confLength, err := pd.getArrayLength() - - if err != nil { - return err - } - - if confLength == -1 { - continue - } - - cfnames := make([]string, confLength) - for i := 0; i < confLength; i++ { - s, err := pd.getString() - if err != nil { - return err - } - cfnames[i] = s - } - r.Resources[i].ConfigNames = cfnames - } - - return nil -} - -func (r *DescribeConfigsRequest) key() int16 { - return 32 -} - -func (r *DescribeConfigsRequest) version() int16 { - return 0 -} - -func (r *DescribeConfigsRequest) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/describe_configs_response.go b/vendor/github.com/Shopify/sarama/describe_configs_response.go deleted file mode 100644 index 6e5d30e4f09..00000000000 --- a/vendor/github.com/Shopify/sarama/describe_configs_response.go +++ /dev/null @@ -1,188 +0,0 @@ -package sarama - -import "time" - -type DescribeConfigsResponse struct { - ThrottleTime time.Duration - Resources []*ResourceResponse -} - -type ResourceResponse struct { - ErrorCode int16 - ErrorMsg string - Type ConfigResourceType - Name string - Configs []*ConfigEntry -} - -type ConfigEntry struct { - Name string - Value string - ReadOnly bool - Default bool - Sensitive bool -} - -func (r *DescribeConfigsResponse) encode(pe packetEncoder) (err error) { - pe.putInt32(int32(r.ThrottleTime / time.Millisecond)) - if err = pe.putArrayLength(len(r.Resources)); err != nil { - return err - } - - for _, c := range r.Resources { - if err = c.encode(pe); err != nil { - return err - } - } - return nil -} - -func (r *DescribeConfigsResponse) decode(pd packetDecoder, version int16) (err error) { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - r.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - r.Resources = make([]*ResourceResponse, n) - for i := 0; i < n; i++ { - rr := &ResourceResponse{} - if err := rr.decode(pd, version); err != nil { - return err - } - r.Resources[i] = rr - } - - return nil -} - -func (r *DescribeConfigsResponse) key() int16 { - return 32 -} - -func (r *DescribeConfigsResponse) version() int16 { - return 0 -} - -func (r *DescribeConfigsResponse) requiredVersion() KafkaVersion { - return V0_11_0_0 -} - -func (r *ResourceResponse) encode(pe packetEncoder) (err error) { - pe.putInt16(r.ErrorCode) - - if err = pe.putString(r.ErrorMsg); err != nil { - return err - } - - pe.putInt8(int8(r.Type)) - - if err = pe.putString(r.Name); err != nil { - return err - } - - if err = pe.putArrayLength(len(r.Configs)); err != nil { - return err - } - - for _, c := range r.Configs { - if err = c.encode(pe); err != nil { - return err - } - } - return nil -} - -func (r *ResourceResponse) decode(pd packetDecoder, version int16) (err error) { - ec, err := pd.getInt16() - if err != nil { - return err - } - r.ErrorCode = ec - - em, err := pd.getString() - if err != nil { - return err - } - r.ErrorMsg = em - - t, err := pd.getInt8() - if err != nil { - return err - } - r.Type = ConfigResourceType(t) - - name, err := pd.getString() - if err != nil { - return err - } - r.Name = name - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - r.Configs = make([]*ConfigEntry, n) - for i := 0; i < n; i++ { - c := &ConfigEntry{} - if err := c.decode(pd, version); err != nil { - return err - } - r.Configs[i] = c - } - return nil -} - -func (r *ConfigEntry) encode(pe packetEncoder) (err error) { - if err = pe.putString(r.Name); err != nil { - return err - } - - if err = pe.putString(r.Value); err != nil { - return err - } - - pe.putBool(r.ReadOnly) - pe.putBool(r.Default) - pe.putBool(r.Sensitive) - return nil -} - -func (r *ConfigEntry) decode(pd packetDecoder, version int16) (err error) { - name, err := pd.getString() - if err != nil { - return err - } - r.Name = name - - value, err := pd.getString() - if err != nil { - return err - } - r.Value = value - - read, err := pd.getBool() - if err != nil { - return err - } - r.ReadOnly = read - - de, err := pd.getBool() - if err != nil { - return err - } - r.Default = de - - sensitive, err := pd.getBool() - if err != nil { - return err - } - r.Sensitive = sensitive - return nil -} diff --git a/vendor/github.com/Shopify/sarama/describe_groups_request.go b/vendor/github.com/Shopify/sarama/describe_groups_request.go deleted file mode 100644 index 1fb35677708..00000000000 --- a/vendor/github.com/Shopify/sarama/describe_groups_request.go +++ /dev/null @@ -1,30 +0,0 @@ -package sarama - -type DescribeGroupsRequest struct { - Groups []string -} - -func (r *DescribeGroupsRequest) encode(pe packetEncoder) error { - return pe.putStringArray(r.Groups) -} - -func (r *DescribeGroupsRequest) decode(pd packetDecoder, version int16) (err error) { - r.Groups, err = pd.getStringArray() - return -} - -func (r *DescribeGroupsRequest) key() int16 { - return 15 -} - -func (r *DescribeGroupsRequest) version() int16 { - return 0 -} - -func (r *DescribeGroupsRequest) requiredVersion() KafkaVersion { - return V0_9_0_0 -} - -func (r *DescribeGroupsRequest) AddGroup(group string) { - r.Groups = append(r.Groups, group) -} diff --git a/vendor/github.com/Shopify/sarama/describe_groups_response.go b/vendor/github.com/Shopify/sarama/describe_groups_response.go deleted file mode 100644 index 542b3a97170..00000000000 --- a/vendor/github.com/Shopify/sarama/describe_groups_response.go +++ /dev/null @@ -1,187 +0,0 @@ -package sarama - -type DescribeGroupsResponse struct { - Groups []*GroupDescription -} - -func (r *DescribeGroupsResponse) encode(pe packetEncoder) error { - if err := pe.putArrayLength(len(r.Groups)); err != nil { - return err - } - - for _, groupDescription := range r.Groups { - if err := groupDescription.encode(pe); err != nil { - return err - } - } - - return nil -} - -func (r *DescribeGroupsResponse) decode(pd packetDecoder, version int16) (err error) { - n, err := pd.getArrayLength() - if err != nil { - return err - } - - r.Groups = make([]*GroupDescription, n) - for i := 0; i < n; i++ { - r.Groups[i] = new(GroupDescription) - if err := r.Groups[i].decode(pd); err != nil { - return err - } - } - - return nil -} - -func (r *DescribeGroupsResponse) key() int16 { - return 15 -} - -func (r *DescribeGroupsResponse) version() int16 { - return 0 -} - -func (r *DescribeGroupsResponse) requiredVersion() KafkaVersion { - return V0_9_0_0 -} - -type GroupDescription struct { - Err KError - GroupId string - State string - ProtocolType string - Protocol string - Members map[string]*GroupMemberDescription -} - -func (gd *GroupDescription) encode(pe packetEncoder) error { - pe.putInt16(int16(gd.Err)) - - if err := pe.putString(gd.GroupId); err != nil { - return err - } - if err := pe.putString(gd.State); err != nil { - return err - } - if err := pe.putString(gd.ProtocolType); err != nil { - return err - } - if err := pe.putString(gd.Protocol); err != nil { - return err - } - - if err := pe.putArrayLength(len(gd.Members)); err != nil { - return err - } - - for memberId, groupMemberDescription := range gd.Members { - if err := pe.putString(memberId); err != nil { - return err - } - if err := groupMemberDescription.encode(pe); err != nil { - return err - } - } - - return nil -} - -func (gd *GroupDescription) decode(pd packetDecoder) (err error) { - kerr, err := pd.getInt16() - if err != nil { - return err - } - - gd.Err = KError(kerr) - - if gd.GroupId, err = pd.getString(); err != nil { - return - } - if gd.State, err = pd.getString(); err != nil { - return - } - if gd.ProtocolType, err = pd.getString(); err != nil { - return - } - if gd.Protocol, err = pd.getString(); err != nil { - return - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - if n == 0 { - return nil - } - - gd.Members = make(map[string]*GroupMemberDescription) - for i := 0; i < n; i++ { - memberId, err := pd.getString() - if err != nil { - return err - } - - gd.Members[memberId] = new(GroupMemberDescription) - if err := gd.Members[memberId].decode(pd); err != nil { - return err - } - } - - return nil -} - -type GroupMemberDescription struct { - ClientId string - ClientHost string - MemberMetadata []byte - MemberAssignment []byte -} - -func (gmd *GroupMemberDescription) encode(pe packetEncoder) error { - if err := pe.putString(gmd.ClientId); err != nil { - return err - } - if err := pe.putString(gmd.ClientHost); err != nil { - return err - } - if err := pe.putBytes(gmd.MemberMetadata); err != nil { - return err - } - if err := pe.putBytes(gmd.MemberAssignment); err != nil { - return err - } - - return nil -} - -func (gmd *GroupMemberDescription) decode(pd packetDecoder) (err error) { - if gmd.ClientId, err = pd.getString(); err != nil { - return - } - if gmd.ClientHost, err = pd.getString(); err != nil { - return - } - if gmd.MemberMetadata, err = pd.getBytes(); err != nil { - return - } - if gmd.MemberAssignment, err = pd.getBytes(); err != nil { - return - } - - return nil -} - -func (gmd *GroupMemberDescription) GetMemberAssignment() (*ConsumerGroupMemberAssignment, error) { - assignment := new(ConsumerGroupMemberAssignment) - err := decode(gmd.MemberAssignment, assignment) - return assignment, err -} - -func (gmd *GroupMemberDescription) GetMemberMetadata() (*ConsumerGroupMemberMetadata, error) { - metadata := new(ConsumerGroupMemberMetadata) - err := decode(gmd.MemberMetadata, metadata) - return metadata, err -} diff --git a/vendor/github.com/Shopify/sarama/encoder_decoder.go b/vendor/github.com/Shopify/sarama/encoder_decoder.go deleted file mode 100644 index 7ce3bc0f6e2..00000000000 --- a/vendor/github.com/Shopify/sarama/encoder_decoder.go +++ /dev/null @@ -1,89 +0,0 @@ -package sarama - -import ( - "fmt" - - "github.com/rcrowley/go-metrics" -) - -// Encoder is the interface that wraps the basic Encode method. -// Anything implementing Encoder can be turned into bytes using Kafka's encoding rules. -type encoder interface { - encode(pe packetEncoder) error -} - -// Encode takes an Encoder and turns it into bytes while potentially recording metrics. -func encode(e encoder, metricRegistry metrics.Registry) ([]byte, error) { - if e == nil { - return nil, nil - } - - var prepEnc prepEncoder - var realEnc realEncoder - - err := e.encode(&prepEnc) - if err != nil { - return nil, err - } - - if prepEnc.length < 0 || prepEnc.length > int(MaxRequestSize) { - return nil, PacketEncodingError{fmt.Sprintf("invalid request size (%d)", prepEnc.length)} - } - - realEnc.raw = make([]byte, prepEnc.length) - realEnc.registry = metricRegistry - err = e.encode(&realEnc) - if err != nil { - return nil, err - } - - return realEnc.raw, nil -} - -// Decoder is the interface that wraps the basic Decode method. -// Anything implementing Decoder can be extracted from bytes using Kafka's encoding rules. -type decoder interface { - decode(pd packetDecoder) error -} - -type versionedDecoder interface { - decode(pd packetDecoder, version int16) error -} - -// Decode takes bytes and a Decoder and fills the fields of the decoder from the bytes, -// interpreted using Kafka's encoding rules. -func decode(buf []byte, in decoder) error { - if buf == nil { - return nil - } - - helper := realDecoder{raw: buf} - err := in.decode(&helper) - if err != nil { - return err - } - - if helper.off != len(buf) { - return PacketDecodingError{"invalid length"} - } - - return nil -} - -func versionedDecode(buf []byte, in versionedDecoder, version int16) error { - if buf == nil { - return nil - } - - helper := realDecoder{raw: buf} - err := in.decode(&helper, version) - if err != nil { - return err - } - - if helper.off != len(buf) { - return PacketDecodingError{"invalid length"} - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/end_txn_request.go b/vendor/github.com/Shopify/sarama/end_txn_request.go deleted file mode 100644 index 2cd9b506d3f..00000000000 --- a/vendor/github.com/Shopify/sarama/end_txn_request.go +++ /dev/null @@ -1,50 +0,0 @@ -package sarama - -type EndTxnRequest struct { - TransactionalID string - ProducerID int64 - ProducerEpoch int16 - TransactionResult bool -} - -func (a *EndTxnRequest) encode(pe packetEncoder) error { - if err := pe.putString(a.TransactionalID); err != nil { - return err - } - - pe.putInt64(a.ProducerID) - - pe.putInt16(a.ProducerEpoch) - - pe.putBool(a.TransactionResult) - - return nil -} - -func (a *EndTxnRequest) decode(pd packetDecoder, version int16) (err error) { - if a.TransactionalID, err = pd.getString(); err != nil { - return err - } - if a.ProducerID, err = pd.getInt64(); err != nil { - return err - } - if a.ProducerEpoch, err = pd.getInt16(); err != nil { - return err - } - if a.TransactionResult, err = pd.getBool(); err != nil { - return err - } - return nil -} - -func (a *EndTxnRequest) key() int16 { - return 26 -} - -func (a *EndTxnRequest) version() int16 { - return 0 -} - -func (a *EndTxnRequest) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/end_txn_response.go b/vendor/github.com/Shopify/sarama/end_txn_response.go deleted file mode 100644 index 33b27e33d49..00000000000 --- a/vendor/github.com/Shopify/sarama/end_txn_response.go +++ /dev/null @@ -1,44 +0,0 @@ -package sarama - -import ( - "time" -) - -type EndTxnResponse struct { - ThrottleTime time.Duration - Err KError -} - -func (e *EndTxnResponse) encode(pe packetEncoder) error { - pe.putInt32(int32(e.ThrottleTime / time.Millisecond)) - pe.putInt16(int16(e.Err)) - return nil -} - -func (e *EndTxnResponse) decode(pd packetDecoder, version int16) (err error) { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - e.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - kerr, err := pd.getInt16() - if err != nil { - return err - } - e.Err = KError(kerr) - - return nil -} - -func (e *EndTxnResponse) key() int16 { - return 25 -} - -func (e *EndTxnResponse) version() int16 { - return 0 -} - -func (e *EndTxnResponse) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/errors.go b/vendor/github.com/Shopify/sarama/errors.go deleted file mode 100644 index c578ef5fb43..00000000000 --- a/vendor/github.com/Shopify/sarama/errors.go +++ /dev/null @@ -1,281 +0,0 @@ -package sarama - -import ( - "errors" - "fmt" -) - -// ErrOutOfBrokers is the error returned when the client has run out of brokers to talk to because all of them errored -// or otherwise failed to respond. -var ErrOutOfBrokers = errors.New("kafka: client has run out of available brokers to talk to (Is your cluster reachable?)") - -// ErrClosedClient is the error returned when a method is called on a client that has been closed. -var ErrClosedClient = errors.New("kafka: tried to use a client that was closed") - -// ErrIncompleteResponse is the error returned when the server returns a syntactically valid response, but it does -// not contain the expected information. -var ErrIncompleteResponse = errors.New("kafka: response did not contain all the expected topic/partition blocks") - -// ErrInvalidPartition is the error returned when a partitioner returns an invalid partition index -// (meaning one outside of the range [0...numPartitions-1]). -var ErrInvalidPartition = errors.New("kafka: partitioner returned an invalid partition index") - -// ErrAlreadyConnected is the error returned when calling Open() on a Broker that is already connected or connecting. -var ErrAlreadyConnected = errors.New("kafka: broker connection already initiated") - -// ErrNotConnected is the error returned when trying to send or call Close() on a Broker that is not connected. -var ErrNotConnected = errors.New("kafka: broker not connected") - -// ErrInsufficientData is returned when decoding and the packet is truncated. This can be expected -// when requesting messages, since as an optimization the server is allowed to return a partial message at the end -// of the message set. -var ErrInsufficientData = errors.New("kafka: insufficient data to decode packet, more bytes expected") - -// ErrShuttingDown is returned when a producer receives a message during shutdown. -var ErrShuttingDown = errors.New("kafka: message received by producer in process of shutting down") - -// ErrMessageTooLarge is returned when the next message to consume is larger than the configured Consumer.Fetch.Max -var ErrMessageTooLarge = errors.New("kafka: message is larger than Consumer.Fetch.Max") - -// ErrConsumerOffsetNotAdvanced is returned when a partition consumer didn't advance its offset after parsing -// a RecordBatch. -var ErrConsumerOffsetNotAdvanced = errors.New("kafka: consumer offset was not advanced after a RecordBatch") - -// ErrControllerNotAvailable is returned when server didn't give correct controller id. May be kafka server's version -// is lower than 0.10.0.0. -var ErrControllerNotAvailable = errors.New("kafka: controller is not available") - -// ErrNoTopicsToUpdateMetadata is returned when Meta.Full is set to false but no specific topics were found to update -// the metadata. -var ErrNoTopicsToUpdateMetadata = errors.New("kafka: no specific topics to update metadata") - -// PacketEncodingError is returned from a failure while encoding a Kafka packet. This can happen, for example, -// if you try to encode a string over 2^15 characters in length, since Kafka's encoding rules do not permit that. -type PacketEncodingError struct { - Info string -} - -func (err PacketEncodingError) Error() string { - return fmt.Sprintf("kafka: error encoding packet: %s", err.Info) -} - -// PacketDecodingError is returned when there was an error (other than truncated data) decoding the Kafka broker's response. -// This can be a bad CRC or length field, or any other invalid value. -type PacketDecodingError struct { - Info string -} - -func (err PacketDecodingError) Error() string { - return fmt.Sprintf("kafka: error decoding packet: %s", err.Info) -} - -// ConfigurationError is the type of error returned from a constructor (e.g. NewClient, or NewConsumer) -// when the specified configuration is invalid. -type ConfigurationError string - -func (err ConfigurationError) Error() string { - return "kafka: invalid configuration (" + string(err) + ")" -} - -// KError is the type of error that can be returned directly by the Kafka broker. -// See https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol#AGuideToTheKafkaProtocol-ErrorCodes -type KError int16 - -// Numeric error codes returned by the Kafka server. -const ( - ErrNoError KError = 0 - ErrUnknown KError = -1 - ErrOffsetOutOfRange KError = 1 - ErrInvalidMessage KError = 2 - ErrUnknownTopicOrPartition KError = 3 - ErrInvalidMessageSize KError = 4 - ErrLeaderNotAvailable KError = 5 - ErrNotLeaderForPartition KError = 6 - ErrRequestTimedOut KError = 7 - ErrBrokerNotAvailable KError = 8 - ErrReplicaNotAvailable KError = 9 - ErrMessageSizeTooLarge KError = 10 - ErrStaleControllerEpochCode KError = 11 - ErrOffsetMetadataTooLarge KError = 12 - ErrNetworkException KError = 13 - ErrOffsetsLoadInProgress KError = 14 - ErrConsumerCoordinatorNotAvailable KError = 15 - ErrNotCoordinatorForConsumer KError = 16 - ErrInvalidTopic KError = 17 - ErrMessageSetSizeTooLarge KError = 18 - ErrNotEnoughReplicas KError = 19 - ErrNotEnoughReplicasAfterAppend KError = 20 - ErrInvalidRequiredAcks KError = 21 - ErrIllegalGeneration KError = 22 - ErrInconsistentGroupProtocol KError = 23 - ErrInvalidGroupId KError = 24 - ErrUnknownMemberId KError = 25 - ErrInvalidSessionTimeout KError = 26 - ErrRebalanceInProgress KError = 27 - ErrInvalidCommitOffsetSize KError = 28 - ErrTopicAuthorizationFailed KError = 29 - ErrGroupAuthorizationFailed KError = 30 - ErrClusterAuthorizationFailed KError = 31 - ErrInvalidTimestamp KError = 32 - ErrUnsupportedSASLMechanism KError = 33 - ErrIllegalSASLState KError = 34 - ErrUnsupportedVersion KError = 35 - ErrTopicAlreadyExists KError = 36 - ErrInvalidPartitions KError = 37 - ErrInvalidReplicationFactor KError = 38 - ErrInvalidReplicaAssignment KError = 39 - ErrInvalidConfig KError = 40 - ErrNotController KError = 41 - ErrInvalidRequest KError = 42 - ErrUnsupportedForMessageFormat KError = 43 - ErrPolicyViolation KError = 44 - ErrOutOfOrderSequenceNumber KError = 45 - ErrDuplicateSequenceNumber KError = 46 - ErrInvalidProducerEpoch KError = 47 - ErrInvalidTxnState KError = 48 - ErrInvalidProducerIDMapping KError = 49 - ErrInvalidTransactionTimeout KError = 50 - ErrConcurrentTransactions KError = 51 - ErrTransactionCoordinatorFenced KError = 52 - ErrTransactionalIDAuthorizationFailed KError = 53 - ErrSecurityDisabled KError = 54 - ErrOperationNotAttempted KError = 55 - ErrKafkaStorageError KError = 56 - ErrLogDirNotFound KError = 57 - ErrSASLAuthenticationFailed KError = 58 - ErrUnknownProducerID KError = 59 - ErrReassignmentInProgress KError = 60 -) - -func (err KError) Error() string { - // Error messages stolen/adapted from - // https://kafka.apache.org/protocol#protocol_error_codes - switch err { - case ErrNoError: - return "kafka server: Not an error, why are you printing me?" - case ErrUnknown: - return "kafka server: Unexpected (unknown?) server error." - case ErrOffsetOutOfRange: - return "kafka server: The requested offset is outside the range of offsets maintained by the server for the given topic/partition." - case ErrInvalidMessage: - return "kafka server: Message contents does not match its CRC." - case ErrUnknownTopicOrPartition: - return "kafka server: Request was for a topic or partition that does not exist on this broker." - case ErrInvalidMessageSize: - return "kafka server: The message has a negative size." - case ErrLeaderNotAvailable: - return "kafka server: In the middle of a leadership election, there is currently no leader for this partition and hence it is unavailable for writes." - case ErrNotLeaderForPartition: - return "kafka server: Tried to send a message to a replica that is not the leader for some partition. Your metadata is out of date." - case ErrRequestTimedOut: - return "kafka server: Request exceeded the user-specified time limit in the request." - case ErrBrokerNotAvailable: - return "kafka server: Broker not available. Not a client facing error, we should never receive this!!!" - case ErrReplicaNotAvailable: - return "kafka server: Replica information not available, one or more brokers are down." - case ErrMessageSizeTooLarge: - return "kafka server: Message was too large, server rejected it to avoid allocation error." - case ErrStaleControllerEpochCode: - return "kafka server: StaleControllerEpochCode (internal error code for broker-to-broker communication)." - case ErrOffsetMetadataTooLarge: - return "kafka server: Specified a string larger than the configured maximum for offset metadata." - case ErrNetworkException: - return "kafka server: The server disconnected before a response was received." - case ErrOffsetsLoadInProgress: - return "kafka server: The broker is still loading offsets after a leader change for that offset's topic partition." - case ErrConsumerCoordinatorNotAvailable: - return "kafka server: Offset's topic has not yet been created." - case ErrNotCoordinatorForConsumer: - return "kafka server: Request was for a consumer group that is not coordinated by this broker." - case ErrInvalidTopic: - return "kafka server: The request attempted to perform an operation on an invalid topic." - case ErrMessageSetSizeTooLarge: - return "kafka server: The request included message batch larger than the configured segment size on the server." - case ErrNotEnoughReplicas: - return "kafka server: Messages are rejected since there are fewer in-sync replicas than required." - case ErrNotEnoughReplicasAfterAppend: - return "kafka server: Messages are written to the log, but to fewer in-sync replicas than required." - case ErrInvalidRequiredAcks: - return "kafka server: The number of required acks is invalid (should be either -1, 0, or 1)." - case ErrIllegalGeneration: - return "kafka server: The provided generation id is not the current generation." - case ErrInconsistentGroupProtocol: - return "kafka server: The provider group protocol type is incompatible with the other members." - case ErrInvalidGroupId: - return "kafka server: The provided group id was empty." - case ErrUnknownMemberId: - return "kafka server: The provided member is not known in the current generation." - case ErrInvalidSessionTimeout: - return "kafka server: The provided session timeout is outside the allowed range." - case ErrRebalanceInProgress: - return "kafka server: A rebalance for the group is in progress. Please re-join the group." - case ErrInvalidCommitOffsetSize: - return "kafka server: The provided commit metadata was too large." - case ErrTopicAuthorizationFailed: - return "kafka server: The client is not authorized to access this topic." - case ErrGroupAuthorizationFailed: - return "kafka server: The client is not authorized to access this group." - case ErrClusterAuthorizationFailed: - return "kafka server: The client is not authorized to send this request type." - case ErrInvalidTimestamp: - return "kafka server: The timestamp of the message is out of acceptable range." - case ErrUnsupportedSASLMechanism: - return "kafka server: The broker does not support the requested SASL mechanism." - case ErrIllegalSASLState: - return "kafka server: Request is not valid given the current SASL state." - case ErrUnsupportedVersion: - return "kafka server: The version of API is not supported." - case ErrTopicAlreadyExists: - return "kafka server: Topic with this name already exists." - case ErrInvalidPartitions: - return "kafka server: Number of partitions is invalid." - case ErrInvalidReplicationFactor: - return "kafka server: Replication-factor is invalid." - case ErrInvalidReplicaAssignment: - return "kafka server: Replica assignment is invalid." - case ErrInvalidConfig: - return "kafka server: Configuration is invalid." - case ErrNotController: - return "kafka server: This is not the correct controller for this cluster." - case ErrInvalidRequest: - return "kafka server: This most likely occurs because of a request being malformed by the client library or the message was sent to an incompatible broker. See the broker logs for more details." - case ErrUnsupportedForMessageFormat: - return "kafka server: The requested operation is not supported by the message format version." - case ErrPolicyViolation: - return "kafka server: Request parameters do not satisfy the configured policy." - case ErrOutOfOrderSequenceNumber: - return "kafka server: The broker received an out of order sequence number." - case ErrDuplicateSequenceNumber: - return "kafka server: The broker received a duplicate sequence number." - case ErrInvalidProducerEpoch: - return "kafka server: Producer attempted an operation with an old epoch." - case ErrInvalidTxnState: - return "kafka server: The producer attempted a transactional operation in an invalid state." - case ErrInvalidProducerIDMapping: - return "kafka server: The producer attempted to use a producer id which is not currently assigned to its transactional id." - case ErrInvalidTransactionTimeout: - return "kafka server: The transaction timeout is larger than the maximum value allowed by the broker (as configured by max.transaction.timeout.ms)." - case ErrConcurrentTransactions: - return "kafka server: The producer attempted to update a transaction while another concurrent operation on the same transaction was ongoing." - case ErrTransactionCoordinatorFenced: - return "kafka server: The transaction coordinator sending a WriteTxnMarker is no longer the current coordinator for a given producer." - case ErrTransactionalIDAuthorizationFailed: - return "kafka server: Transactional ID authorization failed." - case ErrSecurityDisabled: - return "kafka server: Security features are disabled." - case ErrOperationNotAttempted: - return "kafka server: The broker did not attempt to execute this operation." - case ErrKafkaStorageError: - return "kafka server: Disk error when trying to access log file on the disk." - case ErrLogDirNotFound: - return "kafka server: The specified log directory is not found in the broker config." - case ErrSASLAuthenticationFailed: - return "kafka server: SASL Authentication failed." - case ErrUnknownProducerID: - return "kafka server: The broker could not locate the producer metadata associated with the Producer ID." - case ErrReassignmentInProgress: - return "kafka server: A partition reassignment is in progress." - } - - return fmt.Sprintf("Unknown error, how did this happen? Error code = %d", err) -} diff --git a/vendor/github.com/Shopify/sarama/fetch_request.go b/vendor/github.com/Shopify/sarama/fetch_request.go deleted file mode 100644 index 462ab8afbb8..00000000000 --- a/vendor/github.com/Shopify/sarama/fetch_request.go +++ /dev/null @@ -1,170 +0,0 @@ -package sarama - -type fetchRequestBlock struct { - fetchOffset int64 - maxBytes int32 -} - -func (b *fetchRequestBlock) encode(pe packetEncoder) error { - pe.putInt64(b.fetchOffset) - pe.putInt32(b.maxBytes) - return nil -} - -func (b *fetchRequestBlock) decode(pd packetDecoder) (err error) { - if b.fetchOffset, err = pd.getInt64(); err != nil { - return err - } - if b.maxBytes, err = pd.getInt32(); err != nil { - return err - } - return nil -} - -// FetchRequest (API key 1) will fetch Kafka messages. Version 3 introduced the MaxBytes field. See -// https://issues.apache.org/jira/browse/KAFKA-2063 for a discussion of the issues leading up to that. The KIP is at -// https://cwiki.apache.org/confluence/display/KAFKA/KIP-74%3A+Add+Fetch+Response+Size+Limit+in+Bytes -type FetchRequest struct { - MaxWaitTime int32 - MinBytes int32 - MaxBytes int32 - Version int16 - Isolation IsolationLevel - blocks map[string]map[int32]*fetchRequestBlock -} - -type IsolationLevel int8 - -const ( - ReadUncommitted IsolationLevel = 0 - ReadCommitted IsolationLevel = 1 -) - -func (r *FetchRequest) encode(pe packetEncoder) (err error) { - pe.putInt32(-1) // replica ID is always -1 for clients - pe.putInt32(r.MaxWaitTime) - pe.putInt32(r.MinBytes) - if r.Version >= 3 { - pe.putInt32(r.MaxBytes) - } - if r.Version >= 4 { - pe.putInt8(int8(r.Isolation)) - } - err = pe.putArrayLength(len(r.blocks)) - if err != nil { - return err - } - for topic, blocks := range r.blocks { - err = pe.putString(topic) - if err != nil { - return err - } - err = pe.putArrayLength(len(blocks)) - if err != nil { - return err - } - for partition, block := range blocks { - pe.putInt32(partition) - err = block.encode(pe) - if err != nil { - return err - } - } - } - return nil -} - -func (r *FetchRequest) decode(pd packetDecoder, version int16) (err error) { - r.Version = version - if _, err = pd.getInt32(); err != nil { - return err - } - if r.MaxWaitTime, err = pd.getInt32(); err != nil { - return err - } - if r.MinBytes, err = pd.getInt32(); err != nil { - return err - } - if r.Version >= 3 { - if r.MaxBytes, err = pd.getInt32(); err != nil { - return err - } - } - if r.Version >= 4 { - isolation, err := pd.getInt8() - if err != nil { - return err - } - r.Isolation = IsolationLevel(isolation) - } - topicCount, err := pd.getArrayLength() - if err != nil { - return err - } - if topicCount == 0 { - return nil - } - r.blocks = make(map[string]map[int32]*fetchRequestBlock) - for i := 0; i < topicCount; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - partitionCount, err := pd.getArrayLength() - if err != nil { - return err - } - r.blocks[topic] = make(map[int32]*fetchRequestBlock) - for j := 0; j < partitionCount; j++ { - partition, err := pd.getInt32() - if err != nil { - return err - } - fetchBlock := &fetchRequestBlock{} - if err = fetchBlock.decode(pd); err != nil { - return err - } - r.blocks[topic][partition] = fetchBlock - } - } - return nil -} - -func (r *FetchRequest) key() int16 { - return 1 -} - -func (r *FetchRequest) version() int16 { - return r.Version -} - -func (r *FetchRequest) requiredVersion() KafkaVersion { - switch r.Version { - case 1: - return V0_9_0_0 - case 2: - return V0_10_0_0 - case 3: - return V0_10_1_0 - case 4: - return V0_11_0_0 - default: - return MinVersion - } -} - -func (r *FetchRequest) AddBlock(topic string, partitionID int32, fetchOffset int64, maxBytes int32) { - if r.blocks == nil { - r.blocks = make(map[string]map[int32]*fetchRequestBlock) - } - - if r.blocks[topic] == nil { - r.blocks[topic] = make(map[int32]*fetchRequestBlock) - } - - tmp := new(fetchRequestBlock) - tmp.maxBytes = maxBytes - tmp.fetchOffset = fetchOffset - - r.blocks[topic][partitionID] = tmp -} diff --git a/vendor/github.com/Shopify/sarama/fetch_response.go b/vendor/github.com/Shopify/sarama/fetch_response.go deleted file mode 100644 index ae91bb9eb09..00000000000 --- a/vendor/github.com/Shopify/sarama/fetch_response.go +++ /dev/null @@ -1,385 +0,0 @@ -package sarama - -import ( - "time" -) - -type AbortedTransaction struct { - ProducerID int64 - FirstOffset int64 -} - -func (t *AbortedTransaction) decode(pd packetDecoder) (err error) { - if t.ProducerID, err = pd.getInt64(); err != nil { - return err - } - - if t.FirstOffset, err = pd.getInt64(); err != nil { - return err - } - - return nil -} - -func (t *AbortedTransaction) encode(pe packetEncoder) (err error) { - pe.putInt64(t.ProducerID) - pe.putInt64(t.FirstOffset) - - return nil -} - -type FetchResponseBlock struct { - Err KError - HighWaterMarkOffset int64 - LastStableOffset int64 - AbortedTransactions []*AbortedTransaction - Records *Records // deprecated: use FetchResponseBlock.Records - RecordsSet []*Records - Partial bool -} - -func (b *FetchResponseBlock) decode(pd packetDecoder, version int16) (err error) { - tmp, err := pd.getInt16() - if err != nil { - return err - } - b.Err = KError(tmp) - - b.HighWaterMarkOffset, err = pd.getInt64() - if err != nil { - return err - } - - if version >= 4 { - b.LastStableOffset, err = pd.getInt64() - if err != nil { - return err - } - - numTransact, err := pd.getArrayLength() - if err != nil { - return err - } - - if numTransact >= 0 { - b.AbortedTransactions = make([]*AbortedTransaction, numTransact) - } - - for i := 0; i < numTransact; i++ { - transact := new(AbortedTransaction) - if err = transact.decode(pd); err != nil { - return err - } - b.AbortedTransactions[i] = transact - } - } - - recordsSize, err := pd.getInt32() - if err != nil { - return err - } - - recordsDecoder, err := pd.getSubset(int(recordsSize)) - if err != nil { - return err - } - - b.RecordsSet = []*Records{} - - for recordsDecoder.remaining() > 0 { - records := &Records{} - if err := records.decode(recordsDecoder); err != nil { - // If we have at least one decoded records, this is not an error - if err == ErrInsufficientData { - if len(b.RecordsSet) == 0 { - b.Partial = true - } - break - } - return err - } - - partial, err := records.isPartial() - if err != nil { - return err - } - - // If we have at least one full records, we skip incomplete ones - if partial && len(b.RecordsSet) > 0 { - break - } - - b.RecordsSet = append(b.RecordsSet, records) - - if b.Records == nil { - b.Records = records - } - } - - return nil -} - -func (b *FetchResponseBlock) numRecords() (int, error) { - sum := 0 - - for _, records := range b.RecordsSet { - count, err := records.numRecords() - if err != nil { - return 0, err - } - - sum += count - } - - return sum, nil -} - -func (b *FetchResponseBlock) isPartial() (bool, error) { - if b.Partial { - return true, nil - } - - if len(b.RecordsSet) == 1 { - return b.RecordsSet[0].isPartial() - } - - return false, nil -} - -func (b *FetchResponseBlock) encode(pe packetEncoder, version int16) (err error) { - pe.putInt16(int16(b.Err)) - - pe.putInt64(b.HighWaterMarkOffset) - - if version >= 4 { - pe.putInt64(b.LastStableOffset) - - if err = pe.putArrayLength(len(b.AbortedTransactions)); err != nil { - return err - } - for _, transact := range b.AbortedTransactions { - if err = transact.encode(pe); err != nil { - return err - } - } - } - - pe.push(&lengthField{}) - for _, records := range b.RecordsSet { - err = records.encode(pe) - if err != nil { - return err - } - } - return pe.pop() -} - -type FetchResponse struct { - Blocks map[string]map[int32]*FetchResponseBlock - ThrottleTime time.Duration - Version int16 // v1 requires 0.9+, v2 requires 0.10+ -} - -func (r *FetchResponse) decode(pd packetDecoder, version int16) (err error) { - r.Version = version - - if r.Version >= 1 { - throttle, err := pd.getInt32() - if err != nil { - return err - } - r.ThrottleTime = time.Duration(throttle) * time.Millisecond - } - - numTopics, err := pd.getArrayLength() - if err != nil { - return err - } - - r.Blocks = make(map[string]map[int32]*FetchResponseBlock, numTopics) - for i := 0; i < numTopics; i++ { - name, err := pd.getString() - if err != nil { - return err - } - - numBlocks, err := pd.getArrayLength() - if err != nil { - return err - } - - r.Blocks[name] = make(map[int32]*FetchResponseBlock, numBlocks) - - for j := 0; j < numBlocks; j++ { - id, err := pd.getInt32() - if err != nil { - return err - } - - block := new(FetchResponseBlock) - err = block.decode(pd, version) - if err != nil { - return err - } - r.Blocks[name][id] = block - } - } - - return nil -} - -func (r *FetchResponse) encode(pe packetEncoder) (err error) { - if r.Version >= 1 { - pe.putInt32(int32(r.ThrottleTime / time.Millisecond)) - } - - err = pe.putArrayLength(len(r.Blocks)) - if err != nil { - return err - } - - for topic, partitions := range r.Blocks { - err = pe.putString(topic) - if err != nil { - return err - } - - err = pe.putArrayLength(len(partitions)) - if err != nil { - return err - } - - for id, block := range partitions { - pe.putInt32(id) - err = block.encode(pe, r.Version) - if err != nil { - return err - } - } - - } - return nil -} - -func (r *FetchResponse) key() int16 { - return 1 -} - -func (r *FetchResponse) version() int16 { - return r.Version -} - -func (r *FetchResponse) requiredVersion() KafkaVersion { - switch r.Version { - case 1: - return V0_9_0_0 - case 2: - return V0_10_0_0 - case 3: - return V0_10_1_0 - case 4: - return V0_11_0_0 - default: - return MinVersion - } -} - -func (r *FetchResponse) GetBlock(topic string, partition int32) *FetchResponseBlock { - if r.Blocks == nil { - return nil - } - - if r.Blocks[topic] == nil { - return nil - } - - return r.Blocks[topic][partition] -} - -func (r *FetchResponse) AddError(topic string, partition int32, err KError) { - if r.Blocks == nil { - r.Blocks = make(map[string]map[int32]*FetchResponseBlock) - } - partitions, ok := r.Blocks[topic] - if !ok { - partitions = make(map[int32]*FetchResponseBlock) - r.Blocks[topic] = partitions - } - frb, ok := partitions[partition] - if !ok { - frb = new(FetchResponseBlock) - partitions[partition] = frb - } - frb.Err = err -} - -func (r *FetchResponse) getOrCreateBlock(topic string, partition int32) *FetchResponseBlock { - if r.Blocks == nil { - r.Blocks = make(map[string]map[int32]*FetchResponseBlock) - } - partitions, ok := r.Blocks[topic] - if !ok { - partitions = make(map[int32]*FetchResponseBlock) - r.Blocks[topic] = partitions - } - frb, ok := partitions[partition] - if !ok { - frb = new(FetchResponseBlock) - partitions[partition] = frb - } - - return frb -} - -func encodeKV(key, value Encoder) ([]byte, []byte) { - var kb []byte - var vb []byte - if key != nil { - kb, _ = key.Encode() - } - if value != nil { - vb, _ = value.Encode() - } - - return kb, vb -} - -func (r *FetchResponse) AddMessage(topic string, partition int32, key, value Encoder, offset int64) { - frb := r.getOrCreateBlock(topic, partition) - kb, vb := encodeKV(key, value) - msg := &Message{Key: kb, Value: vb} - msgBlock := &MessageBlock{Msg: msg, Offset: offset} - if len(frb.RecordsSet) == 0 { - records := newLegacyRecords(&MessageSet{}) - frb.RecordsSet = []*Records{&records} - } - set := frb.RecordsSet[0].MsgSet - set.Messages = append(set.Messages, msgBlock) -} - -func (r *FetchResponse) AddRecord(topic string, partition int32, key, value Encoder, offset int64) { - frb := r.getOrCreateBlock(topic, partition) - kb, vb := encodeKV(key, value) - rec := &Record{Key: kb, Value: vb, OffsetDelta: offset} - if len(frb.RecordsSet) == 0 { - records := newDefaultRecords(&RecordBatch{Version: 2}) - frb.RecordsSet = []*Records{&records} - } - batch := frb.RecordsSet[0].RecordBatch - batch.addRecord(rec) -} - -func (r *FetchResponse) SetLastOffsetDelta(topic string, partition int32, offset int32) { - frb := r.getOrCreateBlock(topic, partition) - if len(frb.RecordsSet) == 0 { - records := newDefaultRecords(&RecordBatch{Version: 2}) - frb.RecordsSet = []*Records{&records} - } - batch := frb.RecordsSet[0].RecordBatch - batch.LastOffsetDelta = offset -} - -func (r *FetchResponse) SetLastStableOffset(topic string, partition int32, offset int64) { - frb := r.getOrCreateBlock(topic, partition) - frb.LastStableOffset = offset -} diff --git a/vendor/github.com/Shopify/sarama/find_coordinator_request.go b/vendor/github.com/Shopify/sarama/find_coordinator_request.go deleted file mode 100644 index 0ab5cb5ff57..00000000000 --- a/vendor/github.com/Shopify/sarama/find_coordinator_request.go +++ /dev/null @@ -1,61 +0,0 @@ -package sarama - -type CoordinatorType int8 - -const ( - CoordinatorGroup CoordinatorType = 0 - CoordinatorTransaction CoordinatorType = 1 -) - -type FindCoordinatorRequest struct { - Version int16 - CoordinatorKey string - CoordinatorType CoordinatorType -} - -func (f *FindCoordinatorRequest) encode(pe packetEncoder) error { - if err := pe.putString(f.CoordinatorKey); err != nil { - return err - } - - if f.Version >= 1 { - pe.putInt8(int8(f.CoordinatorType)) - } - - return nil -} - -func (f *FindCoordinatorRequest) decode(pd packetDecoder, version int16) (err error) { - if f.CoordinatorKey, err = pd.getString(); err != nil { - return err - } - - if version >= 1 { - f.Version = version - coordinatorType, err := pd.getInt8() - if err != nil { - return err - } - - f.CoordinatorType = CoordinatorType(coordinatorType) - } - - return nil -} - -func (f *FindCoordinatorRequest) key() int16 { - return 10 -} - -func (f *FindCoordinatorRequest) version() int16 { - return f.Version -} - -func (f *FindCoordinatorRequest) requiredVersion() KafkaVersion { - switch f.Version { - case 1: - return V0_11_0_0 - default: - return V0_8_2_0 - } -} diff --git a/vendor/github.com/Shopify/sarama/find_coordinator_response.go b/vendor/github.com/Shopify/sarama/find_coordinator_response.go deleted file mode 100644 index 9c900e8b774..00000000000 --- a/vendor/github.com/Shopify/sarama/find_coordinator_response.go +++ /dev/null @@ -1,92 +0,0 @@ -package sarama - -import ( - "time" -) - -var NoNode = &Broker{id: -1, addr: ":-1"} - -type FindCoordinatorResponse struct { - Version int16 - ThrottleTime time.Duration - Err KError - ErrMsg *string - Coordinator *Broker -} - -func (f *FindCoordinatorResponse) decode(pd packetDecoder, version int16) (err error) { - if version >= 1 { - f.Version = version - - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - f.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - } - - tmp, err := pd.getInt16() - if err != nil { - return err - } - f.Err = KError(tmp) - - if version >= 1 { - if f.ErrMsg, err = pd.getNullableString(); err != nil { - return err - } - } - - coordinator := new(Broker) - // The version is hardcoded to 0, as version 1 of the Broker-decode - // contains the rack-field which is not present in the FindCoordinatorResponse. - if err := coordinator.decode(pd, 0); err != nil { - return err - } - if coordinator.addr == ":0" { - return nil - } - f.Coordinator = coordinator - - return nil -} - -func (f *FindCoordinatorResponse) encode(pe packetEncoder) error { - if f.Version >= 1 { - pe.putInt32(int32(f.ThrottleTime / time.Millisecond)) - } - - pe.putInt16(int16(f.Err)) - - if f.Version >= 1 { - if err := pe.putNullableString(f.ErrMsg); err != nil { - return err - } - } - - coordinator := f.Coordinator - if coordinator == nil { - coordinator = NoNode - } - if err := coordinator.encode(pe, 0); err != nil { - return err - } - return nil -} - -func (f *FindCoordinatorResponse) key() int16 { - return 10 -} - -func (f *FindCoordinatorResponse) version() int16 { - return f.Version -} - -func (f *FindCoordinatorResponse) requiredVersion() KafkaVersion { - switch f.Version { - case 1: - return V0_11_0_0 - default: - return V0_8_2_0 - } -} diff --git a/vendor/github.com/Shopify/sarama/heartbeat_request.go b/vendor/github.com/Shopify/sarama/heartbeat_request.go deleted file mode 100644 index ce49c473972..00000000000 --- a/vendor/github.com/Shopify/sarama/heartbeat_request.go +++ /dev/null @@ -1,47 +0,0 @@ -package sarama - -type HeartbeatRequest struct { - GroupId string - GenerationId int32 - MemberId string -} - -func (r *HeartbeatRequest) encode(pe packetEncoder) error { - if err := pe.putString(r.GroupId); err != nil { - return err - } - - pe.putInt32(r.GenerationId) - - if err := pe.putString(r.MemberId); err != nil { - return err - } - - return nil -} - -func (r *HeartbeatRequest) decode(pd packetDecoder, version int16) (err error) { - if r.GroupId, err = pd.getString(); err != nil { - return - } - if r.GenerationId, err = pd.getInt32(); err != nil { - return - } - if r.MemberId, err = pd.getString(); err != nil { - return - } - - return nil -} - -func (r *HeartbeatRequest) key() int16 { - return 12 -} - -func (r *HeartbeatRequest) version() int16 { - return 0 -} - -func (r *HeartbeatRequest) requiredVersion() KafkaVersion { - return V0_9_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/heartbeat_response.go b/vendor/github.com/Shopify/sarama/heartbeat_response.go deleted file mode 100644 index 766f5fdec6f..00000000000 --- a/vendor/github.com/Shopify/sarama/heartbeat_response.go +++ /dev/null @@ -1,32 +0,0 @@ -package sarama - -type HeartbeatResponse struct { - Err KError -} - -func (r *HeartbeatResponse) encode(pe packetEncoder) error { - pe.putInt16(int16(r.Err)) - return nil -} - -func (r *HeartbeatResponse) decode(pd packetDecoder, version int16) error { - kerr, err := pd.getInt16() - if err != nil { - return err - } - r.Err = KError(kerr) - - return nil -} - -func (r *HeartbeatResponse) key() int16 { - return 12 -} - -func (r *HeartbeatResponse) version() int16 { - return 0 -} - -func (r *HeartbeatResponse) requiredVersion() KafkaVersion { - return V0_9_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/init_producer_id_request.go b/vendor/github.com/Shopify/sarama/init_producer_id_request.go deleted file mode 100644 index 8ceb6c23255..00000000000 --- a/vendor/github.com/Shopify/sarama/init_producer_id_request.go +++ /dev/null @@ -1,43 +0,0 @@ -package sarama - -import "time" - -type InitProducerIDRequest struct { - TransactionalID *string - TransactionTimeout time.Duration -} - -func (i *InitProducerIDRequest) encode(pe packetEncoder) error { - if err := pe.putNullableString(i.TransactionalID); err != nil { - return err - } - pe.putInt32(int32(i.TransactionTimeout / time.Millisecond)) - - return nil -} - -func (i *InitProducerIDRequest) decode(pd packetDecoder, version int16) (err error) { - if i.TransactionalID, err = pd.getNullableString(); err != nil { - return err - } - - timeout, err := pd.getInt32() - if err != nil { - return err - } - i.TransactionTimeout = time.Duration(timeout) * time.Millisecond - - return nil -} - -func (i *InitProducerIDRequest) key() int16 { - return 22 -} - -func (i *InitProducerIDRequest) version() int16 { - return 0 -} - -func (i *InitProducerIDRequest) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/init_producer_id_response.go b/vendor/github.com/Shopify/sarama/init_producer_id_response.go deleted file mode 100644 index 1b32eb085b2..00000000000 --- a/vendor/github.com/Shopify/sarama/init_producer_id_response.go +++ /dev/null @@ -1,55 +0,0 @@ -package sarama - -import "time" - -type InitProducerIDResponse struct { - ThrottleTime time.Duration - Err KError - ProducerID int64 - ProducerEpoch int16 -} - -func (i *InitProducerIDResponse) encode(pe packetEncoder) error { - pe.putInt32(int32(i.ThrottleTime / time.Millisecond)) - pe.putInt16(int16(i.Err)) - pe.putInt64(i.ProducerID) - pe.putInt16(i.ProducerEpoch) - - return nil -} - -func (i *InitProducerIDResponse) decode(pd packetDecoder, version int16) (err error) { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - i.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - kerr, err := pd.getInt16() - if err != nil { - return err - } - i.Err = KError(kerr) - - if i.ProducerID, err = pd.getInt64(); err != nil { - return err - } - - if i.ProducerEpoch, err = pd.getInt16(); err != nil { - return err - } - - return nil -} - -func (i *InitProducerIDResponse) key() int16 { - return 22 -} - -func (i *InitProducerIDResponse) version() int16 { - return 0 -} - -func (i *InitProducerIDResponse) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/join_group_request.go b/vendor/github.com/Shopify/sarama/join_group_request.go deleted file mode 100644 index 97e9299ea1a..00000000000 --- a/vendor/github.com/Shopify/sarama/join_group_request.go +++ /dev/null @@ -1,163 +0,0 @@ -package sarama - -type GroupProtocol struct { - Name string - Metadata []byte -} - -func (p *GroupProtocol) decode(pd packetDecoder) (err error) { - p.Name, err = pd.getString() - if err != nil { - return err - } - p.Metadata, err = pd.getBytes() - return err -} - -func (p *GroupProtocol) encode(pe packetEncoder) (err error) { - if err := pe.putString(p.Name); err != nil { - return err - } - if err := pe.putBytes(p.Metadata); err != nil { - return err - } - return nil -} - -type JoinGroupRequest struct { - Version int16 - GroupId string - SessionTimeout int32 - RebalanceTimeout int32 - MemberId string - ProtocolType string - GroupProtocols map[string][]byte // deprecated; use OrderedGroupProtocols - OrderedGroupProtocols []*GroupProtocol -} - -func (r *JoinGroupRequest) encode(pe packetEncoder) error { - if err := pe.putString(r.GroupId); err != nil { - return err - } - pe.putInt32(r.SessionTimeout) - if r.Version >= 1 { - pe.putInt32(r.RebalanceTimeout) - } - if err := pe.putString(r.MemberId); err != nil { - return err - } - if err := pe.putString(r.ProtocolType); err != nil { - return err - } - - if len(r.GroupProtocols) > 0 { - if len(r.OrderedGroupProtocols) > 0 { - return PacketDecodingError{"cannot specify both GroupProtocols and OrderedGroupProtocols on JoinGroupRequest"} - } - - if err := pe.putArrayLength(len(r.GroupProtocols)); err != nil { - return err - } - for name, metadata := range r.GroupProtocols { - if err := pe.putString(name); err != nil { - return err - } - if err := pe.putBytes(metadata); err != nil { - return err - } - } - } else { - if err := pe.putArrayLength(len(r.OrderedGroupProtocols)); err != nil { - return err - } - for _, protocol := range r.OrderedGroupProtocols { - if err := protocol.encode(pe); err != nil { - return err - } - } - } - - return nil -} - -func (r *JoinGroupRequest) decode(pd packetDecoder, version int16) (err error) { - r.Version = version - - if r.GroupId, err = pd.getString(); err != nil { - return - } - - if r.SessionTimeout, err = pd.getInt32(); err != nil { - return - } - - if version >= 1 { - if r.RebalanceTimeout, err = pd.getInt32(); err != nil { - return err - } - } - - if r.MemberId, err = pd.getString(); err != nil { - return - } - - if r.ProtocolType, err = pd.getString(); err != nil { - return - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - if n == 0 { - return nil - } - - r.GroupProtocols = make(map[string][]byte) - for i := 0; i < n; i++ { - protocol := &GroupProtocol{} - if err := protocol.decode(pd); err != nil { - return err - } - r.GroupProtocols[protocol.Name] = protocol.Metadata - r.OrderedGroupProtocols = append(r.OrderedGroupProtocols, protocol) - } - - return nil -} - -func (r *JoinGroupRequest) key() int16 { - return 11 -} - -func (r *JoinGroupRequest) version() int16 { - return r.Version -} - -func (r *JoinGroupRequest) requiredVersion() KafkaVersion { - switch r.Version { - case 2: - return V0_11_0_0 - case 1: - return V0_10_1_0 - default: - return V0_9_0_0 - } -} - -func (r *JoinGroupRequest) AddGroupProtocol(name string, metadata []byte) { - r.OrderedGroupProtocols = append(r.OrderedGroupProtocols, &GroupProtocol{ - Name: name, - Metadata: metadata, - }) -} - -func (r *JoinGroupRequest) AddGroupProtocolMetadata(name string, metadata *ConsumerGroupMemberMetadata) error { - bin, err := encode(metadata, nil) - if err != nil { - return err - } - - r.AddGroupProtocol(name, bin) - return nil -} diff --git a/vendor/github.com/Shopify/sarama/join_group_response.go b/vendor/github.com/Shopify/sarama/join_group_response.go deleted file mode 100644 index 5752acc8aeb..00000000000 --- a/vendor/github.com/Shopify/sarama/join_group_response.go +++ /dev/null @@ -1,135 +0,0 @@ -package sarama - -type JoinGroupResponse struct { - Version int16 - ThrottleTime int32 - Err KError - GenerationId int32 - GroupProtocol string - LeaderId string - MemberId string - Members map[string][]byte -} - -func (r *JoinGroupResponse) GetMembers() (map[string]ConsumerGroupMemberMetadata, error) { - members := make(map[string]ConsumerGroupMemberMetadata, len(r.Members)) - for id, bin := range r.Members { - meta := new(ConsumerGroupMemberMetadata) - if err := decode(bin, meta); err != nil { - return nil, err - } - members[id] = *meta - } - return members, nil -} - -func (r *JoinGroupResponse) encode(pe packetEncoder) error { - if r.Version >= 2 { - pe.putInt32(r.ThrottleTime) - } - pe.putInt16(int16(r.Err)) - pe.putInt32(r.GenerationId) - - if err := pe.putString(r.GroupProtocol); err != nil { - return err - } - if err := pe.putString(r.LeaderId); err != nil { - return err - } - if err := pe.putString(r.MemberId); err != nil { - return err - } - - if err := pe.putArrayLength(len(r.Members)); err != nil { - return err - } - - for memberId, memberMetadata := range r.Members { - if err := pe.putString(memberId); err != nil { - return err - } - - if err := pe.putBytes(memberMetadata); err != nil { - return err - } - } - - return nil -} - -func (r *JoinGroupResponse) decode(pd packetDecoder, version int16) (err error) { - r.Version = version - - if version >= 2 { - if r.ThrottleTime, err = pd.getInt32(); err != nil { - return - } - } - - kerr, err := pd.getInt16() - if err != nil { - return err - } - - r.Err = KError(kerr) - - if r.GenerationId, err = pd.getInt32(); err != nil { - return - } - - if r.GroupProtocol, err = pd.getString(); err != nil { - return - } - - if r.LeaderId, err = pd.getString(); err != nil { - return - } - - if r.MemberId, err = pd.getString(); err != nil { - return - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - if n == 0 { - return nil - } - - r.Members = make(map[string][]byte) - for i := 0; i < n; i++ { - memberId, err := pd.getString() - if err != nil { - return err - } - - memberMetadata, err := pd.getBytes() - if err != nil { - return err - } - - r.Members[memberId] = memberMetadata - } - - return nil -} - -func (r *JoinGroupResponse) key() int16 { - return 11 -} - -func (r *JoinGroupResponse) version() int16 { - return r.Version -} - -func (r *JoinGroupResponse) requiredVersion() KafkaVersion { - switch r.Version { - case 2: - return V0_11_0_0 - case 1: - return V0_10_1_0 - default: - return V0_9_0_0 - } -} diff --git a/vendor/github.com/Shopify/sarama/leave_group_request.go b/vendor/github.com/Shopify/sarama/leave_group_request.go deleted file mode 100644 index e177427482f..00000000000 --- a/vendor/github.com/Shopify/sarama/leave_group_request.go +++ /dev/null @@ -1,40 +0,0 @@ -package sarama - -type LeaveGroupRequest struct { - GroupId string - MemberId string -} - -func (r *LeaveGroupRequest) encode(pe packetEncoder) error { - if err := pe.putString(r.GroupId); err != nil { - return err - } - if err := pe.putString(r.MemberId); err != nil { - return err - } - - return nil -} - -func (r *LeaveGroupRequest) decode(pd packetDecoder, version int16) (err error) { - if r.GroupId, err = pd.getString(); err != nil { - return - } - if r.MemberId, err = pd.getString(); err != nil { - return - } - - return nil -} - -func (r *LeaveGroupRequest) key() int16 { - return 13 -} - -func (r *LeaveGroupRequest) version() int16 { - return 0 -} - -func (r *LeaveGroupRequest) requiredVersion() KafkaVersion { - return V0_9_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/leave_group_response.go b/vendor/github.com/Shopify/sarama/leave_group_response.go deleted file mode 100644 index d60c626da01..00000000000 --- a/vendor/github.com/Shopify/sarama/leave_group_response.go +++ /dev/null @@ -1,32 +0,0 @@ -package sarama - -type LeaveGroupResponse struct { - Err KError -} - -func (r *LeaveGroupResponse) encode(pe packetEncoder) error { - pe.putInt16(int16(r.Err)) - return nil -} - -func (r *LeaveGroupResponse) decode(pd packetDecoder, version int16) (err error) { - kerr, err := pd.getInt16() - if err != nil { - return err - } - r.Err = KError(kerr) - - return nil -} - -func (r *LeaveGroupResponse) key() int16 { - return 13 -} - -func (r *LeaveGroupResponse) version() int16 { - return 0 -} - -func (r *LeaveGroupResponse) requiredVersion() KafkaVersion { - return V0_9_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/length_field.go b/vendor/github.com/Shopify/sarama/length_field.go deleted file mode 100644 index 576b1a6f6f8..00000000000 --- a/vendor/github.com/Shopify/sarama/length_field.go +++ /dev/null @@ -1,69 +0,0 @@ -package sarama - -import "encoding/binary" - -// LengthField implements the PushEncoder and PushDecoder interfaces for calculating 4-byte lengths. -type lengthField struct { - startOffset int -} - -func (l *lengthField) saveOffset(in int) { - l.startOffset = in -} - -func (l *lengthField) reserveLength() int { - return 4 -} - -func (l *lengthField) run(curOffset int, buf []byte) error { - binary.BigEndian.PutUint32(buf[l.startOffset:], uint32(curOffset-l.startOffset-4)) - return nil -} - -func (l *lengthField) check(curOffset int, buf []byte) error { - if uint32(curOffset-l.startOffset-4) != binary.BigEndian.Uint32(buf[l.startOffset:]) { - return PacketDecodingError{"length field invalid"} - } - - return nil -} - -type varintLengthField struct { - startOffset int - length int64 -} - -func (l *varintLengthField) decode(pd packetDecoder) error { - var err error - l.length, err = pd.getVarint() - return err -} - -func (l *varintLengthField) saveOffset(in int) { - l.startOffset = in -} - -func (l *varintLengthField) adjustLength(currOffset int) int { - oldFieldSize := l.reserveLength() - l.length = int64(currOffset - l.startOffset - oldFieldSize) - - return l.reserveLength() - oldFieldSize -} - -func (l *varintLengthField) reserveLength() int { - var tmp [binary.MaxVarintLen64]byte - return binary.PutVarint(tmp[:], l.length) -} - -func (l *varintLengthField) run(curOffset int, buf []byte) error { - binary.PutVarint(buf[l.startOffset:], l.length) - return nil -} - -func (l *varintLengthField) check(curOffset int, buf []byte) error { - if int64(curOffset-l.startOffset-l.reserveLength()) != l.length { - return PacketDecodingError{"length field invalid"} - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/list_groups_request.go b/vendor/github.com/Shopify/sarama/list_groups_request.go deleted file mode 100644 index 3b16abf7fa8..00000000000 --- a/vendor/github.com/Shopify/sarama/list_groups_request.go +++ /dev/null @@ -1,24 +0,0 @@ -package sarama - -type ListGroupsRequest struct { -} - -func (r *ListGroupsRequest) encode(pe packetEncoder) error { - return nil -} - -func (r *ListGroupsRequest) decode(pd packetDecoder, version int16) (err error) { - return nil -} - -func (r *ListGroupsRequest) key() int16 { - return 16 -} - -func (r *ListGroupsRequest) version() int16 { - return 0 -} - -func (r *ListGroupsRequest) requiredVersion() KafkaVersion { - return V0_9_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/list_groups_response.go b/vendor/github.com/Shopify/sarama/list_groups_response.go deleted file mode 100644 index 56115d4c75a..00000000000 --- a/vendor/github.com/Shopify/sarama/list_groups_response.go +++ /dev/null @@ -1,69 +0,0 @@ -package sarama - -type ListGroupsResponse struct { - Err KError - Groups map[string]string -} - -func (r *ListGroupsResponse) encode(pe packetEncoder) error { - pe.putInt16(int16(r.Err)) - - if err := pe.putArrayLength(len(r.Groups)); err != nil { - return err - } - for groupId, protocolType := range r.Groups { - if err := pe.putString(groupId); err != nil { - return err - } - if err := pe.putString(protocolType); err != nil { - return err - } - } - - return nil -} - -func (r *ListGroupsResponse) decode(pd packetDecoder, version int16) error { - kerr, err := pd.getInt16() - if err != nil { - return err - } - - r.Err = KError(kerr) - - n, err := pd.getArrayLength() - if err != nil { - return err - } - if n == 0 { - return nil - } - - r.Groups = make(map[string]string) - for i := 0; i < n; i++ { - groupId, err := pd.getString() - if err != nil { - return err - } - protocolType, err := pd.getString() - if err != nil { - return err - } - - r.Groups[groupId] = protocolType - } - - return nil -} - -func (r *ListGroupsResponse) key() int16 { - return 16 -} - -func (r *ListGroupsResponse) version() int16 { - return 0 -} - -func (r *ListGroupsResponse) requiredVersion() KafkaVersion { - return V0_9_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/message.go b/vendor/github.com/Shopify/sarama/message.go deleted file mode 100644 index fecdbfdef75..00000000000 --- a/vendor/github.com/Shopify/sarama/message.go +++ /dev/null @@ -1,223 +0,0 @@ -package sarama - -import ( - "bytes" - "compress/gzip" - "fmt" - "io/ioutil" - "time" - - "github.com/eapache/go-xerial-snappy" - "github.com/pierrec/lz4" -) - -// CompressionCodec represents the various compression codecs recognized by Kafka in messages. -type CompressionCodec int8 - -// only the last two bits are really used -const compressionCodecMask int8 = 0x03 - -const ( - CompressionNone CompressionCodec = 0 - CompressionGZIP CompressionCodec = 1 - CompressionSnappy CompressionCodec = 2 - CompressionLZ4 CompressionCodec = 3 -) - -func (cc CompressionCodec) String() string { - return []string{ - "none", - "gzip", - "snappy", - "lz4", - }[int(cc)] -} - -// CompressionLevelDefault is the constant to use in CompressionLevel -// to have the default compression level for any codec. The value is picked -// that we don't use any existing compression levels. -const CompressionLevelDefault = -1000 - -type Message struct { - Codec CompressionCodec // codec used to compress the message contents - CompressionLevel int // compression level - Key []byte // the message key, may be nil - Value []byte // the message contents - Set *MessageSet // the message set a message might wrap - Version int8 // v1 requires Kafka 0.10 - Timestamp time.Time // the timestamp of the message (version 1+ only) - - compressedCache []byte - compressedSize int // used for computing the compression ratio metrics -} - -func (m *Message) encode(pe packetEncoder) error { - pe.push(newCRC32Field(crcIEEE)) - - pe.putInt8(m.Version) - - attributes := int8(m.Codec) & compressionCodecMask - pe.putInt8(attributes) - - if m.Version >= 1 { - if err := (Timestamp{&m.Timestamp}).encode(pe); err != nil { - return err - } - } - - err := pe.putBytes(m.Key) - if err != nil { - return err - } - - var payload []byte - - if m.compressedCache != nil { - payload = m.compressedCache - m.compressedCache = nil - } else if m.Value != nil { - switch m.Codec { - case CompressionNone: - payload = m.Value - case CompressionGZIP: - var buf bytes.Buffer - var writer *gzip.Writer - if m.CompressionLevel != CompressionLevelDefault { - writer, err = gzip.NewWriterLevel(&buf, m.CompressionLevel) - if err != nil { - return err - } - } else { - writer = gzip.NewWriter(&buf) - } - if _, err = writer.Write(m.Value); err != nil { - return err - } - if err = writer.Close(); err != nil { - return err - } - m.compressedCache = buf.Bytes() - payload = m.compressedCache - case CompressionSnappy: - tmp := snappy.Encode(m.Value) - m.compressedCache = tmp - payload = m.compressedCache - case CompressionLZ4: - var buf bytes.Buffer - writer := lz4.NewWriter(&buf) - if _, err = writer.Write(m.Value); err != nil { - return err - } - if err = writer.Close(); err != nil { - return err - } - m.compressedCache = buf.Bytes() - payload = m.compressedCache - - default: - return PacketEncodingError{fmt.Sprintf("unsupported compression codec (%d)", m.Codec)} - } - // Keep in mind the compressed payload size for metric gathering - m.compressedSize = len(payload) - } - - if err = pe.putBytes(payload); err != nil { - return err - } - - return pe.pop() -} - -func (m *Message) decode(pd packetDecoder) (err error) { - err = pd.push(newCRC32Field(crcIEEE)) - if err != nil { - return err - } - - m.Version, err = pd.getInt8() - if err != nil { - return err - } - - if m.Version > 1 { - return PacketDecodingError{fmt.Sprintf("unknown magic byte (%v)", m.Version)} - } - - attribute, err := pd.getInt8() - if err != nil { - return err - } - m.Codec = CompressionCodec(attribute & compressionCodecMask) - - if m.Version == 1 { - if err := (Timestamp{&m.Timestamp}).decode(pd); err != nil { - return err - } - } - - m.Key, err = pd.getBytes() - if err != nil { - return err - } - - m.Value, err = pd.getBytes() - if err != nil { - return err - } - - // Required for deep equal assertion during tests but might be useful - // for future metrics about the compression ratio in fetch requests - m.compressedSize = len(m.Value) - - switch m.Codec { - case CompressionNone: - // nothing to do - case CompressionGZIP: - if m.Value == nil { - break - } - reader, err := gzip.NewReader(bytes.NewReader(m.Value)) - if err != nil { - return err - } - if m.Value, err = ioutil.ReadAll(reader); err != nil { - return err - } - if err := m.decodeSet(); err != nil { - return err - } - case CompressionSnappy: - if m.Value == nil { - break - } - if m.Value, err = snappy.Decode(m.Value); err != nil { - return err - } - if err := m.decodeSet(); err != nil { - return err - } - case CompressionLZ4: - if m.Value == nil { - break - } - reader := lz4.NewReader(bytes.NewReader(m.Value)) - if m.Value, err = ioutil.ReadAll(reader); err != nil { - return err - } - if err := m.decodeSet(); err != nil { - return err - } - - default: - return PacketDecodingError{fmt.Sprintf("invalid compression specified (%d)", m.Codec)} - } - - return pd.pop() -} - -// decodes a message set from a previousy encoded bulk-message -func (m *Message) decodeSet() (err error) { - pd := realDecoder{raw: m.Value} - m.Set = &MessageSet{} - return m.Set.decode(&pd) -} diff --git a/vendor/github.com/Shopify/sarama/message_set.go b/vendor/github.com/Shopify/sarama/message_set.go deleted file mode 100644 index 27db52fdf1f..00000000000 --- a/vendor/github.com/Shopify/sarama/message_set.go +++ /dev/null @@ -1,102 +0,0 @@ -package sarama - -type MessageBlock struct { - Offset int64 - Msg *Message -} - -// Messages convenience helper which returns either all the -// messages that are wrapped in this block -func (msb *MessageBlock) Messages() []*MessageBlock { - if msb.Msg.Set != nil { - return msb.Msg.Set.Messages - } - return []*MessageBlock{msb} -} - -func (msb *MessageBlock) encode(pe packetEncoder) error { - pe.putInt64(msb.Offset) - pe.push(&lengthField{}) - err := msb.Msg.encode(pe) - if err != nil { - return err - } - return pe.pop() -} - -func (msb *MessageBlock) decode(pd packetDecoder) (err error) { - if msb.Offset, err = pd.getInt64(); err != nil { - return err - } - - if err = pd.push(&lengthField{}); err != nil { - return err - } - - msb.Msg = new(Message) - if err = msb.Msg.decode(pd); err != nil { - return err - } - - if err = pd.pop(); err != nil { - return err - } - - return nil -} - -type MessageSet struct { - PartialTrailingMessage bool // whether the set on the wire contained an incomplete trailing MessageBlock - Messages []*MessageBlock -} - -func (ms *MessageSet) encode(pe packetEncoder) error { - for i := range ms.Messages { - err := ms.Messages[i].encode(pe) - if err != nil { - return err - } - } - return nil -} - -func (ms *MessageSet) decode(pd packetDecoder) (err error) { - ms.Messages = nil - - for pd.remaining() > 0 { - magic, err := magicValue(pd) - if err != nil { - if err == ErrInsufficientData { - ms.PartialTrailingMessage = true - return nil - } - return err - } - - if magic > 1 { - return nil - } - - msb := new(MessageBlock) - err = msb.decode(pd) - switch err { - case nil: - ms.Messages = append(ms.Messages, msb) - case ErrInsufficientData: - // As an optimization the server is allowed to return a partial message at the - // end of the message set. Clients should handle this case. So we just ignore such things. - ms.PartialTrailingMessage = true - return nil - default: - return err - } - } - - return nil -} - -func (ms *MessageSet) addMessage(msg *Message) { - block := new(MessageBlock) - block.Msg = msg - ms.Messages = append(ms.Messages, block) -} diff --git a/vendor/github.com/Shopify/sarama/metadata_request.go b/vendor/github.com/Shopify/sarama/metadata_request.go deleted file mode 100644 index 48adfa28cb9..00000000000 --- a/vendor/github.com/Shopify/sarama/metadata_request.go +++ /dev/null @@ -1,88 +0,0 @@ -package sarama - -type MetadataRequest struct { - Version int16 - Topics []string - AllowAutoTopicCreation bool -} - -func (r *MetadataRequest) encode(pe packetEncoder) error { - if r.Version < 0 || r.Version > 5 { - return PacketEncodingError{"invalid or unsupported MetadataRequest version field"} - } - if r.Version == 0 || r.Topics != nil || len(r.Topics) > 0 { - err := pe.putArrayLength(len(r.Topics)) - if err != nil { - return err - } - - for i := range r.Topics { - err = pe.putString(r.Topics[i]) - if err != nil { - return err - } - } - } else { - pe.putInt32(-1) - } - if r.Version > 3 { - pe.putBool(r.AllowAutoTopicCreation) - } - return nil -} - -func (r *MetadataRequest) decode(pd packetDecoder, version int16) error { - r.Version = version - size, err := pd.getInt32() - if err != nil { - return err - } - if size < 0 { - return nil - } else { - topicCount := size - if topicCount == 0 { - return nil - } - - r.Topics = make([]string, topicCount) - for i := range r.Topics { - topic, err := pd.getString() - if err != nil { - return err - } - r.Topics[i] = topic - } - } - if r.Version > 3 { - autoCreation, err := pd.getBool() - if err != nil { - return err - } - r.AllowAutoTopicCreation = autoCreation - } - return nil -} - -func (r *MetadataRequest) key() int16 { - return 3 -} - -func (r *MetadataRequest) version() int16 { - return r.Version -} - -func (r *MetadataRequest) requiredVersion() KafkaVersion { - switch r.Version { - case 1: - return V0_10_0_0 - case 2: - return V0_10_1_0 - case 3, 4: - return V0_11_0_0 - case 5: - return V1_0_0_0 - default: - return MinVersion - } -} diff --git a/vendor/github.com/Shopify/sarama/metadata_response.go b/vendor/github.com/Shopify/sarama/metadata_response.go deleted file mode 100644 index bf8a67bbc52..00000000000 --- a/vendor/github.com/Shopify/sarama/metadata_response.go +++ /dev/null @@ -1,310 +0,0 @@ -package sarama - -type PartitionMetadata struct { - Err KError - ID int32 - Leader int32 - Replicas []int32 - Isr []int32 - OfflineReplicas []int32 -} - -func (pm *PartitionMetadata) decode(pd packetDecoder, version int16) (err error) { - tmp, err := pd.getInt16() - if err != nil { - return err - } - pm.Err = KError(tmp) - - pm.ID, err = pd.getInt32() - if err != nil { - return err - } - - pm.Leader, err = pd.getInt32() - if err != nil { - return err - } - - pm.Replicas, err = pd.getInt32Array() - if err != nil { - return err - } - - pm.Isr, err = pd.getInt32Array() - if err != nil { - return err - } - - if version >= 5 { - pm.OfflineReplicas, err = pd.getInt32Array() - if err != nil { - return err - } - } - - return nil -} - -func (pm *PartitionMetadata) encode(pe packetEncoder, version int16) (err error) { - pe.putInt16(int16(pm.Err)) - pe.putInt32(pm.ID) - pe.putInt32(pm.Leader) - - err = pe.putInt32Array(pm.Replicas) - if err != nil { - return err - } - - err = pe.putInt32Array(pm.Isr) - if err != nil { - return err - } - - if version >= 5 { - err = pe.putInt32Array(pm.OfflineReplicas) - if err != nil { - return err - } - } - - return nil -} - -type TopicMetadata struct { - Err KError - Name string - IsInternal bool // Only valid for Version >= 1 - Partitions []*PartitionMetadata -} - -func (tm *TopicMetadata) decode(pd packetDecoder, version int16) (err error) { - tmp, err := pd.getInt16() - if err != nil { - return err - } - tm.Err = KError(tmp) - - tm.Name, err = pd.getString() - if err != nil { - return err - } - - if version >= 1 { - tm.IsInternal, err = pd.getBool() - if err != nil { - return err - } - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - tm.Partitions = make([]*PartitionMetadata, n) - for i := 0; i < n; i++ { - tm.Partitions[i] = new(PartitionMetadata) - err = tm.Partitions[i].decode(pd, version) - if err != nil { - return err - } - } - - return nil -} - -func (tm *TopicMetadata) encode(pe packetEncoder, version int16) (err error) { - pe.putInt16(int16(tm.Err)) - - err = pe.putString(tm.Name) - if err != nil { - return err - } - - if version >= 1 { - pe.putBool(tm.IsInternal) - } - - err = pe.putArrayLength(len(tm.Partitions)) - if err != nil { - return err - } - - for _, pm := range tm.Partitions { - err = pm.encode(pe, version) - if err != nil { - return err - } - } - - return nil -} - -type MetadataResponse struct { - Version int16 - ThrottleTimeMs int32 - Brokers []*Broker - ClusterID *string - ControllerID int32 - Topics []*TopicMetadata -} - -func (r *MetadataResponse) decode(pd packetDecoder, version int16) (err error) { - r.Version = version - - if version >= 3 { - r.ThrottleTimeMs, err = pd.getInt32() - if err != nil { - return err - } - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - r.Brokers = make([]*Broker, n) - for i := 0; i < n; i++ { - r.Brokers[i] = new(Broker) - err = r.Brokers[i].decode(pd, version) - if err != nil { - return err - } - } - - if version >= 2 { - r.ClusterID, err = pd.getNullableString() - if err != nil { - return err - } - } - - if version >= 1 { - r.ControllerID, err = pd.getInt32() - if err != nil { - return err - } - } else { - r.ControllerID = -1 - } - - n, err = pd.getArrayLength() - if err != nil { - return err - } - - r.Topics = make([]*TopicMetadata, n) - for i := 0; i < n; i++ { - r.Topics[i] = new(TopicMetadata) - err = r.Topics[i].decode(pd, version) - if err != nil { - return err - } - } - - return nil -} - -func (r *MetadataResponse) encode(pe packetEncoder) error { - err := pe.putArrayLength(len(r.Brokers)) - if err != nil { - return err - } - for _, broker := range r.Brokers { - err = broker.encode(pe, r.Version) - if err != nil { - return err - } - } - - if r.Version >= 1 { - pe.putInt32(r.ControllerID) - } - - err = pe.putArrayLength(len(r.Topics)) - if err != nil { - return err - } - for _, tm := range r.Topics { - err = tm.encode(pe, r.Version) - if err != nil { - return err - } - } - - return nil -} - -func (r *MetadataResponse) key() int16 { - return 3 -} - -func (r *MetadataResponse) version() int16 { - return r.Version -} - -func (r *MetadataResponse) requiredVersion() KafkaVersion { - switch r.Version { - case 1: - return V0_10_0_0 - case 2: - return V0_10_1_0 - case 3, 4: - return V0_11_0_0 - case 5: - return V1_0_0_0 - default: - return MinVersion - } -} - -// testing API - -func (r *MetadataResponse) AddBroker(addr string, id int32) { - r.Brokers = append(r.Brokers, &Broker{id: id, addr: addr}) -} - -func (r *MetadataResponse) AddTopic(topic string, err KError) *TopicMetadata { - var tmatch *TopicMetadata - - for _, tm := range r.Topics { - if tm.Name == topic { - tmatch = tm - goto foundTopic - } - } - - tmatch = new(TopicMetadata) - tmatch.Name = topic - r.Topics = append(r.Topics, tmatch) - -foundTopic: - - tmatch.Err = err - return tmatch -} - -func (r *MetadataResponse) AddTopicPartition(topic string, partition, brokerID int32, replicas, isr []int32, err KError) { - tmatch := r.AddTopic(topic, ErrNoError) - var pmatch *PartitionMetadata - - for _, pm := range tmatch.Partitions { - if pm.ID == partition { - pmatch = pm - goto foundPartition - } - } - - pmatch = new(PartitionMetadata) - pmatch.ID = partition - tmatch.Partitions = append(tmatch.Partitions, pmatch) - -foundPartition: - - pmatch.Leader = brokerID - pmatch.Replicas = replicas - pmatch.Isr = isr - pmatch.Err = err - -} diff --git a/vendor/github.com/Shopify/sarama/metrics.go b/vendor/github.com/Shopify/sarama/metrics.go deleted file mode 100644 index 4869708e944..00000000000 --- a/vendor/github.com/Shopify/sarama/metrics.go +++ /dev/null @@ -1,51 +0,0 @@ -package sarama - -import ( - "fmt" - "strings" - - "github.com/rcrowley/go-metrics" -) - -// Use exponentially decaying reservoir for sampling histograms with the same defaults as the Java library: -// 1028 elements, which offers a 99.9% confidence level with a 5% margin of error assuming a normal distribution, -// and an alpha factor of 0.015, which heavily biases the reservoir to the past 5 minutes of measurements. -// See https://github.com/dropwizard/metrics/blob/v3.1.0/metrics-core/src/main/java/com/codahale/metrics/ExponentiallyDecayingReservoir.java#L38 -const ( - metricsReservoirSize = 1028 - metricsAlphaFactor = 0.015 -) - -func getOrRegisterHistogram(name string, r metrics.Registry) metrics.Histogram { - return r.GetOrRegister(name, func() metrics.Histogram { - return metrics.NewHistogram(metrics.NewExpDecaySample(metricsReservoirSize, metricsAlphaFactor)) - }).(metrics.Histogram) -} - -func getMetricNameForBroker(name string, broker *Broker) string { - // Use broker id like the Java client as it does not contain '.' or ':' characters that - // can be interpreted as special character by monitoring tool (e.g. Graphite) - return fmt.Sprintf(name+"-for-broker-%d", broker.ID()) -} - -func getOrRegisterBrokerMeter(name string, broker *Broker, r metrics.Registry) metrics.Meter { - return metrics.GetOrRegisterMeter(getMetricNameForBroker(name, broker), r) -} - -func getOrRegisterBrokerHistogram(name string, broker *Broker, r metrics.Registry) metrics.Histogram { - return getOrRegisterHistogram(getMetricNameForBroker(name, broker), r) -} - -func getMetricNameForTopic(name string, topic string) string { - // Convert dot to _ since reporters like Graphite typically use dot to represent hierarchy - // cf. KAFKA-1902 and KAFKA-2337 - return fmt.Sprintf(name+"-for-topic-%s", strings.Replace(topic, ".", "_", -1)) -} - -func getOrRegisterTopicMeter(name string, topic string, r metrics.Registry) metrics.Meter { - return metrics.GetOrRegisterMeter(getMetricNameForTopic(name, topic), r) -} - -func getOrRegisterTopicHistogram(name string, topic string, r metrics.Registry) metrics.Histogram { - return getOrRegisterHistogram(getMetricNameForTopic(name, topic), r) -} diff --git a/vendor/github.com/Shopify/sarama/mockbroker.go b/vendor/github.com/Shopify/sarama/mockbroker.go deleted file mode 100644 index 55ef1e2920f..00000000000 --- a/vendor/github.com/Shopify/sarama/mockbroker.go +++ /dev/null @@ -1,330 +0,0 @@ -package sarama - -import ( - "bytes" - "encoding/binary" - "fmt" - "io" - "net" - "reflect" - "strconv" - "sync" - "time" - - "github.com/davecgh/go-spew/spew" -) - -const ( - expectationTimeout = 500 * time.Millisecond -) - -type requestHandlerFunc func(req *request) (res encoder) - -// RequestNotifierFunc is invoked when a mock broker processes a request successfully -// and will provides the number of bytes read and written. -type RequestNotifierFunc func(bytesRead, bytesWritten int) - -// MockBroker is a mock Kafka broker that is used in unit tests. It is exposed -// to facilitate testing of higher level or specialized consumers and producers -// built on top of Sarama. Note that it does not 'mimic' the Kafka API protocol, -// but rather provides a facility to do that. It takes care of the TCP -// transport, request unmarshaling, response marshaling, and makes it the test -// writer responsibility to program correct according to the Kafka API protocol -// MockBroker behaviour. -// -// MockBroker is implemented as a TCP server listening on a kernel-selected -// localhost port that can accept many connections. It reads Kafka requests -// from that connection and returns responses programmed by the SetHandlerByMap -// function. If a MockBroker receives a request that it has no programmed -// response for, then it returns nothing and the request times out. -// -// A set of MockRequest builders to define mappings used by MockBroker is -// provided by Sarama. But users can develop MockRequests of their own and use -// them along with or instead of the standard ones. -// -// When running tests with MockBroker it is strongly recommended to specify -// a timeout to `go test` so that if the broker hangs waiting for a response, -// the test panics. -// -// It is not necessary to prefix message length or correlation ID to your -// response bytes, the server does that automatically as a convenience. -type MockBroker struct { - brokerID int32 - port int32 - closing chan none - stopper chan none - expectations chan encoder - listener net.Listener - t TestReporter - latency time.Duration - handler requestHandlerFunc - notifier RequestNotifierFunc - history []RequestResponse - lock sync.Mutex -} - -// RequestResponse represents a Request/Response pair processed by MockBroker. -type RequestResponse struct { - Request protocolBody - Response encoder -} - -// SetLatency makes broker pause for the specified period every time before -// replying. -func (b *MockBroker) SetLatency(latency time.Duration) { - b.latency = latency -} - -// SetHandlerByMap defines mapping of Request types to MockResponses. When a -// request is received by the broker, it looks up the request type in the map -// and uses the found MockResponse instance to generate an appropriate reply. -// If the request type is not found in the map then nothing is sent. -func (b *MockBroker) SetHandlerByMap(handlerMap map[string]MockResponse) { - b.setHandler(func(req *request) (res encoder) { - reqTypeName := reflect.TypeOf(req.body).Elem().Name() - mockResponse := handlerMap[reqTypeName] - if mockResponse == nil { - return nil - } - return mockResponse.For(req.body) - }) -} - -// SetNotifier set a function that will get invoked whenever a request has been -// processed successfully and will provide the number of bytes read and written -func (b *MockBroker) SetNotifier(notifier RequestNotifierFunc) { - b.lock.Lock() - b.notifier = notifier - b.lock.Unlock() -} - -// BrokerID returns broker ID assigned to the broker. -func (b *MockBroker) BrokerID() int32 { - return b.brokerID -} - -// History returns a slice of RequestResponse pairs in the order they were -// processed by the broker. Note that in case of multiple connections to the -// broker the order expected by a test can be different from the order recorded -// in the history, unless some synchronization is implemented in the test. -func (b *MockBroker) History() []RequestResponse { - b.lock.Lock() - history := make([]RequestResponse, len(b.history)) - copy(history, b.history) - b.lock.Unlock() - return history -} - -// Port returns the TCP port number the broker is listening for requests on. -func (b *MockBroker) Port() int32 { - return b.port -} - -// Addr returns the broker connection string in the form "
:". -func (b *MockBroker) Addr() string { - return b.listener.Addr().String() -} - -// Close terminates the broker blocking until it stops internal goroutines and -// releases all resources. -func (b *MockBroker) Close() { - close(b.expectations) - if len(b.expectations) > 0 { - buf := bytes.NewBufferString(fmt.Sprintf("mockbroker/%d: not all expectations were satisfied! Still waiting on:\n", b.BrokerID())) - for e := range b.expectations { - _, _ = buf.WriteString(spew.Sdump(e)) - } - b.t.Error(buf.String()) - } - close(b.closing) - <-b.stopper -} - -// setHandler sets the specified function as the request handler. Whenever -// a mock broker reads a request from the wire it passes the request to the -// function and sends back whatever the handler function returns. -func (b *MockBroker) setHandler(handler requestHandlerFunc) { - b.lock.Lock() - b.handler = handler - b.lock.Unlock() -} - -func (b *MockBroker) serverLoop() { - defer close(b.stopper) - var err error - var conn net.Conn - - go func() { - <-b.closing - err := b.listener.Close() - if err != nil { - b.t.Error(err) - } - }() - - wg := &sync.WaitGroup{} - i := 0 - for conn, err = b.listener.Accept(); err == nil; conn, err = b.listener.Accept() { - wg.Add(1) - go b.handleRequests(conn, i, wg) - i++ - } - wg.Wait() - Logger.Printf("*** mockbroker/%d: listener closed, err=%v", b.BrokerID(), err) -} - -func (b *MockBroker) handleRequests(conn net.Conn, idx int, wg *sync.WaitGroup) { - defer wg.Done() - defer func() { - _ = conn.Close() - }() - Logger.Printf("*** mockbroker/%d/%d: connection opened", b.BrokerID(), idx) - var err error - - abort := make(chan none) - defer close(abort) - go func() { - select { - case <-b.closing: - _ = conn.Close() - case <-abort: - } - }() - - resHeader := make([]byte, 8) - for { - req, bytesRead, err := decodeRequest(conn) - if err != nil { - Logger.Printf("*** mockbroker/%d/%d: invalid request: err=%+v, %+v", b.brokerID, idx, err, spew.Sdump(req)) - b.serverError(err) - break - } - - if b.latency > 0 { - time.Sleep(b.latency) - } - - b.lock.Lock() - res := b.handler(req) - b.history = append(b.history, RequestResponse{req.body, res}) - b.lock.Unlock() - - if res == nil { - Logger.Printf("*** mockbroker/%d/%d: ignored %v", b.brokerID, idx, spew.Sdump(req)) - continue - } - Logger.Printf("*** mockbroker/%d/%d: served %v -> %v", b.brokerID, idx, req, res) - - encodedRes, err := encode(res, nil) - if err != nil { - b.serverError(err) - break - } - if len(encodedRes) == 0 { - b.lock.Lock() - if b.notifier != nil { - b.notifier(bytesRead, 0) - } - b.lock.Unlock() - continue - } - - binary.BigEndian.PutUint32(resHeader, uint32(len(encodedRes)+4)) - binary.BigEndian.PutUint32(resHeader[4:], uint32(req.correlationID)) - if _, err = conn.Write(resHeader); err != nil { - b.serverError(err) - break - } - if _, err = conn.Write(encodedRes); err != nil { - b.serverError(err) - break - } - - b.lock.Lock() - if b.notifier != nil { - b.notifier(bytesRead, len(resHeader)+len(encodedRes)) - } - b.lock.Unlock() - } - Logger.Printf("*** mockbroker/%d/%d: connection closed, err=%v", b.BrokerID(), idx, err) -} - -func (b *MockBroker) defaultRequestHandler(req *request) (res encoder) { - select { - case res, ok := <-b.expectations: - if !ok { - return nil - } - return res - case <-time.After(expectationTimeout): - return nil - } -} - -func (b *MockBroker) serverError(err error) { - isConnectionClosedError := false - if _, ok := err.(*net.OpError); ok { - isConnectionClosedError = true - } else if err == io.EOF { - isConnectionClosedError = true - } else if err.Error() == "use of closed network connection" { - isConnectionClosedError = true - } - - if isConnectionClosedError { - return - } - - b.t.Errorf(err.Error()) -} - -// NewMockBroker launches a fake Kafka broker. It takes a TestReporter as provided by the -// test framework and a channel of responses to use. If an error occurs it is -// simply logged to the TestReporter and the broker exits. -func NewMockBroker(t TestReporter, brokerID int32) *MockBroker { - return NewMockBrokerAddr(t, brokerID, "localhost:0") -} - -// NewMockBrokerAddr behaves like newMockBroker but listens on the address you give -// it rather than just some ephemeral port. -func NewMockBrokerAddr(t TestReporter, brokerID int32, addr string) *MockBroker { - listener, err := net.Listen("tcp", addr) - if err != nil { - t.Fatal(err) - } - return NewMockBrokerListener(t, brokerID, listener) -} - -// NewMockBrokerListener behaves like newMockBrokerAddr but accepts connections on the listener specified. -func NewMockBrokerListener(t TestReporter, brokerID int32, listener net.Listener) *MockBroker { - var err error - - broker := &MockBroker{ - closing: make(chan none), - stopper: make(chan none), - t: t, - brokerID: brokerID, - expectations: make(chan encoder, 512), - listener: listener, - } - broker.handler = broker.defaultRequestHandler - - Logger.Printf("*** mockbroker/%d listening on %s\n", brokerID, broker.listener.Addr().String()) - _, portStr, err := net.SplitHostPort(broker.listener.Addr().String()) - if err != nil { - t.Fatal(err) - } - tmp, err := strconv.ParseInt(portStr, 10, 32) - if err != nil { - t.Fatal(err) - } - broker.port = int32(tmp) - - go broker.serverLoop() - - return broker -} - -func (b *MockBroker) Returns(e encoder) { - b.expectations <- e -} diff --git a/vendor/github.com/Shopify/sarama/mockresponses.go b/vendor/github.com/Shopify/sarama/mockresponses.go deleted file mode 100644 index 1720441996f..00000000000 --- a/vendor/github.com/Shopify/sarama/mockresponses.go +++ /dev/null @@ -1,727 +0,0 @@ -package sarama - -import ( - "fmt" -) - -// TestReporter has methods matching go's testing.T to avoid importing -// `testing` in the main part of the library. -type TestReporter interface { - Error(...interface{}) - Errorf(string, ...interface{}) - Fatal(...interface{}) - Fatalf(string, ...interface{}) -} - -// MockResponse is a response builder interface it defines one method that -// allows generating a response based on a request body. MockResponses are used -// to program behavior of MockBroker in tests. -type MockResponse interface { - For(reqBody versionedDecoder) (res encoder) -} - -// MockWrapper is a mock response builder that returns a particular concrete -// response regardless of the actual request passed to the `For` method. -type MockWrapper struct { - res encoder -} - -func (mw *MockWrapper) For(reqBody versionedDecoder) (res encoder) { - return mw.res -} - -func NewMockWrapper(res encoder) *MockWrapper { - return &MockWrapper{res: res} -} - -// MockSequence is a mock response builder that is created from a sequence of -// concrete responses. Every time when a `MockBroker` calls its `For` method -// the next response from the sequence is returned. When the end of the -// sequence is reached the last element from the sequence is returned. -type MockSequence struct { - responses []MockResponse -} - -func NewMockSequence(responses ...interface{}) *MockSequence { - ms := &MockSequence{} - ms.responses = make([]MockResponse, len(responses)) - for i, res := range responses { - switch res := res.(type) { - case MockResponse: - ms.responses[i] = res - case encoder: - ms.responses[i] = NewMockWrapper(res) - default: - panic(fmt.Sprintf("Unexpected response type: %T", res)) - } - } - return ms -} - -func (mc *MockSequence) For(reqBody versionedDecoder) (res encoder) { - res = mc.responses[0].For(reqBody) - if len(mc.responses) > 1 { - mc.responses = mc.responses[1:] - } - return res -} - -// MockMetadataResponse is a `MetadataResponse` builder. -type MockMetadataResponse struct { - controllerID int32 - leaders map[string]map[int32]int32 - brokers map[string]int32 - t TestReporter -} - -func NewMockMetadataResponse(t TestReporter) *MockMetadataResponse { - return &MockMetadataResponse{ - leaders: make(map[string]map[int32]int32), - brokers: make(map[string]int32), - t: t, - } -} - -func (mmr *MockMetadataResponse) SetLeader(topic string, partition, brokerID int32) *MockMetadataResponse { - partitions := mmr.leaders[topic] - if partitions == nil { - partitions = make(map[int32]int32) - mmr.leaders[topic] = partitions - } - partitions[partition] = brokerID - return mmr -} - -func (mmr *MockMetadataResponse) SetBroker(addr string, brokerID int32) *MockMetadataResponse { - mmr.brokers[addr] = brokerID - return mmr -} - -func (mmr *MockMetadataResponse) SetController(brokerID int32) *MockMetadataResponse { - mmr.controllerID = brokerID - return mmr -} - -func (mmr *MockMetadataResponse) For(reqBody versionedDecoder) encoder { - metadataRequest := reqBody.(*MetadataRequest) - metadataResponse := &MetadataResponse{ - Version: metadataRequest.version(), - ControllerID: mmr.controllerID, - } - for addr, brokerID := range mmr.brokers { - metadataResponse.AddBroker(addr, brokerID) - } - if len(metadataRequest.Topics) == 0 { - for topic, partitions := range mmr.leaders { - for partition, brokerID := range partitions { - metadataResponse.AddTopicPartition(topic, partition, brokerID, nil, nil, ErrNoError) - } - } - return metadataResponse - } - for _, topic := range metadataRequest.Topics { - for partition, brokerID := range mmr.leaders[topic] { - metadataResponse.AddTopicPartition(topic, partition, brokerID, nil, nil, ErrNoError) - } - } - return metadataResponse -} - -// MockOffsetResponse is an `OffsetResponse` builder. -type MockOffsetResponse struct { - offsets map[string]map[int32]map[int64]int64 - t TestReporter - version int16 -} - -func NewMockOffsetResponse(t TestReporter) *MockOffsetResponse { - return &MockOffsetResponse{ - offsets: make(map[string]map[int32]map[int64]int64), - t: t, - } -} - -func (mor *MockOffsetResponse) SetVersion(version int16) *MockOffsetResponse { - mor.version = version - return mor -} - -func (mor *MockOffsetResponse) SetOffset(topic string, partition int32, time, offset int64) *MockOffsetResponse { - partitions := mor.offsets[topic] - if partitions == nil { - partitions = make(map[int32]map[int64]int64) - mor.offsets[topic] = partitions - } - times := partitions[partition] - if times == nil { - times = make(map[int64]int64) - partitions[partition] = times - } - times[time] = offset - return mor -} - -func (mor *MockOffsetResponse) For(reqBody versionedDecoder) encoder { - offsetRequest := reqBody.(*OffsetRequest) - offsetResponse := &OffsetResponse{Version: mor.version} - for topic, partitions := range offsetRequest.blocks { - for partition, block := range partitions { - offset := mor.getOffset(topic, partition, block.time) - offsetResponse.AddTopicPartition(topic, partition, offset) - } - } - return offsetResponse -} - -func (mor *MockOffsetResponse) getOffset(topic string, partition int32, time int64) int64 { - partitions := mor.offsets[topic] - if partitions == nil { - mor.t.Errorf("missing topic: %s", topic) - } - times := partitions[partition] - if times == nil { - mor.t.Errorf("missing partition: %d", partition) - } - offset, ok := times[time] - if !ok { - mor.t.Errorf("missing time: %d", time) - } - return offset -} - -// MockFetchResponse is a `FetchResponse` builder. -type MockFetchResponse struct { - messages map[string]map[int32]map[int64]Encoder - highWaterMarks map[string]map[int32]int64 - t TestReporter - batchSize int - version int16 -} - -func NewMockFetchResponse(t TestReporter, batchSize int) *MockFetchResponse { - return &MockFetchResponse{ - messages: make(map[string]map[int32]map[int64]Encoder), - highWaterMarks: make(map[string]map[int32]int64), - t: t, - batchSize: batchSize, - } -} - -func (mfr *MockFetchResponse) SetVersion(version int16) *MockFetchResponse { - mfr.version = version - return mfr -} - -func (mfr *MockFetchResponse) SetMessage(topic string, partition int32, offset int64, msg Encoder) *MockFetchResponse { - partitions := mfr.messages[topic] - if partitions == nil { - partitions = make(map[int32]map[int64]Encoder) - mfr.messages[topic] = partitions - } - messages := partitions[partition] - if messages == nil { - messages = make(map[int64]Encoder) - partitions[partition] = messages - } - messages[offset] = msg - return mfr -} - -func (mfr *MockFetchResponse) SetHighWaterMark(topic string, partition int32, offset int64) *MockFetchResponse { - partitions := mfr.highWaterMarks[topic] - if partitions == nil { - partitions = make(map[int32]int64) - mfr.highWaterMarks[topic] = partitions - } - partitions[partition] = offset - return mfr -} - -func (mfr *MockFetchResponse) For(reqBody versionedDecoder) encoder { - fetchRequest := reqBody.(*FetchRequest) - res := &FetchResponse{ - Version: mfr.version, - } - for topic, partitions := range fetchRequest.blocks { - for partition, block := range partitions { - initialOffset := block.fetchOffset - offset := initialOffset - maxOffset := initialOffset + int64(mfr.getMessageCount(topic, partition)) - for i := 0; i < mfr.batchSize && offset < maxOffset; { - msg := mfr.getMessage(topic, partition, offset) - if msg != nil { - res.AddMessage(topic, partition, nil, msg, offset) - i++ - } - offset++ - } - fb := res.GetBlock(topic, partition) - if fb == nil { - res.AddError(topic, partition, ErrNoError) - fb = res.GetBlock(topic, partition) - } - fb.HighWaterMarkOffset = mfr.getHighWaterMark(topic, partition) - } - } - return res -} - -func (mfr *MockFetchResponse) getMessage(topic string, partition int32, offset int64) Encoder { - partitions := mfr.messages[topic] - if partitions == nil { - return nil - } - messages := partitions[partition] - if messages == nil { - return nil - } - return messages[offset] -} - -func (mfr *MockFetchResponse) getMessageCount(topic string, partition int32) int { - partitions := mfr.messages[topic] - if partitions == nil { - return 0 - } - messages := partitions[partition] - if messages == nil { - return 0 - } - return len(messages) -} - -func (mfr *MockFetchResponse) getHighWaterMark(topic string, partition int32) int64 { - partitions := mfr.highWaterMarks[topic] - if partitions == nil { - return 0 - } - return partitions[partition] -} - -// MockConsumerMetadataResponse is a `ConsumerMetadataResponse` builder. -type MockConsumerMetadataResponse struct { - coordinators map[string]interface{} - t TestReporter -} - -func NewMockConsumerMetadataResponse(t TestReporter) *MockConsumerMetadataResponse { - return &MockConsumerMetadataResponse{ - coordinators: make(map[string]interface{}), - t: t, - } -} - -func (mr *MockConsumerMetadataResponse) SetCoordinator(group string, broker *MockBroker) *MockConsumerMetadataResponse { - mr.coordinators[group] = broker - return mr -} - -func (mr *MockConsumerMetadataResponse) SetError(group string, kerror KError) *MockConsumerMetadataResponse { - mr.coordinators[group] = kerror - return mr -} - -func (mr *MockConsumerMetadataResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*ConsumerMetadataRequest) - group := req.ConsumerGroup - res := &ConsumerMetadataResponse{} - v := mr.coordinators[group] - switch v := v.(type) { - case *MockBroker: - res.Coordinator = &Broker{id: v.BrokerID(), addr: v.Addr()} - case KError: - res.Err = v - } - return res -} - -// MockFindCoordinatorResponse is a `FindCoordinatorResponse` builder. -type MockFindCoordinatorResponse struct { - groupCoordinators map[string]interface{} - transCoordinators map[string]interface{} - t TestReporter -} - -func NewMockFindCoordinatorResponse(t TestReporter) *MockFindCoordinatorResponse { - return &MockFindCoordinatorResponse{ - groupCoordinators: make(map[string]interface{}), - transCoordinators: make(map[string]interface{}), - t: t, - } -} - -func (mr *MockFindCoordinatorResponse) SetCoordinator(coordinatorType CoordinatorType, group string, broker *MockBroker) *MockFindCoordinatorResponse { - switch coordinatorType { - case CoordinatorGroup: - mr.groupCoordinators[group] = broker - case CoordinatorTransaction: - mr.transCoordinators[group] = broker - } - return mr -} - -func (mr *MockFindCoordinatorResponse) SetError(coordinatorType CoordinatorType, group string, kerror KError) *MockFindCoordinatorResponse { - switch coordinatorType { - case CoordinatorGroup: - mr.groupCoordinators[group] = kerror - case CoordinatorTransaction: - mr.transCoordinators[group] = kerror - } - return mr -} - -func (mr *MockFindCoordinatorResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*FindCoordinatorRequest) - res := &FindCoordinatorResponse{} - var v interface{} - switch req.CoordinatorType { - case CoordinatorGroup: - v = mr.groupCoordinators[req.CoordinatorKey] - case CoordinatorTransaction: - v = mr.transCoordinators[req.CoordinatorKey] - } - switch v := v.(type) { - case *MockBroker: - res.Coordinator = &Broker{id: v.BrokerID(), addr: v.Addr()} - case KError: - res.Err = v - } - return res -} - -// MockOffsetCommitResponse is a `OffsetCommitResponse` builder. -type MockOffsetCommitResponse struct { - errors map[string]map[string]map[int32]KError - t TestReporter -} - -func NewMockOffsetCommitResponse(t TestReporter) *MockOffsetCommitResponse { - return &MockOffsetCommitResponse{t: t} -} - -func (mr *MockOffsetCommitResponse) SetError(group, topic string, partition int32, kerror KError) *MockOffsetCommitResponse { - if mr.errors == nil { - mr.errors = make(map[string]map[string]map[int32]KError) - } - topics := mr.errors[group] - if topics == nil { - topics = make(map[string]map[int32]KError) - mr.errors[group] = topics - } - partitions := topics[topic] - if partitions == nil { - partitions = make(map[int32]KError) - topics[topic] = partitions - } - partitions[partition] = kerror - return mr -} - -func (mr *MockOffsetCommitResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*OffsetCommitRequest) - group := req.ConsumerGroup - res := &OffsetCommitResponse{} - for topic, partitions := range req.blocks { - for partition := range partitions { - res.AddError(topic, partition, mr.getError(group, topic, partition)) - } - } - return res -} - -func (mr *MockOffsetCommitResponse) getError(group, topic string, partition int32) KError { - topics := mr.errors[group] - if topics == nil { - return ErrNoError - } - partitions := topics[topic] - if partitions == nil { - return ErrNoError - } - kerror, ok := partitions[partition] - if !ok { - return ErrNoError - } - return kerror -} - -// MockProduceResponse is a `ProduceResponse` builder. -type MockProduceResponse struct { - version int16 - errors map[string]map[int32]KError - t TestReporter -} - -func NewMockProduceResponse(t TestReporter) *MockProduceResponse { - return &MockProduceResponse{t: t} -} - -func (mr *MockProduceResponse) SetVersion(version int16) *MockProduceResponse { - mr.version = version - return mr -} - -func (mr *MockProduceResponse) SetError(topic string, partition int32, kerror KError) *MockProduceResponse { - if mr.errors == nil { - mr.errors = make(map[string]map[int32]KError) - } - partitions := mr.errors[topic] - if partitions == nil { - partitions = make(map[int32]KError) - mr.errors[topic] = partitions - } - partitions[partition] = kerror - return mr -} - -func (mr *MockProduceResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*ProduceRequest) - res := &ProduceResponse{ - Version: mr.version, - } - for topic, partitions := range req.records { - for partition := range partitions { - res.AddTopicPartition(topic, partition, mr.getError(topic, partition)) - } - } - return res -} - -func (mr *MockProduceResponse) getError(topic string, partition int32) KError { - partitions := mr.errors[topic] - if partitions == nil { - return ErrNoError - } - kerror, ok := partitions[partition] - if !ok { - return ErrNoError - } - return kerror -} - -// MockOffsetFetchResponse is a `OffsetFetchResponse` builder. -type MockOffsetFetchResponse struct { - offsets map[string]map[string]map[int32]*OffsetFetchResponseBlock - t TestReporter -} - -func NewMockOffsetFetchResponse(t TestReporter) *MockOffsetFetchResponse { - return &MockOffsetFetchResponse{t: t} -} - -func (mr *MockOffsetFetchResponse) SetOffset(group, topic string, partition int32, offset int64, metadata string, kerror KError) *MockOffsetFetchResponse { - if mr.offsets == nil { - mr.offsets = make(map[string]map[string]map[int32]*OffsetFetchResponseBlock) - } - topics := mr.offsets[group] - if topics == nil { - topics = make(map[string]map[int32]*OffsetFetchResponseBlock) - mr.offsets[group] = topics - } - partitions := topics[topic] - if partitions == nil { - partitions = make(map[int32]*OffsetFetchResponseBlock) - topics[topic] = partitions - } - partitions[partition] = &OffsetFetchResponseBlock{offset, metadata, kerror} - return mr -} - -func (mr *MockOffsetFetchResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*OffsetFetchRequest) - group := req.ConsumerGroup - res := &OffsetFetchResponse{} - for topic, partitions := range mr.offsets[group] { - for partition, block := range partitions { - res.AddBlock(topic, partition, block) - } - } - return res -} - -type MockCreateTopicsResponse struct { - t TestReporter -} - -func NewMockCreateTopicsResponse(t TestReporter) *MockCreateTopicsResponse { - return &MockCreateTopicsResponse{t: t} -} - -func (mr *MockCreateTopicsResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*CreateTopicsRequest) - res := &CreateTopicsResponse{} - res.TopicErrors = make(map[string]*TopicError) - - for topic, _ := range req.TopicDetails { - res.TopicErrors[topic] = &TopicError{Err: ErrNoError} - } - return res -} - -type MockDeleteTopicsResponse struct { - t TestReporter -} - -func NewMockDeleteTopicsResponse(t TestReporter) *MockDeleteTopicsResponse { - return &MockDeleteTopicsResponse{t: t} -} - -func (mr *MockDeleteTopicsResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*DeleteTopicsRequest) - res := &DeleteTopicsResponse{} - res.TopicErrorCodes = make(map[string]KError) - - for _, topic := range req.Topics { - res.TopicErrorCodes[topic] = ErrNoError - } - return res -} - -type MockCreatePartitionsResponse struct { - t TestReporter -} - -func NewMockCreatePartitionsResponse(t TestReporter) *MockCreatePartitionsResponse { - return &MockCreatePartitionsResponse{t: t} -} - -func (mr *MockCreatePartitionsResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*CreatePartitionsRequest) - res := &CreatePartitionsResponse{} - res.TopicPartitionErrors = make(map[string]*TopicPartitionError) - - for topic, _ := range req.TopicPartitions { - res.TopicPartitionErrors[topic] = &TopicPartitionError{Err: ErrNoError} - } - return res -} - -type MockDeleteRecordsResponse struct { - t TestReporter -} - -func NewMockDeleteRecordsResponse(t TestReporter) *MockDeleteRecordsResponse { - return &MockDeleteRecordsResponse{t: t} -} - -func (mr *MockDeleteRecordsResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*DeleteRecordsRequest) - res := &DeleteRecordsResponse{} - res.Topics = make(map[string]*DeleteRecordsResponseTopic) - - for topic, deleteRecordRequestTopic := range req.Topics { - partitions := make(map[int32]*DeleteRecordsResponsePartition) - for partition, _ := range deleteRecordRequestTopic.PartitionOffsets { - partitions[partition] = &DeleteRecordsResponsePartition{Err: ErrNoError} - } - res.Topics[topic] = &DeleteRecordsResponseTopic{Partitions: partitions} - } - return res -} - -type MockDescribeConfigsResponse struct { - t TestReporter -} - -func NewMockDescribeConfigsResponse(t TestReporter) *MockDescribeConfigsResponse { - return &MockDescribeConfigsResponse{t: t} -} - -func (mr *MockDescribeConfigsResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*DescribeConfigsRequest) - res := &DescribeConfigsResponse{} - - var configEntries []*ConfigEntry - configEntries = append(configEntries, &ConfigEntry{Name: "my_topic", - Value: "my_topic", - ReadOnly: true, - Default: true, - Sensitive: false, - }) - - for _, r := range req.Resources { - res.Resources = append(res.Resources, &ResourceResponse{Name: r.Name, Configs: configEntries}) - } - return res -} - -type MockAlterConfigsResponse struct { - t TestReporter -} - -func NewMockAlterConfigsResponse(t TestReporter) *MockAlterConfigsResponse { - return &MockAlterConfigsResponse{t: t} -} - -func (mr *MockAlterConfigsResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*AlterConfigsRequest) - res := &AlterConfigsResponse{} - - for _, r := range req.Resources { - res.Resources = append(res.Resources, &AlterConfigsResourceResponse{Name: r.Name, - Type: TopicResource, - ErrorMsg: "", - }) - } - return res -} - -type MockCreateAclsResponse struct { - t TestReporter -} - -func NewMockCreateAclsResponse(t TestReporter) *MockCreateAclsResponse { - return &MockCreateAclsResponse{t: t} -} - -func (mr *MockCreateAclsResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*CreateAclsRequest) - res := &CreateAclsResponse{} - - for range req.AclCreations { - res.AclCreationResponses = append(res.AclCreationResponses, &AclCreationResponse{Err: ErrNoError}) - } - return res -} - -type MockListAclsResponse struct { - t TestReporter -} - -func NewMockListAclsResponse(t TestReporter) *MockListAclsResponse { - return &MockListAclsResponse{t: t} -} - -func (mr *MockListAclsResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*DescribeAclsRequest) - res := &DescribeAclsResponse{} - - res.Err = ErrNoError - acl := &ResourceAcls{} - acl.Resource.ResourceName = *req.ResourceName - acl.Resource.ResourceType = req.ResourceType - acl.Acls = append(acl.Acls, &Acl{}) - res.ResourceAcls = append(res.ResourceAcls, acl) - - return res -} - -type MockDeleteAclsResponse struct { - t TestReporter -} - -func NewMockDeleteAclsResponse(t TestReporter) *MockDeleteAclsResponse { - return &MockDeleteAclsResponse{t: t} -} - -func (mr *MockDeleteAclsResponse) For(reqBody versionedDecoder) encoder { - req := reqBody.(*DeleteAclsRequest) - res := &DeleteAclsResponse{} - - for range req.Filters { - response := &FilterResponse{Err: ErrNoError} - response.MatchingAcls = append(response.MatchingAcls, &MatchingAcl{Err: ErrNoError}) - res.FilterResponses = append(res.FilterResponses, response) - } - return res -} diff --git a/vendor/github.com/Shopify/sarama/offset_commit_request.go b/vendor/github.com/Shopify/sarama/offset_commit_request.go deleted file mode 100644 index 37e99fbf5b8..00000000000 --- a/vendor/github.com/Shopify/sarama/offset_commit_request.go +++ /dev/null @@ -1,204 +0,0 @@ -package sarama - -import "errors" - -// ReceiveTime is a special value for the timestamp field of Offset Commit Requests which -// tells the broker to set the timestamp to the time at which the request was received. -// The timestamp is only used if message version 1 is used, which requires kafka 0.8.2. -const ReceiveTime int64 = -1 - -// GroupGenerationUndefined is a special value for the group generation field of -// Offset Commit Requests that should be used when a consumer group does not rely -// on Kafka for partition management. -const GroupGenerationUndefined = -1 - -type offsetCommitRequestBlock struct { - offset int64 - timestamp int64 - metadata string -} - -func (b *offsetCommitRequestBlock) encode(pe packetEncoder, version int16) error { - pe.putInt64(b.offset) - if version == 1 { - pe.putInt64(b.timestamp) - } else if b.timestamp != 0 { - Logger.Println("Non-zero timestamp specified for OffsetCommitRequest not v1, it will be ignored") - } - - return pe.putString(b.metadata) -} - -func (b *offsetCommitRequestBlock) decode(pd packetDecoder, version int16) (err error) { - if b.offset, err = pd.getInt64(); err != nil { - return err - } - if version == 1 { - if b.timestamp, err = pd.getInt64(); err != nil { - return err - } - } - b.metadata, err = pd.getString() - return err -} - -type OffsetCommitRequest struct { - ConsumerGroup string - ConsumerGroupGeneration int32 // v1 or later - ConsumerID string // v1 or later - RetentionTime int64 // v2 or later - - // Version can be: - // - 0 (kafka 0.8.1 and later) - // - 1 (kafka 0.8.2 and later) - // - 2 (kafka 0.9.0 and later) - Version int16 - blocks map[string]map[int32]*offsetCommitRequestBlock -} - -func (r *OffsetCommitRequest) encode(pe packetEncoder) error { - if r.Version < 0 || r.Version > 2 { - return PacketEncodingError{"invalid or unsupported OffsetCommitRequest version field"} - } - - if err := pe.putString(r.ConsumerGroup); err != nil { - return err - } - - if r.Version >= 1 { - pe.putInt32(r.ConsumerGroupGeneration) - if err := pe.putString(r.ConsumerID); err != nil { - return err - } - } else { - if r.ConsumerGroupGeneration != 0 { - Logger.Println("Non-zero ConsumerGroupGeneration specified for OffsetCommitRequest v0, it will be ignored") - } - if r.ConsumerID != "" { - Logger.Println("Non-empty ConsumerID specified for OffsetCommitRequest v0, it will be ignored") - } - } - - if r.Version >= 2 { - pe.putInt64(r.RetentionTime) - } else if r.RetentionTime != 0 { - Logger.Println("Non-zero RetentionTime specified for OffsetCommitRequest version <2, it will be ignored") - } - - if err := pe.putArrayLength(len(r.blocks)); err != nil { - return err - } - for topic, partitions := range r.blocks { - if err := pe.putString(topic); err != nil { - return err - } - if err := pe.putArrayLength(len(partitions)); err != nil { - return err - } - for partition, block := range partitions { - pe.putInt32(partition) - if err := block.encode(pe, r.Version); err != nil { - return err - } - } - } - return nil -} - -func (r *OffsetCommitRequest) decode(pd packetDecoder, version int16) (err error) { - r.Version = version - - if r.ConsumerGroup, err = pd.getString(); err != nil { - return err - } - - if r.Version >= 1 { - if r.ConsumerGroupGeneration, err = pd.getInt32(); err != nil { - return err - } - if r.ConsumerID, err = pd.getString(); err != nil { - return err - } - } - - if r.Version >= 2 { - if r.RetentionTime, err = pd.getInt64(); err != nil { - return err - } - } - - topicCount, err := pd.getArrayLength() - if err != nil { - return err - } - if topicCount == 0 { - return nil - } - r.blocks = make(map[string]map[int32]*offsetCommitRequestBlock) - for i := 0; i < topicCount; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - partitionCount, err := pd.getArrayLength() - if err != nil { - return err - } - r.blocks[topic] = make(map[int32]*offsetCommitRequestBlock) - for j := 0; j < partitionCount; j++ { - partition, err := pd.getInt32() - if err != nil { - return err - } - block := &offsetCommitRequestBlock{} - if err := block.decode(pd, r.Version); err != nil { - return err - } - r.blocks[topic][partition] = block - } - } - return nil -} - -func (r *OffsetCommitRequest) key() int16 { - return 8 -} - -func (r *OffsetCommitRequest) version() int16 { - return r.Version -} - -func (r *OffsetCommitRequest) requiredVersion() KafkaVersion { - switch r.Version { - case 1: - return V0_8_2_0 - case 2: - return V0_9_0_0 - default: - return MinVersion - } -} - -func (r *OffsetCommitRequest) AddBlock(topic string, partitionID int32, offset int64, timestamp int64, metadata string) { - if r.blocks == nil { - r.blocks = make(map[string]map[int32]*offsetCommitRequestBlock) - } - - if r.blocks[topic] == nil { - r.blocks[topic] = make(map[int32]*offsetCommitRequestBlock) - } - - r.blocks[topic][partitionID] = &offsetCommitRequestBlock{offset, timestamp, metadata} -} - -func (r *OffsetCommitRequest) Offset(topic string, partitionID int32) (int64, string, error) { - partitions := r.blocks[topic] - if partitions == nil { - return 0, "", errors.New("No such offset") - } - block := partitions[partitionID] - if block == nil { - return 0, "", errors.New("No such offset") - } - return block.offset, block.metadata, nil -} diff --git a/vendor/github.com/Shopify/sarama/offset_commit_response.go b/vendor/github.com/Shopify/sarama/offset_commit_response.go deleted file mode 100644 index a4b18acdff2..00000000000 --- a/vendor/github.com/Shopify/sarama/offset_commit_response.go +++ /dev/null @@ -1,85 +0,0 @@ -package sarama - -type OffsetCommitResponse struct { - Errors map[string]map[int32]KError -} - -func (r *OffsetCommitResponse) AddError(topic string, partition int32, kerror KError) { - if r.Errors == nil { - r.Errors = make(map[string]map[int32]KError) - } - partitions := r.Errors[topic] - if partitions == nil { - partitions = make(map[int32]KError) - r.Errors[topic] = partitions - } - partitions[partition] = kerror -} - -func (r *OffsetCommitResponse) encode(pe packetEncoder) error { - if err := pe.putArrayLength(len(r.Errors)); err != nil { - return err - } - for topic, partitions := range r.Errors { - if err := pe.putString(topic); err != nil { - return err - } - if err := pe.putArrayLength(len(partitions)); err != nil { - return err - } - for partition, kerror := range partitions { - pe.putInt32(partition) - pe.putInt16(int16(kerror)) - } - } - return nil -} - -func (r *OffsetCommitResponse) decode(pd packetDecoder, version int16) (err error) { - numTopics, err := pd.getArrayLength() - if err != nil || numTopics == 0 { - return err - } - - r.Errors = make(map[string]map[int32]KError, numTopics) - for i := 0; i < numTopics; i++ { - name, err := pd.getString() - if err != nil { - return err - } - - numErrors, err := pd.getArrayLength() - if err != nil { - return err - } - - r.Errors[name] = make(map[int32]KError, numErrors) - - for j := 0; j < numErrors; j++ { - id, err := pd.getInt32() - if err != nil { - return err - } - - tmp, err := pd.getInt16() - if err != nil { - return err - } - r.Errors[name][id] = KError(tmp) - } - } - - return nil -} - -func (r *OffsetCommitResponse) key() int16 { - return 8 -} - -func (r *OffsetCommitResponse) version() int16 { - return 0 -} - -func (r *OffsetCommitResponse) requiredVersion() KafkaVersion { - return MinVersion -} diff --git a/vendor/github.com/Shopify/sarama/offset_fetch_request.go b/vendor/github.com/Shopify/sarama/offset_fetch_request.go deleted file mode 100644 index 5a05014b481..00000000000 --- a/vendor/github.com/Shopify/sarama/offset_fetch_request.go +++ /dev/null @@ -1,81 +0,0 @@ -package sarama - -type OffsetFetchRequest struct { - ConsumerGroup string - Version int16 - partitions map[string][]int32 -} - -func (r *OffsetFetchRequest) encode(pe packetEncoder) (err error) { - if r.Version < 0 || r.Version > 1 { - return PacketEncodingError{"invalid or unsupported OffsetFetchRequest version field"} - } - - if err = pe.putString(r.ConsumerGroup); err != nil { - return err - } - if err = pe.putArrayLength(len(r.partitions)); err != nil { - return err - } - for topic, partitions := range r.partitions { - if err = pe.putString(topic); err != nil { - return err - } - if err = pe.putInt32Array(partitions); err != nil { - return err - } - } - return nil -} - -func (r *OffsetFetchRequest) decode(pd packetDecoder, version int16) (err error) { - r.Version = version - if r.ConsumerGroup, err = pd.getString(); err != nil { - return err - } - partitionCount, err := pd.getArrayLength() - if err != nil { - return err - } - if partitionCount == 0 { - return nil - } - r.partitions = make(map[string][]int32) - for i := 0; i < partitionCount; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - partitions, err := pd.getInt32Array() - if err != nil { - return err - } - r.partitions[topic] = partitions - } - return nil -} - -func (r *OffsetFetchRequest) key() int16 { - return 9 -} - -func (r *OffsetFetchRequest) version() int16 { - return r.Version -} - -func (r *OffsetFetchRequest) requiredVersion() KafkaVersion { - switch r.Version { - case 1: - return V0_8_2_0 - default: - return MinVersion - } -} - -func (r *OffsetFetchRequest) AddPartition(topic string, partitionID int32) { - if r.partitions == nil { - r.partitions = make(map[string][]int32) - } - - r.partitions[topic] = append(r.partitions[topic], partitionID) -} diff --git a/vendor/github.com/Shopify/sarama/offset_fetch_response.go b/vendor/github.com/Shopify/sarama/offset_fetch_response.go deleted file mode 100644 index 11e4b1f3fdf..00000000000 --- a/vendor/github.com/Shopify/sarama/offset_fetch_response.go +++ /dev/null @@ -1,143 +0,0 @@ -package sarama - -type OffsetFetchResponseBlock struct { - Offset int64 - Metadata string - Err KError -} - -func (b *OffsetFetchResponseBlock) decode(pd packetDecoder) (err error) { - b.Offset, err = pd.getInt64() - if err != nil { - return err - } - - b.Metadata, err = pd.getString() - if err != nil { - return err - } - - tmp, err := pd.getInt16() - if err != nil { - return err - } - b.Err = KError(tmp) - - return nil -} - -func (b *OffsetFetchResponseBlock) encode(pe packetEncoder) (err error) { - pe.putInt64(b.Offset) - - err = pe.putString(b.Metadata) - if err != nil { - return err - } - - pe.putInt16(int16(b.Err)) - - return nil -} - -type OffsetFetchResponse struct { - Blocks map[string]map[int32]*OffsetFetchResponseBlock -} - -func (r *OffsetFetchResponse) encode(pe packetEncoder) error { - if err := pe.putArrayLength(len(r.Blocks)); err != nil { - return err - } - for topic, partitions := range r.Blocks { - if err := pe.putString(topic); err != nil { - return err - } - if err := pe.putArrayLength(len(partitions)); err != nil { - return err - } - for partition, block := range partitions { - pe.putInt32(partition) - if err := block.encode(pe); err != nil { - return err - } - } - } - return nil -} - -func (r *OffsetFetchResponse) decode(pd packetDecoder, version int16) (err error) { - numTopics, err := pd.getArrayLength() - if err != nil || numTopics == 0 { - return err - } - - r.Blocks = make(map[string]map[int32]*OffsetFetchResponseBlock, numTopics) - for i := 0; i < numTopics; i++ { - name, err := pd.getString() - if err != nil { - return err - } - - numBlocks, err := pd.getArrayLength() - if err != nil { - return err - } - - if numBlocks == 0 { - r.Blocks[name] = nil - continue - } - r.Blocks[name] = make(map[int32]*OffsetFetchResponseBlock, numBlocks) - - for j := 0; j < numBlocks; j++ { - id, err := pd.getInt32() - if err != nil { - return err - } - - block := new(OffsetFetchResponseBlock) - err = block.decode(pd) - if err != nil { - return err - } - r.Blocks[name][id] = block - } - } - - return nil -} - -func (r *OffsetFetchResponse) key() int16 { - return 9 -} - -func (r *OffsetFetchResponse) version() int16 { - return 0 -} - -func (r *OffsetFetchResponse) requiredVersion() KafkaVersion { - return MinVersion -} - -func (r *OffsetFetchResponse) GetBlock(topic string, partition int32) *OffsetFetchResponseBlock { - if r.Blocks == nil { - return nil - } - - if r.Blocks[topic] == nil { - return nil - } - - return r.Blocks[topic][partition] -} - -func (r *OffsetFetchResponse) AddBlock(topic string, partition int32, block *OffsetFetchResponseBlock) { - if r.Blocks == nil { - r.Blocks = make(map[string]map[int32]*OffsetFetchResponseBlock) - } - partitions := r.Blocks[topic] - if partitions == nil { - partitions = make(map[int32]*OffsetFetchResponseBlock) - r.Blocks[topic] = partitions - } - partitions[partition] = block -} diff --git a/vendor/github.com/Shopify/sarama/offset_manager.go b/vendor/github.com/Shopify/sarama/offset_manager.go deleted file mode 100644 index 6c01f959e99..00000000000 --- a/vendor/github.com/Shopify/sarama/offset_manager.go +++ /dev/null @@ -1,560 +0,0 @@ -package sarama - -import ( - "sync" - "time" -) - -// Offset Manager - -// OffsetManager uses Kafka to store and fetch consumed partition offsets. -type OffsetManager interface { - // ManagePartition creates a PartitionOffsetManager on the given topic/partition. - // It will return an error if this OffsetManager is already managing the given - // topic/partition. - ManagePartition(topic string, partition int32) (PartitionOffsetManager, error) - - // Close stops the OffsetManager from managing offsets. It is required to call - // this function before an OffsetManager object passes out of scope, as it - // will otherwise leak memory. You must call this after all the - // PartitionOffsetManagers are closed. - Close() error -} - -type offsetManager struct { - client Client - conf *Config - group string - - lock sync.Mutex - poms map[string]map[int32]*partitionOffsetManager - boms map[*Broker]*brokerOffsetManager -} - -// NewOffsetManagerFromClient creates a new OffsetManager from the given client. -// It is still necessary to call Close() on the underlying client when finished with the partition manager. -func NewOffsetManagerFromClient(group string, client Client) (OffsetManager, error) { - // Check that we are not dealing with a closed Client before processing any other arguments - if client.Closed() { - return nil, ErrClosedClient - } - - om := &offsetManager{ - client: client, - conf: client.Config(), - group: group, - poms: make(map[string]map[int32]*partitionOffsetManager), - boms: make(map[*Broker]*brokerOffsetManager), - } - - return om, nil -} - -func (om *offsetManager) ManagePartition(topic string, partition int32) (PartitionOffsetManager, error) { - pom, err := om.newPartitionOffsetManager(topic, partition) - if err != nil { - return nil, err - } - - om.lock.Lock() - defer om.lock.Unlock() - - topicManagers := om.poms[topic] - if topicManagers == nil { - topicManagers = make(map[int32]*partitionOffsetManager) - om.poms[topic] = topicManagers - } - - if topicManagers[partition] != nil { - return nil, ConfigurationError("That topic/partition is already being managed") - } - - topicManagers[partition] = pom - return pom, nil -} - -func (om *offsetManager) Close() error { - return nil -} - -func (om *offsetManager) refBrokerOffsetManager(broker *Broker) *brokerOffsetManager { - om.lock.Lock() - defer om.lock.Unlock() - - bom := om.boms[broker] - if bom == nil { - bom = om.newBrokerOffsetManager(broker) - om.boms[broker] = bom - } - - bom.refs++ - - return bom -} - -func (om *offsetManager) unrefBrokerOffsetManager(bom *brokerOffsetManager) { - om.lock.Lock() - defer om.lock.Unlock() - - bom.refs-- - - if bom.refs == 0 { - close(bom.updateSubscriptions) - if om.boms[bom.broker] == bom { - delete(om.boms, bom.broker) - } - } -} - -func (om *offsetManager) abandonBroker(bom *brokerOffsetManager) { - om.lock.Lock() - defer om.lock.Unlock() - - delete(om.boms, bom.broker) -} - -func (om *offsetManager) abandonPartitionOffsetManager(pom *partitionOffsetManager) { - om.lock.Lock() - defer om.lock.Unlock() - - delete(om.poms[pom.topic], pom.partition) - if len(om.poms[pom.topic]) == 0 { - delete(om.poms, pom.topic) - } -} - -// Partition Offset Manager - -// PartitionOffsetManager uses Kafka to store and fetch consumed partition offsets. You MUST call Close() -// on a partition offset manager to avoid leaks, it will not be garbage-collected automatically when it passes -// out of scope. -type PartitionOffsetManager interface { - // NextOffset returns the next offset that should be consumed for the managed - // partition, accompanied by metadata which can be used to reconstruct the state - // of the partition consumer when it resumes. NextOffset() will return - // `config.Consumer.Offsets.Initial` and an empty metadata string if no offset - // was committed for this partition yet. - NextOffset() (int64, string) - - // MarkOffset marks the provided offset, alongside a metadata string - // that represents the state of the partition consumer at that point in time. The - // metadata string can be used by another consumer to restore that state, so it - // can resume consumption. - // - // To follow upstream conventions, you are expected to mark the offset of the - // next message to read, not the last message read. Thus, when calling `MarkOffset` - // you should typically add one to the offset of the last consumed message. - // - // Note: calling MarkOffset does not necessarily commit the offset to the backend - // store immediately for efficiency reasons, and it may never be committed if - // your application crashes. This means that you may end up processing the same - // message twice, and your processing should ideally be idempotent. - MarkOffset(offset int64, metadata string) - - // ResetOffset resets to the provided offset, alongside a metadata string that - // represents the state of the partition consumer at that point in time. Reset - // acts as a counterpart to MarkOffset, the difference being that it allows to - // reset an offset to an earlier or smaller value, where MarkOffset only - // allows incrementing the offset. cf MarkOffset for more details. - ResetOffset(offset int64, metadata string) - - // Errors returns a read channel of errors that occur during offset management, if - // enabled. By default, errors are logged and not returned over this channel. If - // you want to implement any custom error handling, set your config's - // Consumer.Return.Errors setting to true, and read from this channel. - Errors() <-chan *ConsumerError - - // AsyncClose initiates a shutdown of the PartitionOffsetManager. This method will - // return immediately, after which you should wait until the 'errors' channel has - // been drained and closed. It is required to call this function, or Close before - // a consumer object passes out of scope, as it will otherwise leak memory. You - // must call this before calling Close on the underlying client. - AsyncClose() - - // Close stops the PartitionOffsetManager from managing offsets. It is required to - // call this function (or AsyncClose) before a PartitionOffsetManager object - // passes out of scope, as it will otherwise leak memory. You must call this - // before calling Close on the underlying client. - Close() error -} - -type partitionOffsetManager struct { - parent *offsetManager - topic string - partition int32 - - lock sync.Mutex - offset int64 - metadata string - dirty bool - clean sync.Cond - broker *brokerOffsetManager - - errors chan *ConsumerError - rebalance chan none - dying chan none -} - -func (om *offsetManager) newPartitionOffsetManager(topic string, partition int32) (*partitionOffsetManager, error) { - pom := &partitionOffsetManager{ - parent: om, - topic: topic, - partition: partition, - errors: make(chan *ConsumerError, om.conf.ChannelBufferSize), - rebalance: make(chan none, 1), - dying: make(chan none), - } - pom.clean.L = &pom.lock - - if err := pom.selectBroker(); err != nil { - return nil, err - } - - if err := pom.fetchInitialOffset(om.conf.Metadata.Retry.Max); err != nil { - return nil, err - } - - pom.broker.updateSubscriptions <- pom - - go withRecover(pom.mainLoop) - - return pom, nil -} - -func (pom *partitionOffsetManager) mainLoop() { - for { - select { - case <-pom.rebalance: - if err := pom.selectBroker(); err != nil { - pom.handleError(err) - pom.rebalance <- none{} - } else { - pom.broker.updateSubscriptions <- pom - } - case <-pom.dying: - if pom.broker != nil { - select { - case <-pom.rebalance: - case pom.broker.updateSubscriptions <- pom: - } - pom.parent.unrefBrokerOffsetManager(pom.broker) - } - pom.parent.abandonPartitionOffsetManager(pom) - close(pom.errors) - return - } - } -} - -func (pom *partitionOffsetManager) selectBroker() error { - if pom.broker != nil { - pom.parent.unrefBrokerOffsetManager(pom.broker) - pom.broker = nil - } - - var broker *Broker - var err error - - if err = pom.parent.client.RefreshCoordinator(pom.parent.group); err != nil { - return err - } - - if broker, err = pom.parent.client.Coordinator(pom.parent.group); err != nil { - return err - } - - pom.broker = pom.parent.refBrokerOffsetManager(broker) - return nil -} - -func (pom *partitionOffsetManager) fetchInitialOffset(retries int) error { - request := new(OffsetFetchRequest) - request.Version = 1 - request.ConsumerGroup = pom.parent.group - request.AddPartition(pom.topic, pom.partition) - - response, err := pom.broker.broker.FetchOffset(request) - if err != nil { - return err - } - - block := response.GetBlock(pom.topic, pom.partition) - if block == nil { - return ErrIncompleteResponse - } - - switch block.Err { - case ErrNoError: - pom.offset = block.Offset - pom.metadata = block.Metadata - return nil - case ErrNotCoordinatorForConsumer: - if retries <= 0 { - return block.Err - } - if err := pom.selectBroker(); err != nil { - return err - } - return pom.fetchInitialOffset(retries - 1) - case ErrOffsetsLoadInProgress: - if retries <= 0 { - return block.Err - } - time.Sleep(pom.parent.conf.Metadata.Retry.Backoff) - return pom.fetchInitialOffset(retries - 1) - default: - return block.Err - } -} - -func (pom *partitionOffsetManager) handleError(err error) { - cErr := &ConsumerError{ - Topic: pom.topic, - Partition: pom.partition, - Err: err, - } - - if pom.parent.conf.Consumer.Return.Errors { - pom.errors <- cErr - } else { - Logger.Println(cErr) - } -} - -func (pom *partitionOffsetManager) Errors() <-chan *ConsumerError { - return pom.errors -} - -func (pom *partitionOffsetManager) MarkOffset(offset int64, metadata string) { - pom.lock.Lock() - defer pom.lock.Unlock() - - if offset > pom.offset { - pom.offset = offset - pom.metadata = metadata - pom.dirty = true - } -} - -func (pom *partitionOffsetManager) ResetOffset(offset int64, metadata string) { - pom.lock.Lock() - defer pom.lock.Unlock() - - if offset <= pom.offset { - pom.offset = offset - pom.metadata = metadata - pom.dirty = true - } -} - -func (pom *partitionOffsetManager) updateCommitted(offset int64, metadata string) { - pom.lock.Lock() - defer pom.lock.Unlock() - - if pom.offset == offset && pom.metadata == metadata { - pom.dirty = false - pom.clean.Signal() - } -} - -func (pom *partitionOffsetManager) NextOffset() (int64, string) { - pom.lock.Lock() - defer pom.lock.Unlock() - - if pom.offset >= 0 { - return pom.offset, pom.metadata - } - - return pom.parent.conf.Consumer.Offsets.Initial, "" -} - -func (pom *partitionOffsetManager) AsyncClose() { - go func() { - pom.lock.Lock() - defer pom.lock.Unlock() - - for pom.dirty { - pom.clean.Wait() - } - - close(pom.dying) - }() -} - -func (pom *partitionOffsetManager) Close() error { - pom.AsyncClose() - - var errors ConsumerErrors - for err := range pom.errors { - errors = append(errors, err) - } - - if len(errors) > 0 { - return errors - } - return nil -} - -// Broker Offset Manager - -type brokerOffsetManager struct { - parent *offsetManager - broker *Broker - timer *time.Ticker - updateSubscriptions chan *partitionOffsetManager - subscriptions map[*partitionOffsetManager]none - refs int -} - -func (om *offsetManager) newBrokerOffsetManager(broker *Broker) *brokerOffsetManager { - bom := &brokerOffsetManager{ - parent: om, - broker: broker, - timer: time.NewTicker(om.conf.Consumer.Offsets.CommitInterval), - updateSubscriptions: make(chan *partitionOffsetManager), - subscriptions: make(map[*partitionOffsetManager]none), - } - - go withRecover(bom.mainLoop) - - return bom -} - -func (bom *brokerOffsetManager) mainLoop() { - for { - select { - case <-bom.timer.C: - if len(bom.subscriptions) > 0 { - bom.flushToBroker() - } - case s, ok := <-bom.updateSubscriptions: - if !ok { - bom.timer.Stop() - return - } - if _, ok := bom.subscriptions[s]; ok { - delete(bom.subscriptions, s) - } else { - bom.subscriptions[s] = none{} - } - } - } -} - -func (bom *brokerOffsetManager) flushToBroker() { - request := bom.constructRequest() - if request == nil { - return - } - - response, err := bom.broker.CommitOffset(request) - - if err != nil { - bom.abort(err) - return - } - - for s := range bom.subscriptions { - if request.blocks[s.topic] == nil || request.blocks[s.topic][s.partition] == nil { - continue - } - - var err KError - var ok bool - - if response.Errors[s.topic] == nil { - s.handleError(ErrIncompleteResponse) - delete(bom.subscriptions, s) - s.rebalance <- none{} - continue - } - if err, ok = response.Errors[s.topic][s.partition]; !ok { - s.handleError(ErrIncompleteResponse) - delete(bom.subscriptions, s) - s.rebalance <- none{} - continue - } - - switch err { - case ErrNoError: - block := request.blocks[s.topic][s.partition] - s.updateCommitted(block.offset, block.metadata) - case ErrNotLeaderForPartition, ErrLeaderNotAvailable, - ErrConsumerCoordinatorNotAvailable, ErrNotCoordinatorForConsumer: - // not a critical error, we just need to redispatch - delete(bom.subscriptions, s) - s.rebalance <- none{} - case ErrOffsetMetadataTooLarge, ErrInvalidCommitOffsetSize: - // nothing we can do about this, just tell the user and carry on - s.handleError(err) - case ErrOffsetsLoadInProgress: - // nothing wrong but we didn't commit, we'll get it next time round - break - case ErrUnknownTopicOrPartition: - // let the user know *and* try redispatching - if topic-auto-create is - // enabled, redispatching should trigger a metadata request and create the - // topic; if not then re-dispatching won't help, but we've let the user - // know and it shouldn't hurt either (see https://github.com/Shopify/sarama/issues/706) - fallthrough - default: - // dunno, tell the user and try redispatching - s.handleError(err) - delete(bom.subscriptions, s) - s.rebalance <- none{} - } - } -} - -func (bom *brokerOffsetManager) constructRequest() *OffsetCommitRequest { - var r *OffsetCommitRequest - var perPartitionTimestamp int64 - if bom.parent.conf.Consumer.Offsets.Retention == 0 { - perPartitionTimestamp = ReceiveTime - r = &OffsetCommitRequest{ - Version: 1, - ConsumerGroup: bom.parent.group, - ConsumerGroupGeneration: GroupGenerationUndefined, - } - } else { - r = &OffsetCommitRequest{ - Version: 2, - RetentionTime: int64(bom.parent.conf.Consumer.Offsets.Retention / time.Millisecond), - ConsumerGroup: bom.parent.group, - ConsumerGroupGeneration: GroupGenerationUndefined, - } - - } - - for s := range bom.subscriptions { - s.lock.Lock() - if s.dirty { - r.AddBlock(s.topic, s.partition, s.offset, perPartitionTimestamp, s.metadata) - } - s.lock.Unlock() - } - - if len(r.blocks) > 0 { - return r - } - - return nil -} - -func (bom *brokerOffsetManager) abort(err error) { - _ = bom.broker.Close() // we don't care about the error this might return, we already have one - bom.parent.abandonBroker(bom) - - for pom := range bom.subscriptions { - pom.handleError(err) - pom.rebalance <- none{} - } - - for s := range bom.updateSubscriptions { - if _, ok := bom.subscriptions[s]; !ok { - s.handleError(err) - s.rebalance <- none{} - } - } - - bom.subscriptions = make(map[*partitionOffsetManager]none) -} diff --git a/vendor/github.com/Shopify/sarama/offset_request.go b/vendor/github.com/Shopify/sarama/offset_request.go deleted file mode 100644 index 4c5df75df05..00000000000 --- a/vendor/github.com/Shopify/sarama/offset_request.go +++ /dev/null @@ -1,132 +0,0 @@ -package sarama - -type offsetRequestBlock struct { - time int64 - maxOffsets int32 // Only used in version 0 -} - -func (b *offsetRequestBlock) encode(pe packetEncoder, version int16) error { - pe.putInt64(int64(b.time)) - if version == 0 { - pe.putInt32(b.maxOffsets) - } - - return nil -} - -func (b *offsetRequestBlock) decode(pd packetDecoder, version int16) (err error) { - if b.time, err = pd.getInt64(); err != nil { - return err - } - if version == 0 { - if b.maxOffsets, err = pd.getInt32(); err != nil { - return err - } - } - return nil -} - -type OffsetRequest struct { - Version int16 - blocks map[string]map[int32]*offsetRequestBlock -} - -func (r *OffsetRequest) encode(pe packetEncoder) error { - pe.putInt32(-1) // replica ID is always -1 for clients - err := pe.putArrayLength(len(r.blocks)) - if err != nil { - return err - } - for topic, partitions := range r.blocks { - err = pe.putString(topic) - if err != nil { - return err - } - err = pe.putArrayLength(len(partitions)) - if err != nil { - return err - } - for partition, block := range partitions { - pe.putInt32(partition) - if err = block.encode(pe, r.Version); err != nil { - return err - } - } - } - return nil -} - -func (r *OffsetRequest) decode(pd packetDecoder, version int16) error { - r.Version = version - - // Ignore replica ID - if _, err := pd.getInt32(); err != nil { - return err - } - blockCount, err := pd.getArrayLength() - if err != nil { - return err - } - if blockCount == 0 { - return nil - } - r.blocks = make(map[string]map[int32]*offsetRequestBlock) - for i := 0; i < blockCount; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - partitionCount, err := pd.getArrayLength() - if err != nil { - return err - } - r.blocks[topic] = make(map[int32]*offsetRequestBlock) - for j := 0; j < partitionCount; j++ { - partition, err := pd.getInt32() - if err != nil { - return err - } - block := &offsetRequestBlock{} - if err := block.decode(pd, version); err != nil { - return err - } - r.blocks[topic][partition] = block - } - } - return nil -} - -func (r *OffsetRequest) key() int16 { - return 2 -} - -func (r *OffsetRequest) version() int16 { - return r.Version -} - -func (r *OffsetRequest) requiredVersion() KafkaVersion { - switch r.Version { - case 1: - return V0_10_1_0 - default: - return MinVersion - } -} - -func (r *OffsetRequest) AddBlock(topic string, partitionID int32, time int64, maxOffsets int32) { - if r.blocks == nil { - r.blocks = make(map[string]map[int32]*offsetRequestBlock) - } - - if r.blocks[topic] == nil { - r.blocks[topic] = make(map[int32]*offsetRequestBlock) - } - - tmp := new(offsetRequestBlock) - tmp.time = time - if r.Version == 0 { - tmp.maxOffsets = maxOffsets - } - - r.blocks[topic][partitionID] = tmp -} diff --git a/vendor/github.com/Shopify/sarama/offset_response.go b/vendor/github.com/Shopify/sarama/offset_response.go deleted file mode 100644 index 8b2193f9a0b..00000000000 --- a/vendor/github.com/Shopify/sarama/offset_response.go +++ /dev/null @@ -1,174 +0,0 @@ -package sarama - -type OffsetResponseBlock struct { - Err KError - Offsets []int64 // Version 0 - Offset int64 // Version 1 - Timestamp int64 // Version 1 -} - -func (b *OffsetResponseBlock) decode(pd packetDecoder, version int16) (err error) { - tmp, err := pd.getInt16() - if err != nil { - return err - } - b.Err = KError(tmp) - - if version == 0 { - b.Offsets, err = pd.getInt64Array() - - return err - } - - b.Timestamp, err = pd.getInt64() - if err != nil { - return err - } - - b.Offset, err = pd.getInt64() - if err != nil { - return err - } - - // For backwards compatibility put the offset in the offsets array too - b.Offsets = []int64{b.Offset} - - return nil -} - -func (b *OffsetResponseBlock) encode(pe packetEncoder, version int16) (err error) { - pe.putInt16(int16(b.Err)) - - if version == 0 { - return pe.putInt64Array(b.Offsets) - } - - pe.putInt64(b.Timestamp) - pe.putInt64(b.Offset) - - return nil -} - -type OffsetResponse struct { - Version int16 - Blocks map[string]map[int32]*OffsetResponseBlock -} - -func (r *OffsetResponse) decode(pd packetDecoder, version int16) (err error) { - numTopics, err := pd.getArrayLength() - if err != nil { - return err - } - - r.Blocks = make(map[string]map[int32]*OffsetResponseBlock, numTopics) - for i := 0; i < numTopics; i++ { - name, err := pd.getString() - if err != nil { - return err - } - - numBlocks, err := pd.getArrayLength() - if err != nil { - return err - } - - r.Blocks[name] = make(map[int32]*OffsetResponseBlock, numBlocks) - - for j := 0; j < numBlocks; j++ { - id, err := pd.getInt32() - if err != nil { - return err - } - - block := new(OffsetResponseBlock) - err = block.decode(pd, version) - if err != nil { - return err - } - r.Blocks[name][id] = block - } - } - - return nil -} - -func (r *OffsetResponse) GetBlock(topic string, partition int32) *OffsetResponseBlock { - if r.Blocks == nil { - return nil - } - - if r.Blocks[topic] == nil { - return nil - } - - return r.Blocks[topic][partition] -} - -/* -// [0 0 0 1 ntopics -0 8 109 121 95 116 111 112 105 99 topic -0 0 0 1 npartitions -0 0 0 0 id -0 0 - -0 0 0 1 0 0 0 0 -0 1 1 1 0 0 0 1 -0 8 109 121 95 116 111 112 -105 99 0 0 0 1 0 0 -0 0 0 0 0 0 0 1 -0 0 0 0 0 1 1 1] - -*/ -func (r *OffsetResponse) encode(pe packetEncoder) (err error) { - if err = pe.putArrayLength(len(r.Blocks)); err != nil { - return err - } - - for topic, partitions := range r.Blocks { - if err = pe.putString(topic); err != nil { - return err - } - if err = pe.putArrayLength(len(partitions)); err != nil { - return err - } - for partition, block := range partitions { - pe.putInt32(partition) - if err = block.encode(pe, r.version()); err != nil { - return err - } - } - } - - return nil -} - -func (r *OffsetResponse) key() int16 { - return 2 -} - -func (r *OffsetResponse) version() int16 { - return r.Version -} - -func (r *OffsetResponse) requiredVersion() KafkaVersion { - switch r.Version { - case 1: - return V0_10_1_0 - default: - return MinVersion - } -} - -// testing API - -func (r *OffsetResponse) AddTopicPartition(topic string, partition int32, offset int64) { - if r.Blocks == nil { - r.Blocks = make(map[string]map[int32]*OffsetResponseBlock) - } - byTopic, ok := r.Blocks[topic] - if !ok { - byTopic = make(map[int32]*OffsetResponseBlock) - r.Blocks[topic] = byTopic - } - byTopic[partition] = &OffsetResponseBlock{Offsets: []int64{offset}, Offset: offset} -} diff --git a/vendor/github.com/Shopify/sarama/packet_decoder.go b/vendor/github.com/Shopify/sarama/packet_decoder.go deleted file mode 100644 index 74805ccbf53..00000000000 --- a/vendor/github.com/Shopify/sarama/packet_decoder.go +++ /dev/null @@ -1,60 +0,0 @@ -package sarama - -// PacketDecoder is the interface providing helpers for reading with Kafka's encoding rules. -// Types implementing Decoder only need to worry about calling methods like GetString, -// not about how a string is represented in Kafka. -type packetDecoder interface { - // Primitives - getInt8() (int8, error) - getInt16() (int16, error) - getInt32() (int32, error) - getInt64() (int64, error) - getVarint() (int64, error) - getArrayLength() (int, error) - getBool() (bool, error) - - // Collections - getBytes() ([]byte, error) - getVarintBytes() ([]byte, error) - getRawBytes(length int) ([]byte, error) - getString() (string, error) - getNullableString() (*string, error) - getInt32Array() ([]int32, error) - getInt64Array() ([]int64, error) - getStringArray() ([]string, error) - - // Subsets - remaining() int - getSubset(length int) (packetDecoder, error) - peek(offset, length int) (packetDecoder, error) // similar to getSubset, but it doesn't advance the offset - - // Stacks, see PushDecoder - push(in pushDecoder) error - pop() error -} - -// PushDecoder is the interface for decoding fields like CRCs and lengths where the validity -// of the field depends on what is after it in the packet. Start them with PacketDecoder.Push() where -// the actual value is located in the packet, then PacketDecoder.Pop() them when all the bytes they -// depend upon have been decoded. -type pushDecoder interface { - // Saves the offset into the input buffer as the location to actually read the calculated value when able. - saveOffset(in int) - - // Returns the length of data to reserve for the input of this encoder (eg 4 bytes for a CRC32). - reserveLength() int - - // Indicates that all required data is now available to calculate and check the field. - // SaveOffset is guaranteed to have been called first. The implementation should read ReserveLength() bytes - // of data from the saved offset, and verify it based on the data between the saved offset and curOffset. - check(curOffset int, buf []byte) error -} - -// dynamicPushDecoder extends the interface of pushDecoder for uses cases where the length of the -// fields itself is unknown until its value was decoded (for instance varint encoded length -// fields). -// During push, dynamicPushDecoder.decode() method will be called instead of reserveLength() -type dynamicPushDecoder interface { - pushDecoder - decoder -} diff --git a/vendor/github.com/Shopify/sarama/packet_encoder.go b/vendor/github.com/Shopify/sarama/packet_encoder.go deleted file mode 100644 index 67b8daed829..00000000000 --- a/vendor/github.com/Shopify/sarama/packet_encoder.go +++ /dev/null @@ -1,65 +0,0 @@ -package sarama - -import "github.com/rcrowley/go-metrics" - -// PacketEncoder is the interface providing helpers for writing with Kafka's encoding rules. -// Types implementing Encoder only need to worry about calling methods like PutString, -// not about how a string is represented in Kafka. -type packetEncoder interface { - // Primitives - putInt8(in int8) - putInt16(in int16) - putInt32(in int32) - putInt64(in int64) - putVarint(in int64) - putArrayLength(in int) error - putBool(in bool) - - // Collections - putBytes(in []byte) error - putVarintBytes(in []byte) error - putRawBytes(in []byte) error - putString(in string) error - putNullableString(in *string) error - putStringArray(in []string) error - putInt32Array(in []int32) error - putInt64Array(in []int64) error - - // Provide the current offset to record the batch size metric - offset() int - - // Stacks, see PushEncoder - push(in pushEncoder) - pop() error - - // To record metrics when provided - metricRegistry() metrics.Registry -} - -// PushEncoder is the interface for encoding fields like CRCs and lengths where the value -// of the field depends on what is encoded after it in the packet. Start them with PacketEncoder.Push() where -// the actual value is located in the packet, then PacketEncoder.Pop() them when all the bytes they -// depend upon have been written. -type pushEncoder interface { - // Saves the offset into the input buffer as the location to actually write the calculated value when able. - saveOffset(in int) - - // Returns the length of data to reserve for the output of this encoder (eg 4 bytes for a CRC32). - reserveLength() int - - // Indicates that all required data is now available to calculate and write the field. - // SaveOffset is guaranteed to have been called first. The implementation should write ReserveLength() bytes - // of data to the saved offset, based on the data between the saved offset and curOffset. - run(curOffset int, buf []byte) error -} - -// dynamicPushEncoder extends the interface of pushEncoder for uses cases where the length of the -// fields itself is unknown until its value was computed (for instance varint encoded length -// fields). -type dynamicPushEncoder interface { - pushEncoder - - // Called during pop() to adjust the length of the field. - // It should return the difference in bytes between the last computed length and current length. - adjustLength(currOffset int) int -} diff --git a/vendor/github.com/Shopify/sarama/partitioner.go b/vendor/github.com/Shopify/sarama/partitioner.go deleted file mode 100644 index 6a708e729ee..00000000000 --- a/vendor/github.com/Shopify/sarama/partitioner.go +++ /dev/null @@ -1,217 +0,0 @@ -package sarama - -import ( - "hash" - "hash/fnv" - "math/rand" - "time" -) - -// Partitioner is anything that, given a Kafka message and a number of partitions indexed [0...numPartitions-1], -// decides to which partition to send the message. RandomPartitioner, RoundRobinPartitioner and HashPartitioner are provided -// as simple default implementations. -type Partitioner interface { - // Partition takes a message and partition count and chooses a partition - Partition(message *ProducerMessage, numPartitions int32) (int32, error) - - // RequiresConsistency indicates to the user of the partitioner whether the - // mapping of key->partition is consistent or not. Specifically, if a - // partitioner requires consistency then it must be allowed to choose from all - // partitions (even ones known to be unavailable), and its choice must be - // respected by the caller. The obvious example is the HashPartitioner. - RequiresConsistency() bool -} - -// DynamicConsistencyPartitioner can optionally be implemented by Partitioners -// in order to allow more flexibility than is originally allowed by the -// RequiresConsistency method in the Partitioner interface. This allows -// partitioners to require consistency sometimes, but not all times. It's useful -// for, e.g., the HashPartitioner, which does not require consistency if the -// message key is nil. -type DynamicConsistencyPartitioner interface { - Partitioner - - // MessageRequiresConsistency is similar to Partitioner.RequiresConsistency, - // but takes in the message being partitioned so that the partitioner can - // make a per-message determination. - MessageRequiresConsistency(message *ProducerMessage) bool -} - -// PartitionerConstructor is the type for a function capable of constructing new Partitioners. -type PartitionerConstructor func(topic string) Partitioner - -type manualPartitioner struct{} - -// HashPartitionOption lets you modify default values of the partitioner -type HashPartitionerOption func(*hashPartitioner) - -// WithAbsFirst means that the partitioner handles absolute values -// in the same way as the reference Java implementation -func WithAbsFirst() HashPartitionerOption { - return func(hp *hashPartitioner) { - hp.referenceAbs = true - } -} - -// WithCustomHashFunction lets you specify what hash function to use for the partitioning -func WithCustomHashFunction(hasher func() hash.Hash32) HashPartitionerOption { - return func(hp *hashPartitioner) { - hp.hasher = hasher() - } -} - -// WithCustomFallbackPartitioner lets you specify what HashPartitioner should be used in case a Distribution Key is empty -func WithCustomFallbackPartitioner(randomHP *hashPartitioner) HashPartitionerOption { - return func(hp *hashPartitioner) { - hp.random = hp - } -} - -// NewManualPartitioner returns a Partitioner which uses the partition manually set in the provided -// ProducerMessage's Partition field as the partition to produce to. -func NewManualPartitioner(topic string) Partitioner { - return new(manualPartitioner) -} - -func (p *manualPartitioner) Partition(message *ProducerMessage, numPartitions int32) (int32, error) { - return message.Partition, nil -} - -func (p *manualPartitioner) RequiresConsistency() bool { - return true -} - -type randomPartitioner struct { - generator *rand.Rand -} - -// NewRandomPartitioner returns a Partitioner which chooses a random partition each time. -func NewRandomPartitioner(topic string) Partitioner { - p := new(randomPartitioner) - p.generator = rand.New(rand.NewSource(time.Now().UTC().UnixNano())) - return p -} - -func (p *randomPartitioner) Partition(message *ProducerMessage, numPartitions int32) (int32, error) { - return int32(p.generator.Intn(int(numPartitions))), nil -} - -func (p *randomPartitioner) RequiresConsistency() bool { - return false -} - -type roundRobinPartitioner struct { - partition int32 -} - -// NewRoundRobinPartitioner returns a Partitioner which walks through the available partitions one at a time. -func NewRoundRobinPartitioner(topic string) Partitioner { - return &roundRobinPartitioner{} -} - -func (p *roundRobinPartitioner) Partition(message *ProducerMessage, numPartitions int32) (int32, error) { - if p.partition >= numPartitions { - p.partition = 0 - } - ret := p.partition - p.partition++ - return ret, nil -} - -func (p *roundRobinPartitioner) RequiresConsistency() bool { - return false -} - -type hashPartitioner struct { - random Partitioner - hasher hash.Hash32 - referenceAbs bool -} - -// NewCustomHashPartitioner is a wrapper around NewHashPartitioner, allowing the use of custom hasher. -// The argument is a function providing the instance, implementing the hash.Hash32 interface. This is to ensure that -// each partition dispatcher gets its own hasher, to avoid concurrency issues by sharing an instance. -func NewCustomHashPartitioner(hasher func() hash.Hash32) PartitionerConstructor { - return func(topic string) Partitioner { - p := new(hashPartitioner) - p.random = NewRandomPartitioner(topic) - p.hasher = hasher() - p.referenceAbs = false - return p - } -} - -// NewCustomPartitioner creates a default Partitioner but lets you specify the behavior of each component via options -func NewCustomPartitioner(options ...HashPartitionerOption) PartitionerConstructor { - return func(topic string) Partitioner { - p := new(hashPartitioner) - p.random = NewRandomPartitioner(topic) - p.hasher = fnv.New32a() - p.referenceAbs = false - for _, option := range options { - option(p) - } - return p - } -} - -// NewHashPartitioner returns a Partitioner which behaves as follows. If the message's key is nil then a -// random partition is chosen. Otherwise the FNV-1a hash of the encoded bytes of the message key is used, -// modulus the number of partitions. This ensures that messages with the same key always end up on the -// same partition. -func NewHashPartitioner(topic string) Partitioner { - p := new(hashPartitioner) - p.random = NewRandomPartitioner(topic) - p.hasher = fnv.New32a() - p.referenceAbs = false - return p -} - -// NewReferenceHashPartitioner is like NewHashPartitioner except that it handles absolute values -// in the same way as the reference Java implementation. NewHashPartitioner was supposed to do -// that but it had a mistake and now there are people depending on both behaviours. This will -// all go away on the next major version bump. -func NewReferenceHashPartitioner(topic string) Partitioner { - p := new(hashPartitioner) - p.random = NewRandomPartitioner(topic) - p.hasher = fnv.New32a() - p.referenceAbs = true - return p -} - -func (p *hashPartitioner) Partition(message *ProducerMessage, numPartitions int32) (int32, error) { - if message.Key == nil { - return p.random.Partition(message, numPartitions) - } - bytes, err := message.Key.Encode() - if err != nil { - return -1, err - } - p.hasher.Reset() - _, err = p.hasher.Write(bytes) - if err != nil { - return -1, err - } - var partition int32 - // Turns out we were doing our absolute value in a subtly different way from the upstream - // implementation, but now we need to maintain backwards compat for people who started using - // the old version; if referenceAbs is set we are compatible with the reference java client - // but not past Sarama versions - if p.referenceAbs { - partition = (int32(p.hasher.Sum32()) & 0x7fffffff) % numPartitions - } else { - partition = int32(p.hasher.Sum32()) % numPartitions - if partition < 0 { - partition = -partition - } - } - return partition, nil -} - -func (p *hashPartitioner) RequiresConsistency() bool { - return true -} - -func (p *hashPartitioner) MessageRequiresConsistency(message *ProducerMessage) bool { - return message.Key != nil -} diff --git a/vendor/github.com/Shopify/sarama/prep_encoder.go b/vendor/github.com/Shopify/sarama/prep_encoder.go deleted file mode 100644 index b633cd15111..00000000000 --- a/vendor/github.com/Shopify/sarama/prep_encoder.go +++ /dev/null @@ -1,153 +0,0 @@ -package sarama - -import ( - "encoding/binary" - "fmt" - "math" - - "github.com/rcrowley/go-metrics" -) - -type prepEncoder struct { - stack []pushEncoder - length int -} - -// primitives - -func (pe *prepEncoder) putInt8(in int8) { - pe.length++ -} - -func (pe *prepEncoder) putInt16(in int16) { - pe.length += 2 -} - -func (pe *prepEncoder) putInt32(in int32) { - pe.length += 4 -} - -func (pe *prepEncoder) putInt64(in int64) { - pe.length += 8 -} - -func (pe *prepEncoder) putVarint(in int64) { - var buf [binary.MaxVarintLen64]byte - pe.length += binary.PutVarint(buf[:], in) -} - -func (pe *prepEncoder) putArrayLength(in int) error { - if in > math.MaxInt32 { - return PacketEncodingError{fmt.Sprintf("array too long (%d)", in)} - } - pe.length += 4 - return nil -} - -func (pe *prepEncoder) putBool(in bool) { - pe.length++ -} - -// arrays - -func (pe *prepEncoder) putBytes(in []byte) error { - pe.length += 4 - if in == nil { - return nil - } - return pe.putRawBytes(in) -} - -func (pe *prepEncoder) putVarintBytes(in []byte) error { - if in == nil { - pe.putVarint(-1) - return nil - } - pe.putVarint(int64(len(in))) - return pe.putRawBytes(in) -} - -func (pe *prepEncoder) putRawBytes(in []byte) error { - if len(in) > math.MaxInt32 { - return PacketEncodingError{fmt.Sprintf("byteslice too long (%d)", len(in))} - } - pe.length += len(in) - return nil -} - -func (pe *prepEncoder) putNullableString(in *string) error { - if in == nil { - pe.length += 2 - return nil - } - return pe.putString(*in) -} - -func (pe *prepEncoder) putString(in string) error { - pe.length += 2 - if len(in) > math.MaxInt16 { - return PacketEncodingError{fmt.Sprintf("string too long (%d)", len(in))} - } - pe.length += len(in) - return nil -} - -func (pe *prepEncoder) putStringArray(in []string) error { - err := pe.putArrayLength(len(in)) - if err != nil { - return err - } - - for _, str := range in { - if err := pe.putString(str); err != nil { - return err - } - } - - return nil -} - -func (pe *prepEncoder) putInt32Array(in []int32) error { - err := pe.putArrayLength(len(in)) - if err != nil { - return err - } - pe.length += 4 * len(in) - return nil -} - -func (pe *prepEncoder) putInt64Array(in []int64) error { - err := pe.putArrayLength(len(in)) - if err != nil { - return err - } - pe.length += 8 * len(in) - return nil -} - -func (pe *prepEncoder) offset() int { - return pe.length -} - -// stackable - -func (pe *prepEncoder) push(in pushEncoder) { - in.saveOffset(pe.length) - pe.length += in.reserveLength() - pe.stack = append(pe.stack, in) -} - -func (pe *prepEncoder) pop() error { - in := pe.stack[len(pe.stack)-1] - pe.stack = pe.stack[:len(pe.stack)-1] - if dpe, ok := in.(dynamicPushEncoder); ok { - pe.length += dpe.adjustLength(pe.length) - } - - return nil -} - -// we do not record metrics during the prep encoder pass -func (pe *prepEncoder) metricRegistry() metrics.Registry { - return nil -} diff --git a/vendor/github.com/Shopify/sarama/produce_request.go b/vendor/github.com/Shopify/sarama/produce_request.go deleted file mode 100644 index 0c755d02b64..00000000000 --- a/vendor/github.com/Shopify/sarama/produce_request.go +++ /dev/null @@ -1,252 +0,0 @@ -package sarama - -import "github.com/rcrowley/go-metrics" - -// RequiredAcks is used in Produce Requests to tell the broker how many replica acknowledgements -// it must see before responding. Any of the constants defined here are valid. On broker versions -// prior to 0.8.2.0 any other positive int16 is also valid (the broker will wait for that many -// acknowledgements) but in 0.8.2.0 and later this will raise an exception (it has been replaced -// by setting the `min.isr` value in the brokers configuration). -type RequiredAcks int16 - -const ( - // NoResponse doesn't send any response, the TCP ACK is all you get. - NoResponse RequiredAcks = 0 - // WaitForLocal waits for only the local commit to succeed before responding. - WaitForLocal RequiredAcks = 1 - // WaitForAll waits for all in-sync replicas to commit before responding. - // The minimum number of in-sync replicas is configured on the broker via - // the `min.insync.replicas` configuration key. - WaitForAll RequiredAcks = -1 -) - -type ProduceRequest struct { - TransactionalID *string - RequiredAcks RequiredAcks - Timeout int32 - Version int16 // v1 requires Kafka 0.9, v2 requires Kafka 0.10, v3 requires Kafka 0.11 - records map[string]map[int32]Records -} - -func updateMsgSetMetrics(msgSet *MessageSet, compressionRatioMetric metrics.Histogram, - topicCompressionRatioMetric metrics.Histogram) int64 { - var topicRecordCount int64 - for _, messageBlock := range msgSet.Messages { - // Is this a fake "message" wrapping real messages? - if messageBlock.Msg.Set != nil { - topicRecordCount += int64(len(messageBlock.Msg.Set.Messages)) - } else { - // A single uncompressed message - topicRecordCount++ - } - // Better be safe than sorry when computing the compression ratio - if messageBlock.Msg.compressedSize != 0 { - compressionRatio := float64(len(messageBlock.Msg.Value)) / - float64(messageBlock.Msg.compressedSize) - // Histogram do not support decimal values, let's multiple it by 100 for better precision - intCompressionRatio := int64(100 * compressionRatio) - compressionRatioMetric.Update(intCompressionRatio) - topicCompressionRatioMetric.Update(intCompressionRatio) - } - } - return topicRecordCount -} - -func updateBatchMetrics(recordBatch *RecordBatch, compressionRatioMetric metrics.Histogram, - topicCompressionRatioMetric metrics.Histogram) int64 { - if recordBatch.compressedRecords != nil { - compressionRatio := int64(float64(recordBatch.recordsLen) / float64(len(recordBatch.compressedRecords)) * 100) - compressionRatioMetric.Update(compressionRatio) - topicCompressionRatioMetric.Update(compressionRatio) - } - - return int64(len(recordBatch.Records)) -} - -func (r *ProduceRequest) encode(pe packetEncoder) error { - if r.Version >= 3 { - if err := pe.putNullableString(r.TransactionalID); err != nil { - return err - } - } - pe.putInt16(int16(r.RequiredAcks)) - pe.putInt32(r.Timeout) - metricRegistry := pe.metricRegistry() - var batchSizeMetric metrics.Histogram - var compressionRatioMetric metrics.Histogram - if metricRegistry != nil { - batchSizeMetric = getOrRegisterHistogram("batch-size", metricRegistry) - compressionRatioMetric = getOrRegisterHistogram("compression-ratio", metricRegistry) - } - totalRecordCount := int64(0) - - err := pe.putArrayLength(len(r.records)) - if err != nil { - return err - } - - for topic, partitions := range r.records { - err = pe.putString(topic) - if err != nil { - return err - } - err = pe.putArrayLength(len(partitions)) - if err != nil { - return err - } - topicRecordCount := int64(0) - var topicCompressionRatioMetric metrics.Histogram - if metricRegistry != nil { - topicCompressionRatioMetric = getOrRegisterTopicHistogram("compression-ratio", topic, metricRegistry) - } - for id, records := range partitions { - startOffset := pe.offset() - pe.putInt32(id) - pe.push(&lengthField{}) - err = records.encode(pe) - if err != nil { - return err - } - err = pe.pop() - if err != nil { - return err - } - if metricRegistry != nil { - if r.Version >= 3 { - topicRecordCount += updateBatchMetrics(records.RecordBatch, compressionRatioMetric, topicCompressionRatioMetric) - } else { - topicRecordCount += updateMsgSetMetrics(records.MsgSet, compressionRatioMetric, topicCompressionRatioMetric) - } - batchSize := int64(pe.offset() - startOffset) - batchSizeMetric.Update(batchSize) - getOrRegisterTopicHistogram("batch-size", topic, metricRegistry).Update(batchSize) - } - } - if topicRecordCount > 0 { - getOrRegisterTopicMeter("record-send-rate", topic, metricRegistry).Mark(topicRecordCount) - getOrRegisterTopicHistogram("records-per-request", topic, metricRegistry).Update(topicRecordCount) - totalRecordCount += topicRecordCount - } - } - if totalRecordCount > 0 { - metrics.GetOrRegisterMeter("record-send-rate", metricRegistry).Mark(totalRecordCount) - getOrRegisterHistogram("records-per-request", metricRegistry).Update(totalRecordCount) - } - - return nil -} - -func (r *ProduceRequest) decode(pd packetDecoder, version int16) error { - r.Version = version - - if version >= 3 { - id, err := pd.getNullableString() - if err != nil { - return err - } - r.TransactionalID = id - } - requiredAcks, err := pd.getInt16() - if err != nil { - return err - } - r.RequiredAcks = RequiredAcks(requiredAcks) - if r.Timeout, err = pd.getInt32(); err != nil { - return err - } - topicCount, err := pd.getArrayLength() - if err != nil { - return err - } - if topicCount == 0 { - return nil - } - - r.records = make(map[string]map[int32]Records) - for i := 0; i < topicCount; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - partitionCount, err := pd.getArrayLength() - if err != nil { - return err - } - r.records[topic] = make(map[int32]Records) - - for j := 0; j < partitionCount; j++ { - partition, err := pd.getInt32() - if err != nil { - return err - } - size, err := pd.getInt32() - if err != nil { - return err - } - recordsDecoder, err := pd.getSubset(int(size)) - if err != nil { - return err - } - var records Records - if err := records.decode(recordsDecoder); err != nil { - return err - } - r.records[topic][partition] = records - } - } - - return nil -} - -func (r *ProduceRequest) key() int16 { - return 0 -} - -func (r *ProduceRequest) version() int16 { - return r.Version -} - -func (r *ProduceRequest) requiredVersion() KafkaVersion { - switch r.Version { - case 1: - return V0_9_0_0 - case 2: - return V0_10_0_0 - case 3: - return V0_11_0_0 - default: - return MinVersion - } -} - -func (r *ProduceRequest) ensureRecords(topic string, partition int32) { - if r.records == nil { - r.records = make(map[string]map[int32]Records) - } - - if r.records[topic] == nil { - r.records[topic] = make(map[int32]Records) - } -} - -func (r *ProduceRequest) AddMessage(topic string, partition int32, msg *Message) { - r.ensureRecords(topic, partition) - set := r.records[topic][partition].MsgSet - - if set == nil { - set = new(MessageSet) - r.records[topic][partition] = newLegacyRecords(set) - } - - set.addMessage(msg) -} - -func (r *ProduceRequest) AddSet(topic string, partition int32, set *MessageSet) { - r.ensureRecords(topic, partition) - r.records[topic][partition] = newLegacyRecords(set) -} - -func (r *ProduceRequest) AddBatch(topic string, partition int32, batch *RecordBatch) { - r.ensureRecords(topic, partition) - r.records[topic][partition] = newDefaultRecords(batch) -} diff --git a/vendor/github.com/Shopify/sarama/produce_response.go b/vendor/github.com/Shopify/sarama/produce_response.go deleted file mode 100644 index 667e34c661b..00000000000 --- a/vendor/github.com/Shopify/sarama/produce_response.go +++ /dev/null @@ -1,183 +0,0 @@ -package sarama - -import ( - "fmt" - "time" -) - -type ProduceResponseBlock struct { - Err KError - Offset int64 - // only provided if Version >= 2 and the broker is configured with `LogAppendTime` - Timestamp time.Time -} - -func (b *ProduceResponseBlock) decode(pd packetDecoder, version int16) (err error) { - tmp, err := pd.getInt16() - if err != nil { - return err - } - b.Err = KError(tmp) - - b.Offset, err = pd.getInt64() - if err != nil { - return err - } - - if version >= 2 { - if millis, err := pd.getInt64(); err != nil { - return err - } else if millis != -1 { - b.Timestamp = time.Unix(millis/1000, (millis%1000)*int64(time.Millisecond)) - } - } - - return nil -} - -func (b *ProduceResponseBlock) encode(pe packetEncoder, version int16) (err error) { - pe.putInt16(int16(b.Err)) - pe.putInt64(b.Offset) - - if version >= 2 { - timestamp := int64(-1) - if !b.Timestamp.Before(time.Unix(0, 0)) { - timestamp = b.Timestamp.UnixNano() / int64(time.Millisecond) - } else if !b.Timestamp.IsZero() { - return PacketEncodingError{fmt.Sprintf("invalid timestamp (%v)", b.Timestamp)} - } - pe.putInt64(timestamp) - } - - return nil -} - -type ProduceResponse struct { - Blocks map[string]map[int32]*ProduceResponseBlock - Version int16 - ThrottleTime time.Duration // only provided if Version >= 1 -} - -func (r *ProduceResponse) decode(pd packetDecoder, version int16) (err error) { - r.Version = version - - numTopics, err := pd.getArrayLength() - if err != nil { - return err - } - - r.Blocks = make(map[string]map[int32]*ProduceResponseBlock, numTopics) - for i := 0; i < numTopics; i++ { - name, err := pd.getString() - if err != nil { - return err - } - - numBlocks, err := pd.getArrayLength() - if err != nil { - return err - } - - r.Blocks[name] = make(map[int32]*ProduceResponseBlock, numBlocks) - - for j := 0; j < numBlocks; j++ { - id, err := pd.getInt32() - if err != nil { - return err - } - - block := new(ProduceResponseBlock) - err = block.decode(pd, version) - if err != nil { - return err - } - r.Blocks[name][id] = block - } - } - - if r.Version >= 1 { - millis, err := pd.getInt32() - if err != nil { - return err - } - - r.ThrottleTime = time.Duration(millis) * time.Millisecond - } - - return nil -} - -func (r *ProduceResponse) encode(pe packetEncoder) error { - err := pe.putArrayLength(len(r.Blocks)) - if err != nil { - return err - } - for topic, partitions := range r.Blocks { - err = pe.putString(topic) - if err != nil { - return err - } - err = pe.putArrayLength(len(partitions)) - if err != nil { - return err - } - for id, prb := range partitions { - pe.putInt32(id) - err = prb.encode(pe, r.Version) - if err != nil { - return err - } - } - } - if r.Version >= 1 { - pe.putInt32(int32(r.ThrottleTime / time.Millisecond)) - } - return nil -} - -func (r *ProduceResponse) key() int16 { - return 0 -} - -func (r *ProduceResponse) version() int16 { - return r.Version -} - -func (r *ProduceResponse) requiredVersion() KafkaVersion { - switch r.Version { - case 1: - return V0_9_0_0 - case 2: - return V0_10_0_0 - case 3: - return V0_11_0_0 - default: - return MinVersion - } -} - -func (r *ProduceResponse) GetBlock(topic string, partition int32) *ProduceResponseBlock { - if r.Blocks == nil { - return nil - } - - if r.Blocks[topic] == nil { - return nil - } - - return r.Blocks[topic][partition] -} - -// Testing API - -func (r *ProduceResponse) AddTopicPartition(topic string, partition int32, err KError) { - if r.Blocks == nil { - r.Blocks = make(map[string]map[int32]*ProduceResponseBlock) - } - byTopic, ok := r.Blocks[topic] - if !ok { - byTopic = make(map[int32]*ProduceResponseBlock) - r.Blocks[topic] = byTopic - } - byTopic[partition] = &ProduceResponseBlock{Err: err} -} diff --git a/vendor/github.com/Shopify/sarama/produce_set.go b/vendor/github.com/Shopify/sarama/produce_set.go deleted file mode 100644 index 13be2b3c92b..00000000000 --- a/vendor/github.com/Shopify/sarama/produce_set.go +++ /dev/null @@ -1,252 +0,0 @@ -package sarama - -import ( - "encoding/binary" - "time" -) - -type partitionSet struct { - msgs []*ProducerMessage - recordsToSend Records - bufferBytes int -} - -type produceSet struct { - parent *asyncProducer - msgs map[string]map[int32]*partitionSet - - bufferBytes int - bufferCount int -} - -func newProduceSet(parent *asyncProducer) *produceSet { - return &produceSet{ - msgs: make(map[string]map[int32]*partitionSet), - parent: parent, - } -} - -func (ps *produceSet) add(msg *ProducerMessage) error { - var err error - var key, val []byte - - if msg.Key != nil { - if key, err = msg.Key.Encode(); err != nil { - return err - } - } - - if msg.Value != nil { - if val, err = msg.Value.Encode(); err != nil { - return err - } - } - - timestamp := msg.Timestamp - if msg.Timestamp.IsZero() { - timestamp = time.Now() - } - - partitions := ps.msgs[msg.Topic] - if partitions == nil { - partitions = make(map[int32]*partitionSet) - ps.msgs[msg.Topic] = partitions - } - - var size int - - set := partitions[msg.Partition] - if set == nil { - if ps.parent.conf.Version.IsAtLeast(V0_11_0_0) { - batch := &RecordBatch{ - FirstTimestamp: timestamp, - Version: 2, - ProducerID: -1, /* No producer id */ - Codec: ps.parent.conf.Producer.Compression, - CompressionLevel: ps.parent.conf.Producer.CompressionLevel, - } - set = &partitionSet{recordsToSend: newDefaultRecords(batch)} - size = recordBatchOverhead - } else { - set = &partitionSet{recordsToSend: newLegacyRecords(new(MessageSet))} - } - partitions[msg.Partition] = set - } - - set.msgs = append(set.msgs, msg) - if ps.parent.conf.Version.IsAtLeast(V0_11_0_0) { - // We are being conservative here to avoid having to prep encode the record - size += maximumRecordOverhead - rec := &Record{ - Key: key, - Value: val, - TimestampDelta: timestamp.Sub(set.recordsToSend.RecordBatch.FirstTimestamp), - } - size += len(key) + len(val) - if len(msg.Headers) > 0 { - rec.Headers = make([]*RecordHeader, len(msg.Headers)) - for i := range msg.Headers { - rec.Headers[i] = &msg.Headers[i] - size += len(rec.Headers[i].Key) + len(rec.Headers[i].Value) + 2*binary.MaxVarintLen32 - } - } - set.recordsToSend.RecordBatch.addRecord(rec) - } else { - msgToSend := &Message{Codec: CompressionNone, Key: key, Value: val} - if ps.parent.conf.Version.IsAtLeast(V0_10_0_0) { - msgToSend.Timestamp = timestamp - msgToSend.Version = 1 - } - set.recordsToSend.MsgSet.addMessage(msgToSend) - size = producerMessageOverhead + len(key) + len(val) - } - - set.bufferBytes += size - ps.bufferBytes += size - ps.bufferCount++ - - return nil -} - -func (ps *produceSet) buildRequest() *ProduceRequest { - req := &ProduceRequest{ - RequiredAcks: ps.parent.conf.Producer.RequiredAcks, - Timeout: int32(ps.parent.conf.Producer.Timeout / time.Millisecond), - } - if ps.parent.conf.Version.IsAtLeast(V0_10_0_0) { - req.Version = 2 - } - if ps.parent.conf.Version.IsAtLeast(V0_11_0_0) { - req.Version = 3 - } - - for topic, partitionSet := range ps.msgs { - for partition, set := range partitionSet { - if req.Version >= 3 { - // If the API version we're hitting is 3 or greater, we need to calculate - // offsets for each record in the batch relative to FirstOffset. - // Additionally, we must set LastOffsetDelta to the value of the last offset - // in the batch. Since the OffsetDelta of the first record is 0, we know that the - // final record of any batch will have an offset of (# of records in batch) - 1. - // (See https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol#AGuideToTheKafkaProtocol-Messagesets - // under the RecordBatch section for details.) - rb := set.recordsToSend.RecordBatch - if len(rb.Records) > 0 { - rb.LastOffsetDelta = int32(len(rb.Records) - 1) - for i, record := range rb.Records { - record.OffsetDelta = int64(i) - } - } - - req.AddBatch(topic, partition, rb) - continue - } - if ps.parent.conf.Producer.Compression == CompressionNone { - req.AddSet(topic, partition, set.recordsToSend.MsgSet) - } else { - // When compression is enabled, the entire set for each partition is compressed - // and sent as the payload of a single fake "message" with the appropriate codec - // set and no key. When the server sees a message with a compression codec, it - // decompresses the payload and treats the result as its message set. - - if ps.parent.conf.Version.IsAtLeast(V0_10_0_0) { - // If our version is 0.10 or later, assign relative offsets - // to the inner messages. This lets the broker avoid - // recompressing the message set. - // (See https://cwiki.apache.org/confluence/display/KAFKA/KIP-31+-+Move+to+relative+offsets+in+compressed+message+sets - // for details on relative offsets.) - for i, msg := range set.recordsToSend.MsgSet.Messages { - msg.Offset = int64(i) - } - } - payload, err := encode(set.recordsToSend.MsgSet, ps.parent.conf.MetricRegistry) - if err != nil { - Logger.Println(err) // if this happens, it's basically our fault. - panic(err) - } - compMsg := &Message{ - Codec: ps.parent.conf.Producer.Compression, - CompressionLevel: ps.parent.conf.Producer.CompressionLevel, - Key: nil, - Value: payload, - Set: set.recordsToSend.MsgSet, // Provide the underlying message set for accurate metrics - } - if ps.parent.conf.Version.IsAtLeast(V0_10_0_0) { - compMsg.Version = 1 - compMsg.Timestamp = set.recordsToSend.MsgSet.Messages[0].Msg.Timestamp - } - req.AddMessage(topic, partition, compMsg) - } - } - } - - return req -} - -func (ps *produceSet) eachPartition(cb func(topic string, partition int32, msgs []*ProducerMessage)) { - for topic, partitionSet := range ps.msgs { - for partition, set := range partitionSet { - cb(topic, partition, set.msgs) - } - } -} - -func (ps *produceSet) dropPartition(topic string, partition int32) []*ProducerMessage { - if ps.msgs[topic] == nil { - return nil - } - set := ps.msgs[topic][partition] - if set == nil { - return nil - } - ps.bufferBytes -= set.bufferBytes - ps.bufferCount -= len(set.msgs) - delete(ps.msgs[topic], partition) - return set.msgs -} - -func (ps *produceSet) wouldOverflow(msg *ProducerMessage) bool { - version := 1 - if ps.parent.conf.Version.IsAtLeast(V0_11_0_0) { - version = 2 - } - - switch { - // Would we overflow our maximum possible size-on-the-wire? 10KiB is arbitrary overhead for safety. - case ps.bufferBytes+msg.byteSize(version) >= int(MaxRequestSize-(10*1024)): - return true - // Would we overflow the size-limit of a compressed message-batch for this partition? - case ps.parent.conf.Producer.Compression != CompressionNone && - ps.msgs[msg.Topic] != nil && ps.msgs[msg.Topic][msg.Partition] != nil && - ps.msgs[msg.Topic][msg.Partition].bufferBytes+msg.byteSize(version) >= ps.parent.conf.Producer.MaxMessageBytes: - return true - // Would we overflow simply in number of messages? - case ps.parent.conf.Producer.Flush.MaxMessages > 0 && ps.bufferCount >= ps.parent.conf.Producer.Flush.MaxMessages: - return true - default: - return false - } -} - -func (ps *produceSet) readyToFlush() bool { - switch { - // If we don't have any messages, nothing else matters - case ps.empty(): - return false - // If all three config values are 0, we always flush as-fast-as-possible - case ps.parent.conf.Producer.Flush.Frequency == 0 && ps.parent.conf.Producer.Flush.Bytes == 0 && ps.parent.conf.Producer.Flush.Messages == 0: - return true - // If we've passed the message trigger-point - case ps.parent.conf.Producer.Flush.Messages > 0 && ps.bufferCount >= ps.parent.conf.Producer.Flush.Messages: - return true - // If we've passed the byte trigger-point - case ps.parent.conf.Producer.Flush.Bytes > 0 && ps.bufferBytes >= ps.parent.conf.Producer.Flush.Bytes: - return true - default: - return false - } -} - -func (ps *produceSet) empty() bool { - return ps.bufferCount == 0 -} diff --git a/vendor/github.com/Shopify/sarama/real_decoder.go b/vendor/github.com/Shopify/sarama/real_decoder.go deleted file mode 100644 index 23045e7d33a..00000000000 --- a/vendor/github.com/Shopify/sarama/real_decoder.go +++ /dev/null @@ -1,324 +0,0 @@ -package sarama - -import ( - "encoding/binary" - "math" -) - -var errInvalidArrayLength = PacketDecodingError{"invalid array length"} -var errInvalidByteSliceLength = PacketDecodingError{"invalid byteslice length"} -var errInvalidByteSliceLengthType = PacketDecodingError{"invalid byteslice length type"} -var errInvalidStringLength = PacketDecodingError{"invalid string length"} -var errInvalidSubsetSize = PacketDecodingError{"invalid subset size"} -var errVarintOverflow = PacketDecodingError{"varint overflow"} -var errInvalidBool = PacketDecodingError{"invalid bool"} - -type realDecoder struct { - raw []byte - off int - stack []pushDecoder -} - -// primitives - -func (rd *realDecoder) getInt8() (int8, error) { - if rd.remaining() < 1 { - rd.off = len(rd.raw) - return -1, ErrInsufficientData - } - tmp := int8(rd.raw[rd.off]) - rd.off++ - return tmp, nil -} - -func (rd *realDecoder) getInt16() (int16, error) { - if rd.remaining() < 2 { - rd.off = len(rd.raw) - return -1, ErrInsufficientData - } - tmp := int16(binary.BigEndian.Uint16(rd.raw[rd.off:])) - rd.off += 2 - return tmp, nil -} - -func (rd *realDecoder) getInt32() (int32, error) { - if rd.remaining() < 4 { - rd.off = len(rd.raw) - return -1, ErrInsufficientData - } - tmp := int32(binary.BigEndian.Uint32(rd.raw[rd.off:])) - rd.off += 4 - return tmp, nil -} - -func (rd *realDecoder) getInt64() (int64, error) { - if rd.remaining() < 8 { - rd.off = len(rd.raw) - return -1, ErrInsufficientData - } - tmp := int64(binary.BigEndian.Uint64(rd.raw[rd.off:])) - rd.off += 8 - return tmp, nil -} - -func (rd *realDecoder) getVarint() (int64, error) { - tmp, n := binary.Varint(rd.raw[rd.off:]) - if n == 0 { - rd.off = len(rd.raw) - return -1, ErrInsufficientData - } - if n < 0 { - rd.off -= n - return -1, errVarintOverflow - } - rd.off += n - return tmp, nil -} - -func (rd *realDecoder) getArrayLength() (int, error) { - if rd.remaining() < 4 { - rd.off = len(rd.raw) - return -1, ErrInsufficientData - } - tmp := int(int32(binary.BigEndian.Uint32(rd.raw[rd.off:]))) - rd.off += 4 - if tmp > rd.remaining() { - rd.off = len(rd.raw) - return -1, ErrInsufficientData - } else if tmp > 2*math.MaxUint16 { - return -1, errInvalidArrayLength - } - return tmp, nil -} - -func (rd *realDecoder) getBool() (bool, error) { - b, err := rd.getInt8() - if err != nil || b == 0 { - return false, err - } - if b != 1 { - return false, errInvalidBool - } - return true, nil -} - -// collections - -func (rd *realDecoder) getBytes() ([]byte, error) { - tmp, err := rd.getInt32() - if err != nil { - return nil, err - } - if tmp == -1 { - return nil, nil - } - - return rd.getRawBytes(int(tmp)) -} - -func (rd *realDecoder) getVarintBytes() ([]byte, error) { - tmp, err := rd.getVarint() - if err != nil { - return nil, err - } - if tmp == -1 { - return nil, nil - } - - return rd.getRawBytes(int(tmp)) -} - -func (rd *realDecoder) getStringLength() (int, error) { - length, err := rd.getInt16() - if err != nil { - return 0, err - } - - n := int(length) - - switch { - case n < -1: - return 0, errInvalidStringLength - case n > rd.remaining(): - rd.off = len(rd.raw) - return 0, ErrInsufficientData - } - - return n, nil -} - -func (rd *realDecoder) getString() (string, error) { - n, err := rd.getStringLength() - if err != nil || n == -1 { - return "", err - } - - tmpStr := string(rd.raw[rd.off : rd.off+n]) - rd.off += n - return tmpStr, nil -} - -func (rd *realDecoder) getNullableString() (*string, error) { - n, err := rd.getStringLength() - if err != nil || n == -1 { - return nil, err - } - - tmpStr := string(rd.raw[rd.off : rd.off+n]) - rd.off += n - return &tmpStr, err -} - -func (rd *realDecoder) getInt32Array() ([]int32, error) { - if rd.remaining() < 4 { - rd.off = len(rd.raw) - return nil, ErrInsufficientData - } - n := int(binary.BigEndian.Uint32(rd.raw[rd.off:])) - rd.off += 4 - - if rd.remaining() < 4*n { - rd.off = len(rd.raw) - return nil, ErrInsufficientData - } - - if n == 0 { - return nil, nil - } - - if n < 0 { - return nil, errInvalidArrayLength - } - - ret := make([]int32, n) - for i := range ret { - ret[i] = int32(binary.BigEndian.Uint32(rd.raw[rd.off:])) - rd.off += 4 - } - return ret, nil -} - -func (rd *realDecoder) getInt64Array() ([]int64, error) { - if rd.remaining() < 4 { - rd.off = len(rd.raw) - return nil, ErrInsufficientData - } - n := int(binary.BigEndian.Uint32(rd.raw[rd.off:])) - rd.off += 4 - - if rd.remaining() < 8*n { - rd.off = len(rd.raw) - return nil, ErrInsufficientData - } - - if n == 0 { - return nil, nil - } - - if n < 0 { - return nil, errInvalidArrayLength - } - - ret := make([]int64, n) - for i := range ret { - ret[i] = int64(binary.BigEndian.Uint64(rd.raw[rd.off:])) - rd.off += 8 - } - return ret, nil -} - -func (rd *realDecoder) getStringArray() ([]string, error) { - if rd.remaining() < 4 { - rd.off = len(rd.raw) - return nil, ErrInsufficientData - } - n := int(binary.BigEndian.Uint32(rd.raw[rd.off:])) - rd.off += 4 - - if n == 0 { - return nil, nil - } - - if n < 0 { - return nil, errInvalidArrayLength - } - - ret := make([]string, n) - for i := range ret { - str, err := rd.getString() - if err != nil { - return nil, err - } - - ret[i] = str - } - return ret, nil -} - -// subsets - -func (rd *realDecoder) remaining() int { - return len(rd.raw) - rd.off -} - -func (rd *realDecoder) getSubset(length int) (packetDecoder, error) { - buf, err := rd.getRawBytes(length) - if err != nil { - return nil, err - } - return &realDecoder{raw: buf}, nil -} - -func (rd *realDecoder) getRawBytes(length int) ([]byte, error) { - if length < 0 { - return nil, errInvalidByteSliceLength - } else if length > rd.remaining() { - rd.off = len(rd.raw) - return nil, ErrInsufficientData - } - - start := rd.off - rd.off += length - return rd.raw[start:rd.off], nil -} - -func (rd *realDecoder) peek(offset, length int) (packetDecoder, error) { - if rd.remaining() < offset+length { - return nil, ErrInsufficientData - } - off := rd.off + offset - return &realDecoder{raw: rd.raw[off : off+length]}, nil -} - -// stacks - -func (rd *realDecoder) push(in pushDecoder) error { - in.saveOffset(rd.off) - - var reserve int - if dpd, ok := in.(dynamicPushDecoder); ok { - if err := dpd.decode(rd); err != nil { - return err - } - } else { - reserve = in.reserveLength() - if rd.remaining() < reserve { - rd.off = len(rd.raw) - return ErrInsufficientData - } - } - - rd.stack = append(rd.stack, in) - - rd.off += reserve - - return nil -} - -func (rd *realDecoder) pop() error { - // this is go's ugly pop pattern (the inverse of append) - in := rd.stack[len(rd.stack)-1] - rd.stack = rd.stack[:len(rd.stack)-1] - - return in.check(rd.off, rd.raw) -} diff --git a/vendor/github.com/Shopify/sarama/real_encoder.go b/vendor/github.com/Shopify/sarama/real_encoder.go deleted file mode 100644 index 3c75387f779..00000000000 --- a/vendor/github.com/Shopify/sarama/real_encoder.go +++ /dev/null @@ -1,156 +0,0 @@ -package sarama - -import ( - "encoding/binary" - - "github.com/rcrowley/go-metrics" -) - -type realEncoder struct { - raw []byte - off int - stack []pushEncoder - registry metrics.Registry -} - -// primitives - -func (re *realEncoder) putInt8(in int8) { - re.raw[re.off] = byte(in) - re.off++ -} - -func (re *realEncoder) putInt16(in int16) { - binary.BigEndian.PutUint16(re.raw[re.off:], uint16(in)) - re.off += 2 -} - -func (re *realEncoder) putInt32(in int32) { - binary.BigEndian.PutUint32(re.raw[re.off:], uint32(in)) - re.off += 4 -} - -func (re *realEncoder) putInt64(in int64) { - binary.BigEndian.PutUint64(re.raw[re.off:], uint64(in)) - re.off += 8 -} - -func (re *realEncoder) putVarint(in int64) { - re.off += binary.PutVarint(re.raw[re.off:], in) -} - -func (re *realEncoder) putArrayLength(in int) error { - re.putInt32(int32(in)) - return nil -} - -func (re *realEncoder) putBool(in bool) { - if in { - re.putInt8(1) - return - } - re.putInt8(0) -} - -// collection - -func (re *realEncoder) putRawBytes(in []byte) error { - copy(re.raw[re.off:], in) - re.off += len(in) - return nil -} - -func (re *realEncoder) putBytes(in []byte) error { - if in == nil { - re.putInt32(-1) - return nil - } - re.putInt32(int32(len(in))) - return re.putRawBytes(in) -} - -func (re *realEncoder) putVarintBytes(in []byte) error { - if in == nil { - re.putVarint(-1) - return nil - } - re.putVarint(int64(len(in))) - return re.putRawBytes(in) -} - -func (re *realEncoder) putString(in string) error { - re.putInt16(int16(len(in))) - copy(re.raw[re.off:], in) - re.off += len(in) - return nil -} - -func (re *realEncoder) putNullableString(in *string) error { - if in == nil { - re.putInt16(-1) - return nil - } - return re.putString(*in) -} - -func (re *realEncoder) putStringArray(in []string) error { - err := re.putArrayLength(len(in)) - if err != nil { - return err - } - - for _, val := range in { - if err := re.putString(val); err != nil { - return err - } - } - - return nil -} - -func (re *realEncoder) putInt32Array(in []int32) error { - err := re.putArrayLength(len(in)) - if err != nil { - return err - } - for _, val := range in { - re.putInt32(val) - } - return nil -} - -func (re *realEncoder) putInt64Array(in []int64) error { - err := re.putArrayLength(len(in)) - if err != nil { - return err - } - for _, val := range in { - re.putInt64(val) - } - return nil -} - -func (re *realEncoder) offset() int { - return re.off -} - -// stacks - -func (re *realEncoder) push(in pushEncoder) { - in.saveOffset(re.off) - re.off += in.reserveLength() - re.stack = append(re.stack, in) -} - -func (re *realEncoder) pop() error { - // this is go's ugly pop pattern (the inverse of append) - in := re.stack[len(re.stack)-1] - re.stack = re.stack[:len(re.stack)-1] - - return in.run(re.off, re.raw) -} - -// we do record metrics during the real encoder pass -func (re *realEncoder) metricRegistry() metrics.Registry { - return re.registry -} diff --git a/vendor/github.com/Shopify/sarama/record.go b/vendor/github.com/Shopify/sarama/record.go deleted file mode 100644 index cded308cf0f..00000000000 --- a/vendor/github.com/Shopify/sarama/record.go +++ /dev/null @@ -1,113 +0,0 @@ -package sarama - -import ( - "encoding/binary" - "time" -) - -const ( - controlMask = 0x20 - maximumRecordOverhead = 5*binary.MaxVarintLen32 + binary.MaxVarintLen64 + 1 -) - -type RecordHeader struct { - Key []byte - Value []byte -} - -func (h *RecordHeader) encode(pe packetEncoder) error { - if err := pe.putVarintBytes(h.Key); err != nil { - return err - } - return pe.putVarintBytes(h.Value) -} - -func (h *RecordHeader) decode(pd packetDecoder) (err error) { - if h.Key, err = pd.getVarintBytes(); err != nil { - return err - } - - if h.Value, err = pd.getVarintBytes(); err != nil { - return err - } - return nil -} - -type Record struct { - Attributes int8 - TimestampDelta time.Duration - OffsetDelta int64 - Key []byte - Value []byte - Headers []*RecordHeader - - length varintLengthField -} - -func (r *Record) encode(pe packetEncoder) error { - pe.push(&r.length) - pe.putInt8(r.Attributes) - pe.putVarint(int64(r.TimestampDelta / time.Millisecond)) - pe.putVarint(r.OffsetDelta) - if err := pe.putVarintBytes(r.Key); err != nil { - return err - } - if err := pe.putVarintBytes(r.Value); err != nil { - return err - } - pe.putVarint(int64(len(r.Headers))) - - for _, h := range r.Headers { - if err := h.encode(pe); err != nil { - return err - } - } - - return pe.pop() -} - -func (r *Record) decode(pd packetDecoder) (err error) { - if err = pd.push(&r.length); err != nil { - return err - } - - if r.Attributes, err = pd.getInt8(); err != nil { - return err - } - - timestamp, err := pd.getVarint() - if err != nil { - return err - } - r.TimestampDelta = time.Duration(timestamp) * time.Millisecond - - if r.OffsetDelta, err = pd.getVarint(); err != nil { - return err - } - - if r.Key, err = pd.getVarintBytes(); err != nil { - return err - } - - if r.Value, err = pd.getVarintBytes(); err != nil { - return err - } - - numHeaders, err := pd.getVarint() - if err != nil { - return err - } - - if numHeaders >= 0 { - r.Headers = make([]*RecordHeader, numHeaders) - } - for i := int64(0); i < numHeaders; i++ { - hdr := new(RecordHeader) - if err := hdr.decode(pd); err != nil { - return err - } - r.Headers[i] = hdr - } - - return pd.pop() -} diff --git a/vendor/github.com/Shopify/sarama/record_batch.go b/vendor/github.com/Shopify/sarama/record_batch.go deleted file mode 100644 index 845318aa341..00000000000 --- a/vendor/github.com/Shopify/sarama/record_batch.go +++ /dev/null @@ -1,268 +0,0 @@ -package sarama - -import ( - "bytes" - "compress/gzip" - "fmt" - "io/ioutil" - "time" - - "github.com/eapache/go-xerial-snappy" - "github.com/pierrec/lz4" -) - -const recordBatchOverhead = 49 - -type recordsArray []*Record - -func (e recordsArray) encode(pe packetEncoder) error { - for _, r := range e { - if err := r.encode(pe); err != nil { - return err - } - } - return nil -} - -func (e recordsArray) decode(pd packetDecoder) error { - for i := range e { - rec := &Record{} - if err := rec.decode(pd); err != nil { - return err - } - e[i] = rec - } - return nil -} - -type RecordBatch struct { - FirstOffset int64 - PartitionLeaderEpoch int32 - Version int8 - Codec CompressionCodec - CompressionLevel int - Control bool - LastOffsetDelta int32 - FirstTimestamp time.Time - MaxTimestamp time.Time - ProducerID int64 - ProducerEpoch int16 - FirstSequence int32 - Records []*Record - PartialTrailingRecord bool - - compressedRecords []byte - recordsLen int // uncompressed records size -} - -func (b *RecordBatch) encode(pe packetEncoder) error { - if b.Version != 2 { - return PacketEncodingError{fmt.Sprintf("unsupported compression codec (%d)", b.Codec)} - } - pe.putInt64(b.FirstOffset) - pe.push(&lengthField{}) - pe.putInt32(b.PartitionLeaderEpoch) - pe.putInt8(b.Version) - pe.push(newCRC32Field(crcCastagnoli)) - pe.putInt16(b.computeAttributes()) - pe.putInt32(b.LastOffsetDelta) - - if err := (Timestamp{&b.FirstTimestamp}).encode(pe); err != nil { - return err - } - - if err := (Timestamp{&b.MaxTimestamp}).encode(pe); err != nil { - return err - } - - pe.putInt64(b.ProducerID) - pe.putInt16(b.ProducerEpoch) - pe.putInt32(b.FirstSequence) - - if err := pe.putArrayLength(len(b.Records)); err != nil { - return err - } - - if b.compressedRecords == nil { - if err := b.encodeRecords(pe); err != nil { - return err - } - } - if err := pe.putRawBytes(b.compressedRecords); err != nil { - return err - } - - if err := pe.pop(); err != nil { - return err - } - return pe.pop() -} - -func (b *RecordBatch) decode(pd packetDecoder) (err error) { - if b.FirstOffset, err = pd.getInt64(); err != nil { - return err - } - - batchLen, err := pd.getInt32() - if err != nil { - return err - } - - if b.PartitionLeaderEpoch, err = pd.getInt32(); err != nil { - return err - } - - if b.Version, err = pd.getInt8(); err != nil { - return err - } - - if err = pd.push(&crc32Field{polynomial: crcCastagnoli}); err != nil { - return err - } - - attributes, err := pd.getInt16() - if err != nil { - return err - } - b.Codec = CompressionCodec(int8(attributes) & compressionCodecMask) - b.Control = attributes&controlMask == controlMask - - if b.LastOffsetDelta, err = pd.getInt32(); err != nil { - return err - } - - if err = (Timestamp{&b.FirstTimestamp}).decode(pd); err != nil { - return err - } - - if err = (Timestamp{&b.MaxTimestamp}).decode(pd); err != nil { - return err - } - - if b.ProducerID, err = pd.getInt64(); err != nil { - return err - } - - if b.ProducerEpoch, err = pd.getInt16(); err != nil { - return err - } - - if b.FirstSequence, err = pd.getInt32(); err != nil { - return err - } - - numRecs, err := pd.getArrayLength() - if err != nil { - return err - } - if numRecs >= 0 { - b.Records = make([]*Record, numRecs) - } - - bufSize := int(batchLen) - recordBatchOverhead - recBuffer, err := pd.getRawBytes(bufSize) - if err != nil { - if err == ErrInsufficientData { - b.PartialTrailingRecord = true - b.Records = nil - return nil - } - return err - } - - if err = pd.pop(); err != nil { - return err - } - - switch b.Codec { - case CompressionNone: - case CompressionGZIP: - reader, err := gzip.NewReader(bytes.NewReader(recBuffer)) - if err != nil { - return err - } - if recBuffer, err = ioutil.ReadAll(reader); err != nil { - return err - } - case CompressionSnappy: - if recBuffer, err = snappy.Decode(recBuffer); err != nil { - return err - } - case CompressionLZ4: - reader := lz4.NewReader(bytes.NewReader(recBuffer)) - if recBuffer, err = ioutil.ReadAll(reader); err != nil { - return err - } - default: - return PacketDecodingError{fmt.Sprintf("invalid compression specified (%d)", b.Codec)} - } - - b.recordsLen = len(recBuffer) - err = decode(recBuffer, recordsArray(b.Records)) - if err == ErrInsufficientData { - b.PartialTrailingRecord = true - b.Records = nil - return nil - } - return err -} - -func (b *RecordBatch) encodeRecords(pe packetEncoder) error { - var raw []byte - var err error - if raw, err = encode(recordsArray(b.Records), pe.metricRegistry()); err != nil { - return err - } - b.recordsLen = len(raw) - - switch b.Codec { - case CompressionNone: - b.compressedRecords = raw - case CompressionGZIP: - var buf bytes.Buffer - var writer *gzip.Writer - if b.CompressionLevel != CompressionLevelDefault { - writer, err = gzip.NewWriterLevel(&buf, b.CompressionLevel) - if err != nil { - return err - } - } else { - writer = gzip.NewWriter(&buf) - } - if _, err := writer.Write(raw); err != nil { - return err - } - if err := writer.Close(); err != nil { - return err - } - b.compressedRecords = buf.Bytes() - case CompressionSnappy: - b.compressedRecords = snappy.Encode(raw) - case CompressionLZ4: - var buf bytes.Buffer - writer := lz4.NewWriter(&buf) - if _, err := writer.Write(raw); err != nil { - return err - } - if err := writer.Close(); err != nil { - return err - } - b.compressedRecords = buf.Bytes() - default: - return PacketEncodingError{fmt.Sprintf("unsupported compression codec (%d)", b.Codec)} - } - - return nil -} - -func (b *RecordBatch) computeAttributes() int16 { - attr := int16(b.Codec) & int16(compressionCodecMask) - if b.Control { - attr |= controlMask - } - return attr -} - -func (b *RecordBatch) addRecord(r *Record) { - b.Records = append(b.Records, r) -} diff --git a/vendor/github.com/Shopify/sarama/records.go b/vendor/github.com/Shopify/sarama/records.go deleted file mode 100644 index 301055bb070..00000000000 --- a/vendor/github.com/Shopify/sarama/records.go +++ /dev/null @@ -1,173 +0,0 @@ -package sarama - -import "fmt" - -const ( - unknownRecords = iota - legacyRecords - defaultRecords - - magicOffset = 16 - magicLength = 1 -) - -// Records implements a union type containing either a RecordBatch or a legacy MessageSet. -type Records struct { - recordsType int - MsgSet *MessageSet - RecordBatch *RecordBatch -} - -func newLegacyRecords(msgSet *MessageSet) Records { - return Records{recordsType: legacyRecords, MsgSet: msgSet} -} - -func newDefaultRecords(batch *RecordBatch) Records { - return Records{recordsType: defaultRecords, RecordBatch: batch} -} - -// setTypeFromFields sets type of Records depending on which of MsgSet or RecordBatch is not nil. -// The first return value indicates whether both fields are nil (and the type is not set). -// If both fields are not nil, it returns an error. -func (r *Records) setTypeFromFields() (bool, error) { - if r.MsgSet == nil && r.RecordBatch == nil { - return true, nil - } - if r.MsgSet != nil && r.RecordBatch != nil { - return false, fmt.Errorf("both MsgSet and RecordBatch are set, but record type is unknown") - } - r.recordsType = defaultRecords - if r.MsgSet != nil { - r.recordsType = legacyRecords - } - return false, nil -} - -func (r *Records) encode(pe packetEncoder) error { - if r.recordsType == unknownRecords { - if empty, err := r.setTypeFromFields(); err != nil || empty { - return err - } - } - - switch r.recordsType { - case legacyRecords: - if r.MsgSet == nil { - return nil - } - return r.MsgSet.encode(pe) - case defaultRecords: - if r.RecordBatch == nil { - return nil - } - return r.RecordBatch.encode(pe) - } - - return fmt.Errorf("unknown records type: %v", r.recordsType) -} - -func (r *Records) setTypeFromMagic(pd packetDecoder) error { - magic, err := magicValue(pd) - if err != nil { - return err - } - - r.recordsType = defaultRecords - if magic < 2 { - r.recordsType = legacyRecords - } - - return nil -} - -func (r *Records) decode(pd packetDecoder) error { - if r.recordsType == unknownRecords { - if err := r.setTypeFromMagic(pd); err != nil { - return err - } - } - - switch r.recordsType { - case legacyRecords: - r.MsgSet = &MessageSet{} - return r.MsgSet.decode(pd) - case defaultRecords: - r.RecordBatch = &RecordBatch{} - return r.RecordBatch.decode(pd) - } - return fmt.Errorf("unknown records type: %v", r.recordsType) -} - -func (r *Records) numRecords() (int, error) { - if r.recordsType == unknownRecords { - if empty, err := r.setTypeFromFields(); err != nil || empty { - return 0, err - } - } - - switch r.recordsType { - case legacyRecords: - if r.MsgSet == nil { - return 0, nil - } - return len(r.MsgSet.Messages), nil - case defaultRecords: - if r.RecordBatch == nil { - return 0, nil - } - return len(r.RecordBatch.Records), nil - } - return 0, fmt.Errorf("unknown records type: %v", r.recordsType) -} - -func (r *Records) isPartial() (bool, error) { - if r.recordsType == unknownRecords { - if empty, err := r.setTypeFromFields(); err != nil || empty { - return false, err - } - } - - switch r.recordsType { - case unknownRecords: - return false, nil - case legacyRecords: - if r.MsgSet == nil { - return false, nil - } - return r.MsgSet.PartialTrailingMessage, nil - case defaultRecords: - if r.RecordBatch == nil { - return false, nil - } - return r.RecordBatch.PartialTrailingRecord, nil - } - return false, fmt.Errorf("unknown records type: %v", r.recordsType) -} - -func (r *Records) isControl() (bool, error) { - if r.recordsType == unknownRecords { - if empty, err := r.setTypeFromFields(); err != nil || empty { - return false, err - } - } - - switch r.recordsType { - case legacyRecords: - return false, nil - case defaultRecords: - if r.RecordBatch == nil { - return false, nil - } - return r.RecordBatch.Control, nil - } - return false, fmt.Errorf("unknown records type: %v", r.recordsType) -} - -func magicValue(pd packetDecoder) (int8, error) { - dec, err := pd.peek(magicOffset, magicLength) - if err != nil { - return 0, err - } - - return dec.getInt8() -} diff --git a/vendor/github.com/Shopify/sarama/request.go b/vendor/github.com/Shopify/sarama/request.go deleted file mode 100644 index 4d211a14f17..00000000000 --- a/vendor/github.com/Shopify/sarama/request.go +++ /dev/null @@ -1,149 +0,0 @@ -package sarama - -import ( - "encoding/binary" - "fmt" - "io" -) - -type protocolBody interface { - encoder - versionedDecoder - key() int16 - version() int16 - requiredVersion() KafkaVersion -} - -type request struct { - correlationID int32 - clientID string - body protocolBody -} - -func (r *request) encode(pe packetEncoder) (err error) { - pe.push(&lengthField{}) - pe.putInt16(r.body.key()) - pe.putInt16(r.body.version()) - pe.putInt32(r.correlationID) - err = pe.putString(r.clientID) - if err != nil { - return err - } - err = r.body.encode(pe) - if err != nil { - return err - } - return pe.pop() -} - -func (r *request) decode(pd packetDecoder) (err error) { - var key int16 - if key, err = pd.getInt16(); err != nil { - return err - } - var version int16 - if version, err = pd.getInt16(); err != nil { - return err - } - if r.correlationID, err = pd.getInt32(); err != nil { - return err - } - r.clientID, err = pd.getString() - - r.body = allocateBody(key, version) - if r.body == nil { - return PacketDecodingError{fmt.Sprintf("unknown request key (%d)", key)} - } - return r.body.decode(pd, version) -} - -func decodeRequest(r io.Reader) (req *request, bytesRead int, err error) { - lengthBytes := make([]byte, 4) - if _, err := io.ReadFull(r, lengthBytes); err != nil { - return nil, bytesRead, err - } - bytesRead += len(lengthBytes) - - length := int32(binary.BigEndian.Uint32(lengthBytes)) - if length <= 4 || length > MaxRequestSize { - return nil, bytesRead, PacketDecodingError{fmt.Sprintf("message of length %d too large or too small", length)} - } - - encodedReq := make([]byte, length) - if _, err := io.ReadFull(r, encodedReq); err != nil { - return nil, bytesRead, err - } - bytesRead += len(encodedReq) - - req = &request{} - if err := decode(encodedReq, req); err != nil { - return nil, bytesRead, err - } - return req, bytesRead, nil -} - -func allocateBody(key, version int16) protocolBody { - switch key { - case 0: - return &ProduceRequest{} - case 1: - return &FetchRequest{} - case 2: - return &OffsetRequest{Version: version} - case 3: - return &MetadataRequest{} - case 8: - return &OffsetCommitRequest{Version: version} - case 9: - return &OffsetFetchRequest{} - case 10: - return &FindCoordinatorRequest{} - case 11: - return &JoinGroupRequest{} - case 12: - return &HeartbeatRequest{} - case 13: - return &LeaveGroupRequest{} - case 14: - return &SyncGroupRequest{} - case 15: - return &DescribeGroupsRequest{} - case 16: - return &ListGroupsRequest{} - case 17: - return &SaslHandshakeRequest{} - case 18: - return &ApiVersionsRequest{} - case 19: - return &CreateTopicsRequest{} - case 20: - return &DeleteTopicsRequest{} - case 21: - return &DeleteRecordsRequest{} - case 22: - return &InitProducerIDRequest{} - case 24: - return &AddPartitionsToTxnRequest{} - case 25: - return &AddOffsetsToTxnRequest{} - case 26: - return &EndTxnRequest{} - case 28: - return &TxnOffsetCommitRequest{} - case 29: - return &DescribeAclsRequest{} - case 30: - return &CreateAclsRequest{} - case 31: - return &DeleteAclsRequest{} - case 32: - return &DescribeConfigsRequest{} - case 33: - return &AlterConfigsRequest{} - case 37: - return &CreatePartitionsRequest{} - case 42: - return &DeleteGroupsRequest{} - } - return nil -} diff --git a/vendor/github.com/Shopify/sarama/response_header.go b/vendor/github.com/Shopify/sarama/response_header.go deleted file mode 100644 index f3f4d27d6c4..00000000000 --- a/vendor/github.com/Shopify/sarama/response_header.go +++ /dev/null @@ -1,21 +0,0 @@ -package sarama - -import "fmt" - -type responseHeader struct { - length int32 - correlationID int32 -} - -func (r *responseHeader) decode(pd packetDecoder) (err error) { - r.length, err = pd.getInt32() - if err != nil { - return err - } - if r.length <= 4 || r.length > MaxResponseSize { - return PacketDecodingError{fmt.Sprintf("message of length %d too large or too small", r.length)} - } - - r.correlationID, err = pd.getInt32() - return err -} diff --git a/vendor/github.com/Shopify/sarama/sarama.go b/vendor/github.com/Shopify/sarama/sarama.go deleted file mode 100644 index 7d5dc60d3e2..00000000000 --- a/vendor/github.com/Shopify/sarama/sarama.go +++ /dev/null @@ -1,99 +0,0 @@ -/* -Package sarama is a pure Go client library for dealing with Apache Kafka (versions 0.8 and later). It includes a high-level -API for easily producing and consuming messages, and a low-level API for controlling bytes on the wire when the high-level -API is insufficient. Usage examples for the high-level APIs are provided inline with their full documentation. - -To produce messages, use either the AsyncProducer or the SyncProducer. The AsyncProducer accepts messages on a channel -and produces them asynchronously in the background as efficiently as possible; it is preferred in most cases. -The SyncProducer provides a method which will block until Kafka acknowledges the message as produced. This can be -useful but comes with two caveats: it will generally be less efficient, and the actual durability guarantees -depend on the configured value of `Producer.RequiredAcks`. There are configurations where a message acknowledged by the -SyncProducer can still sometimes be lost. - -To consume messages, use the Consumer. Note that Sarama's Consumer implementation does not currently support automatic -consumer-group rebalancing and offset tracking. For Zookeeper-based tracking (Kafka 0.8.2 and earlier), the -https://github.com/wvanbergen/kafka library builds on Sarama to add this support. For Kafka-based tracking (Kafka 0.9 -and later), the https://github.com/bsm/sarama-cluster library builds on Sarama to add this support. - -For lower-level needs, the Broker and Request/Response objects permit precise control over each connection -and message sent on the wire; the Client provides higher-level metadata management that is shared between -the producers and the consumer. The Request/Response objects and properties are mostly undocumented, as they line up -exactly with the protocol fields documented by Kafka at -https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol - -Metrics are exposed through https://github.com/rcrowley/go-metrics library in a local registry. - -Broker related metrics: - - +----------------------------------------------+------------+---------------------------------------------------------------+ - | Name | Type | Description | - +----------------------------------------------+------------+---------------------------------------------------------------+ - | incoming-byte-rate | meter | Bytes/second read off all brokers | - | incoming-byte-rate-for-broker- | meter | Bytes/second read off a given broker | - | outgoing-byte-rate | meter | Bytes/second written off all brokers | - | outgoing-byte-rate-for-broker- | meter | Bytes/second written off a given broker | - | request-rate | meter | Requests/second sent to all brokers | - | request-rate-for-broker- | meter | Requests/second sent to a given broker | - | request-size | histogram | Distribution of the request size in bytes for all brokers | - | request-size-for-broker- | histogram | Distribution of the request size in bytes for a given broker | - | request-latency-in-ms | histogram | Distribution of the request latency in ms for all brokers | - | request-latency-in-ms-for-broker- | histogram | Distribution of the request latency in ms for a given broker | - | response-rate | meter | Responses/second received from all brokers | - | response-rate-for-broker- | meter | Responses/second received from a given broker | - | response-size | histogram | Distribution of the response size in bytes for all brokers | - | response-size-for-broker- | histogram | Distribution of the response size in bytes for a given broker | - +----------------------------------------------+------------+---------------------------------------------------------------+ - -Note that we do not gather specific metrics for seed brokers but they are part of the "all brokers" metrics. - -Producer related metrics: - - +-------------------------------------------+------------+--------------------------------------------------------------------------------------+ - | Name | Type | Description | - +-------------------------------------------+------------+--------------------------------------------------------------------------------------+ - | batch-size | histogram | Distribution of the number of bytes sent per partition per request for all topics | - | batch-size-for-topic- | histogram | Distribution of the number of bytes sent per partition per request for a given topic | - | record-send-rate | meter | Records/second sent to all topics | - | record-send-rate-for-topic- | meter | Records/second sent to a given topic | - | records-per-request | histogram | Distribution of the number of records sent per request for all topics | - | records-per-request-for-topic- | histogram | Distribution of the number of records sent per request for a given topic | - | compression-ratio | histogram | Distribution of the compression ratio times 100 of record batches for all topics | - | compression-ratio-for-topic- | histogram | Distribution of the compression ratio times 100 of record batches for a given topic | - +-------------------------------------------+------------+--------------------------------------------------------------------------------------+ - -*/ -package sarama - -import ( - "io/ioutil" - "log" -) - -// Logger is the instance of a StdLogger interface that Sarama writes connection -// management events to. By default it is set to discard all log messages via ioutil.Discard, -// but you can set it to redirect wherever you want. -var Logger StdLogger = log.New(ioutil.Discard, "[Sarama] ", log.LstdFlags) - -// StdLogger is used to log error messages. -type StdLogger interface { - Print(v ...interface{}) - Printf(format string, v ...interface{}) - Println(v ...interface{}) -} - -// PanicHandler is called for recovering from panics spawned internally to the library (and thus -// not recoverable by the caller's goroutine). Defaults to nil, which means panics are not recovered. -var PanicHandler func(interface{}) - -// MaxRequestSize is the maximum size (in bytes) of any request that Sarama will attempt to send. Trying -// to send a request larger than this will result in an PacketEncodingError. The default of 100 MiB is aligned -// with Kafka's default `socket.request.max.bytes`, which is the largest request the broker will attempt -// to process. -var MaxRequestSize int32 = 100 * 1024 * 1024 - -// MaxResponseSize is the maximum size (in bytes) of any response that Sarama will attempt to parse. If -// a broker returns a response message larger than this value, Sarama will return a PacketDecodingError to -// protect the client from running out of memory. Please note that brokers do not have any natural limit on -// the size of responses they send. In particular, they can send arbitrarily large fetch responses to consumers -// (see https://issues.apache.org/jira/browse/KAFKA-2063). -var MaxResponseSize int32 = 100 * 1024 * 1024 diff --git a/vendor/github.com/Shopify/sarama/sasl_handshake_request.go b/vendor/github.com/Shopify/sarama/sasl_handshake_request.go deleted file mode 100644 index fbbc8947b2e..00000000000 --- a/vendor/github.com/Shopify/sarama/sasl_handshake_request.go +++ /dev/null @@ -1,33 +0,0 @@ -package sarama - -type SaslHandshakeRequest struct { - Mechanism string -} - -func (r *SaslHandshakeRequest) encode(pe packetEncoder) error { - if err := pe.putString(r.Mechanism); err != nil { - return err - } - - return nil -} - -func (r *SaslHandshakeRequest) decode(pd packetDecoder, version int16) (err error) { - if r.Mechanism, err = pd.getString(); err != nil { - return err - } - - return nil -} - -func (r *SaslHandshakeRequest) key() int16 { - return 17 -} - -func (r *SaslHandshakeRequest) version() int16 { - return 0 -} - -func (r *SaslHandshakeRequest) requiredVersion() KafkaVersion { - return V0_10_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/sasl_handshake_response.go b/vendor/github.com/Shopify/sarama/sasl_handshake_response.go deleted file mode 100644 index ef290d4bc6d..00000000000 --- a/vendor/github.com/Shopify/sarama/sasl_handshake_response.go +++ /dev/null @@ -1,38 +0,0 @@ -package sarama - -type SaslHandshakeResponse struct { - Err KError - EnabledMechanisms []string -} - -func (r *SaslHandshakeResponse) encode(pe packetEncoder) error { - pe.putInt16(int16(r.Err)) - return pe.putStringArray(r.EnabledMechanisms) -} - -func (r *SaslHandshakeResponse) decode(pd packetDecoder, version int16) error { - kerr, err := pd.getInt16() - if err != nil { - return err - } - - r.Err = KError(kerr) - - if r.EnabledMechanisms, err = pd.getStringArray(); err != nil { - return err - } - - return nil -} - -func (r *SaslHandshakeResponse) key() int16 { - return 17 -} - -func (r *SaslHandshakeResponse) version() int16 { - return 0 -} - -func (r *SaslHandshakeResponse) requiredVersion() KafkaVersion { - return V0_10_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/sync_group_request.go b/vendor/github.com/Shopify/sarama/sync_group_request.go deleted file mode 100644 index fe207080e03..00000000000 --- a/vendor/github.com/Shopify/sarama/sync_group_request.go +++ /dev/null @@ -1,100 +0,0 @@ -package sarama - -type SyncGroupRequest struct { - GroupId string - GenerationId int32 - MemberId string - GroupAssignments map[string][]byte -} - -func (r *SyncGroupRequest) encode(pe packetEncoder) error { - if err := pe.putString(r.GroupId); err != nil { - return err - } - - pe.putInt32(r.GenerationId) - - if err := pe.putString(r.MemberId); err != nil { - return err - } - - if err := pe.putArrayLength(len(r.GroupAssignments)); err != nil { - return err - } - for memberId, memberAssignment := range r.GroupAssignments { - if err := pe.putString(memberId); err != nil { - return err - } - if err := pe.putBytes(memberAssignment); err != nil { - return err - } - } - - return nil -} - -func (r *SyncGroupRequest) decode(pd packetDecoder, version int16) (err error) { - if r.GroupId, err = pd.getString(); err != nil { - return - } - if r.GenerationId, err = pd.getInt32(); err != nil { - return - } - if r.MemberId, err = pd.getString(); err != nil { - return - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - if n == 0 { - return nil - } - - r.GroupAssignments = make(map[string][]byte) - for i := 0; i < n; i++ { - memberId, err := pd.getString() - if err != nil { - return err - } - memberAssignment, err := pd.getBytes() - if err != nil { - return err - } - - r.GroupAssignments[memberId] = memberAssignment - } - - return nil -} - -func (r *SyncGroupRequest) key() int16 { - return 14 -} - -func (r *SyncGroupRequest) version() int16 { - return 0 -} - -func (r *SyncGroupRequest) requiredVersion() KafkaVersion { - return V0_9_0_0 -} - -func (r *SyncGroupRequest) AddGroupAssignment(memberId string, memberAssignment []byte) { - if r.GroupAssignments == nil { - r.GroupAssignments = make(map[string][]byte) - } - - r.GroupAssignments[memberId] = memberAssignment -} - -func (r *SyncGroupRequest) AddGroupAssignmentMember(memberId string, memberAssignment *ConsumerGroupMemberAssignment) error { - bin, err := encode(memberAssignment, nil) - if err != nil { - return err - } - - r.AddGroupAssignment(memberId, bin) - return nil -} diff --git a/vendor/github.com/Shopify/sarama/sync_group_response.go b/vendor/github.com/Shopify/sarama/sync_group_response.go deleted file mode 100644 index 194b382b4ab..00000000000 --- a/vendor/github.com/Shopify/sarama/sync_group_response.go +++ /dev/null @@ -1,41 +0,0 @@ -package sarama - -type SyncGroupResponse struct { - Err KError - MemberAssignment []byte -} - -func (r *SyncGroupResponse) GetMemberAssignment() (*ConsumerGroupMemberAssignment, error) { - assignment := new(ConsumerGroupMemberAssignment) - err := decode(r.MemberAssignment, assignment) - return assignment, err -} - -func (r *SyncGroupResponse) encode(pe packetEncoder) error { - pe.putInt16(int16(r.Err)) - return pe.putBytes(r.MemberAssignment) -} - -func (r *SyncGroupResponse) decode(pd packetDecoder, version int16) (err error) { - kerr, err := pd.getInt16() - if err != nil { - return err - } - - r.Err = KError(kerr) - - r.MemberAssignment, err = pd.getBytes() - return -} - -func (r *SyncGroupResponse) key() int16 { - return 14 -} - -func (r *SyncGroupResponse) version() int16 { - return 0 -} - -func (r *SyncGroupResponse) requiredVersion() KafkaVersion { - return V0_9_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/sync_producer.go b/vendor/github.com/Shopify/sarama/sync_producer.go deleted file mode 100644 index 021c5a01032..00000000000 --- a/vendor/github.com/Shopify/sarama/sync_producer.go +++ /dev/null @@ -1,149 +0,0 @@ -package sarama - -import "sync" - -// SyncProducer publishes Kafka messages, blocking until they have been acknowledged. It routes messages to the correct -// broker, refreshing metadata as appropriate, and parses responses for errors. You must call Close() on a producer -// to avoid leaks, it may not be garbage-collected automatically when it passes out of scope. -// -// The SyncProducer comes with two caveats: it will generally be less efficient than the AsyncProducer, and the actual -// durability guarantee provided when a message is acknowledged depend on the configured value of `Producer.RequiredAcks`. -// There are configurations where a message acknowledged by the SyncProducer can still sometimes be lost. -// -// For implementation reasons, the SyncProducer requires `Producer.Return.Errors` and `Producer.Return.Successes` to -// be set to true in its configuration. -type SyncProducer interface { - - // SendMessage produces a given message, and returns only when it either has - // succeeded or failed to produce. It will return the partition and the offset - // of the produced message, or an error if the message failed to produce. - SendMessage(msg *ProducerMessage) (partition int32, offset int64, err error) - - // SendMessages produces a given set of messages, and returns only when all - // messages in the set have either succeeded or failed. Note that messages - // can succeed and fail individually; if some succeed and some fail, - // SendMessages will return an error. - SendMessages(msgs []*ProducerMessage) error - - // Close shuts down the producer and waits for any buffered messages to be - // flushed. You must call this function before a producer object passes out of - // scope, as it may otherwise leak memory. You must call this before calling - // Close on the underlying client. - Close() error -} - -type syncProducer struct { - producer *asyncProducer - wg sync.WaitGroup -} - -// NewSyncProducer creates a new SyncProducer using the given broker addresses and configuration. -func NewSyncProducer(addrs []string, config *Config) (SyncProducer, error) { - if config == nil { - config = NewConfig() - config.Producer.Return.Successes = true - } - - if err := verifyProducerConfig(config); err != nil { - return nil, err - } - - p, err := NewAsyncProducer(addrs, config) - if err != nil { - return nil, err - } - return newSyncProducerFromAsyncProducer(p.(*asyncProducer)), nil -} - -// NewSyncProducerFromClient creates a new SyncProducer using the given client. It is still -// necessary to call Close() on the underlying client when shutting down this producer. -func NewSyncProducerFromClient(client Client) (SyncProducer, error) { - if err := verifyProducerConfig(client.Config()); err != nil { - return nil, err - } - - p, err := NewAsyncProducerFromClient(client) - if err != nil { - return nil, err - } - return newSyncProducerFromAsyncProducer(p.(*asyncProducer)), nil -} - -func newSyncProducerFromAsyncProducer(p *asyncProducer) *syncProducer { - sp := &syncProducer{producer: p} - - sp.wg.Add(2) - go withRecover(sp.handleSuccesses) - go withRecover(sp.handleErrors) - - return sp -} - -func verifyProducerConfig(config *Config) error { - if !config.Producer.Return.Errors { - return ConfigurationError("Producer.Return.Errors must be true to be used in a SyncProducer") - } - if !config.Producer.Return.Successes { - return ConfigurationError("Producer.Return.Successes must be true to be used in a SyncProducer") - } - return nil -} - -func (sp *syncProducer) SendMessage(msg *ProducerMessage) (partition int32, offset int64, err error) { - expectation := make(chan *ProducerError, 1) - msg.expectation = expectation - sp.producer.Input() <- msg - - if err := <-expectation; err != nil { - return -1, -1, err.Err - } - - return msg.Partition, msg.Offset, nil -} - -func (sp *syncProducer) SendMessages(msgs []*ProducerMessage) error { - expectations := make(chan chan *ProducerError, len(msgs)) - go func() { - for _, msg := range msgs { - expectation := make(chan *ProducerError, 1) - msg.expectation = expectation - sp.producer.Input() <- msg - expectations <- expectation - } - close(expectations) - }() - - var errors ProducerErrors - for expectation := range expectations { - if err := <-expectation; err != nil { - errors = append(errors, err) - } - } - - if len(errors) > 0 { - return errors - } - return nil -} - -func (sp *syncProducer) handleSuccesses() { - defer sp.wg.Done() - for msg := range sp.producer.Successes() { - expectation := msg.expectation - expectation <- nil - } -} - -func (sp *syncProducer) handleErrors() { - defer sp.wg.Done() - for err := range sp.producer.Errors() { - expectation := err.Msg.expectation - expectation <- err - } -} - -func (sp *syncProducer) Close() error { - sp.producer.AsyncClose() - sp.wg.Wait() - return nil -} diff --git a/vendor/github.com/Shopify/sarama/timestamp.go b/vendor/github.com/Shopify/sarama/timestamp.go deleted file mode 100644 index 372278d0bfa..00000000000 --- a/vendor/github.com/Shopify/sarama/timestamp.go +++ /dev/null @@ -1,40 +0,0 @@ -package sarama - -import ( - "fmt" - "time" -) - -type Timestamp struct { - *time.Time -} - -func (t Timestamp) encode(pe packetEncoder) error { - timestamp := int64(-1) - - if !t.Before(time.Unix(0, 0)) { - timestamp = t.UnixNano() / int64(time.Millisecond) - } else if !t.IsZero() { - return PacketEncodingError{fmt.Sprintf("invalid timestamp (%v)", t)} - } - - pe.putInt64(timestamp) - return nil -} - -func (t Timestamp) decode(pd packetDecoder) error { - millis, err := pd.getInt64() - if err != nil { - return err - } - - // negative timestamps are invalid, in these cases we should return - // a zero time - timestamp := time.Time{} - if millis >= 0 { - timestamp = time.Unix(millis/1000, (millis%1000)*int64(time.Millisecond)) - } - - *t.Time = timestamp - return nil -} diff --git a/vendor/github.com/Shopify/sarama/txn_offset_commit_request.go b/vendor/github.com/Shopify/sarama/txn_offset_commit_request.go deleted file mode 100644 index 71e95b814cb..00000000000 --- a/vendor/github.com/Shopify/sarama/txn_offset_commit_request.go +++ /dev/null @@ -1,126 +0,0 @@ -package sarama - -type TxnOffsetCommitRequest struct { - TransactionalID string - GroupID string - ProducerID int64 - ProducerEpoch int16 - Topics map[string][]*PartitionOffsetMetadata -} - -func (t *TxnOffsetCommitRequest) encode(pe packetEncoder) error { - if err := pe.putString(t.TransactionalID); err != nil { - return err - } - if err := pe.putString(t.GroupID); err != nil { - return err - } - pe.putInt64(t.ProducerID) - pe.putInt16(t.ProducerEpoch) - - if err := pe.putArrayLength(len(t.Topics)); err != nil { - return err - } - for topic, partitions := range t.Topics { - if err := pe.putString(topic); err != nil { - return err - } - if err := pe.putArrayLength(len(partitions)); err != nil { - return err - } - for _, partition := range partitions { - if err := partition.encode(pe); err != nil { - return err - } - } - } - - return nil -} - -func (t *TxnOffsetCommitRequest) decode(pd packetDecoder, version int16) (err error) { - if t.TransactionalID, err = pd.getString(); err != nil { - return err - } - if t.GroupID, err = pd.getString(); err != nil { - return err - } - if t.ProducerID, err = pd.getInt64(); err != nil { - return err - } - if t.ProducerEpoch, err = pd.getInt16(); err != nil { - return err - } - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - t.Topics = make(map[string][]*PartitionOffsetMetadata) - for i := 0; i < n; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - - m, err := pd.getArrayLength() - if err != nil { - return err - } - - t.Topics[topic] = make([]*PartitionOffsetMetadata, m) - - for j := 0; j < m; j++ { - partitionOffsetMetadata := new(PartitionOffsetMetadata) - if err := partitionOffsetMetadata.decode(pd, version); err != nil { - return err - } - t.Topics[topic][j] = partitionOffsetMetadata - } - } - - return nil -} - -func (a *TxnOffsetCommitRequest) key() int16 { - return 28 -} - -func (a *TxnOffsetCommitRequest) version() int16 { - return 0 -} - -func (a *TxnOffsetCommitRequest) requiredVersion() KafkaVersion { - return V0_11_0_0 -} - -type PartitionOffsetMetadata struct { - Partition int32 - Offset int64 - Metadata *string -} - -func (p *PartitionOffsetMetadata) encode(pe packetEncoder) error { - pe.putInt32(p.Partition) - pe.putInt64(p.Offset) - if err := pe.putNullableString(p.Metadata); err != nil { - return err - } - - return nil -} - -func (p *PartitionOffsetMetadata) decode(pd packetDecoder, version int16) (err error) { - if p.Partition, err = pd.getInt32(); err != nil { - return err - } - if p.Offset, err = pd.getInt64(); err != nil { - return err - } - if p.Metadata, err = pd.getNullableString(); err != nil { - return err - } - - return nil -} diff --git a/vendor/github.com/Shopify/sarama/txn_offset_commit_response.go b/vendor/github.com/Shopify/sarama/txn_offset_commit_response.go deleted file mode 100644 index 6c980f4066f..00000000000 --- a/vendor/github.com/Shopify/sarama/txn_offset_commit_response.go +++ /dev/null @@ -1,83 +0,0 @@ -package sarama - -import ( - "time" -) - -type TxnOffsetCommitResponse struct { - ThrottleTime time.Duration - Topics map[string][]*PartitionError -} - -func (t *TxnOffsetCommitResponse) encode(pe packetEncoder) error { - pe.putInt32(int32(t.ThrottleTime / time.Millisecond)) - if err := pe.putArrayLength(len(t.Topics)); err != nil { - return err - } - - for topic, e := range t.Topics { - if err := pe.putString(topic); err != nil { - return err - } - if err := pe.putArrayLength(len(e)); err != nil { - return err - } - for _, partitionError := range e { - if err := partitionError.encode(pe); err != nil { - return err - } - } - } - - return nil -} - -func (t *TxnOffsetCommitResponse) decode(pd packetDecoder, version int16) (err error) { - throttleTime, err := pd.getInt32() - if err != nil { - return err - } - t.ThrottleTime = time.Duration(throttleTime) * time.Millisecond - - n, err := pd.getArrayLength() - if err != nil { - return err - } - - t.Topics = make(map[string][]*PartitionError) - - for i := 0; i < n; i++ { - topic, err := pd.getString() - if err != nil { - return err - } - - m, err := pd.getArrayLength() - if err != nil { - return err - } - - t.Topics[topic] = make([]*PartitionError, m) - - for j := 0; j < m; j++ { - t.Topics[topic][j] = new(PartitionError) - if err := t.Topics[topic][j].decode(pd, version); err != nil { - return err - } - } - } - - return nil -} - -func (a *TxnOffsetCommitResponse) key() int16 { - return 28 -} - -func (a *TxnOffsetCommitResponse) version() int16 { - return 0 -} - -func (a *TxnOffsetCommitResponse) requiredVersion() KafkaVersion { - return V0_11_0_0 -} diff --git a/vendor/github.com/Shopify/sarama/utils.go b/vendor/github.com/Shopify/sarama/utils.go deleted file mode 100644 index 702e2262701..00000000000 --- a/vendor/github.com/Shopify/sarama/utils.go +++ /dev/null @@ -1,212 +0,0 @@ -package sarama - -import ( - "bufio" - "fmt" - "net" - "regexp" -) - -type none struct{} - -// make []int32 sortable so we can sort partition numbers -type int32Slice []int32 - -func (slice int32Slice) Len() int { - return len(slice) -} - -func (slice int32Slice) Less(i, j int) bool { - return slice[i] < slice[j] -} - -func (slice int32Slice) Swap(i, j int) { - slice[i], slice[j] = slice[j], slice[i] -} - -func dupInt32Slice(input []int32) []int32 { - ret := make([]int32, 0, len(input)) - for _, val := range input { - ret = append(ret, val) - } - return ret -} - -func withRecover(fn func()) { - defer func() { - handler := PanicHandler - if handler != nil { - if err := recover(); err != nil { - handler(err) - } - } - }() - - fn() -} - -func safeAsyncClose(b *Broker) { - tmp := b // local var prevents clobbering in goroutine - go withRecover(func() { - if connected, _ := tmp.Connected(); connected { - if err := tmp.Close(); err != nil { - Logger.Println("Error closing broker", tmp.ID(), ":", err) - } - } - }) -} - -// Encoder is a simple interface for any type that can be encoded as an array of bytes -// in order to be sent as the key or value of a Kafka message. Length() is provided as an -// optimization, and must return the same as len() on the result of Encode(). -type Encoder interface { - Encode() ([]byte, error) - Length() int -} - -// make strings and byte slices encodable for convenience so they can be used as keys -// and/or values in kafka messages - -// StringEncoder implements the Encoder interface for Go strings so that they can be used -// as the Key or Value in a ProducerMessage. -type StringEncoder string - -func (s StringEncoder) Encode() ([]byte, error) { - return []byte(s), nil -} - -func (s StringEncoder) Length() int { - return len(s) -} - -// ByteEncoder implements the Encoder interface for Go byte slices so that they can be used -// as the Key or Value in a ProducerMessage. -type ByteEncoder []byte - -func (b ByteEncoder) Encode() ([]byte, error) { - return b, nil -} - -func (b ByteEncoder) Length() int { - return len(b) -} - -// bufConn wraps a net.Conn with a buffer for reads to reduce the number of -// reads that trigger syscalls. -type bufConn struct { - net.Conn - buf *bufio.Reader -} - -func newBufConn(conn net.Conn) *bufConn { - return &bufConn{ - Conn: conn, - buf: bufio.NewReader(conn), - } -} - -func (bc *bufConn) Read(b []byte) (n int, err error) { - return bc.buf.Read(b) -} - -// KafkaVersion instances represent versions of the upstream Kafka broker. -type KafkaVersion struct { - // it's a struct rather than just typing the array directly to make it opaque and stop people - // generating their own arbitrary versions - version [4]uint -} - -func newKafkaVersion(major, minor, veryMinor, patch uint) KafkaVersion { - return KafkaVersion{ - version: [4]uint{major, minor, veryMinor, patch}, - } -} - -// IsAtLeast return true if and only if the version it is called on is -// greater than or equal to the version passed in: -// V1.IsAtLeast(V2) // false -// V2.IsAtLeast(V1) // true -func (v KafkaVersion) IsAtLeast(other KafkaVersion) bool { - for i := range v.version { - if v.version[i] > other.version[i] { - return true - } else if v.version[i] < other.version[i] { - return false - } - } - return true -} - -// Effective constants defining the supported kafka versions. -var ( - V0_8_2_0 = newKafkaVersion(0, 8, 2, 0) - V0_8_2_1 = newKafkaVersion(0, 8, 2, 1) - V0_8_2_2 = newKafkaVersion(0, 8, 2, 2) - V0_9_0_0 = newKafkaVersion(0, 9, 0, 0) - V0_9_0_1 = newKafkaVersion(0, 9, 0, 1) - V0_10_0_0 = newKafkaVersion(0, 10, 0, 0) - V0_10_0_1 = newKafkaVersion(0, 10, 0, 1) - V0_10_1_0 = newKafkaVersion(0, 10, 1, 0) - V0_10_1_1 = newKafkaVersion(0, 10, 1, 1) - V0_10_2_0 = newKafkaVersion(0, 10, 2, 0) - V0_10_2_1 = newKafkaVersion(0, 10, 2, 1) - V0_11_0_0 = newKafkaVersion(0, 11, 0, 0) - V0_11_0_1 = newKafkaVersion(0, 11, 0, 1) - V0_11_0_2 = newKafkaVersion(0, 11, 0, 2) - V1_0_0_0 = newKafkaVersion(1, 0, 0, 0) - V1_1_0_0 = newKafkaVersion(1, 1, 0, 0) - - SupportedVersions = []KafkaVersion{ - V0_8_2_0, - V0_8_2_1, - V0_8_2_2, - V0_9_0_0, - V0_9_0_1, - V0_10_0_0, - V0_10_0_1, - V0_10_1_0, - V0_10_1_1, - V0_10_2_0, - V0_10_2_1, - V0_11_0_0, - V0_11_0_1, - V0_11_0_2, - V1_0_0_0, - V1_1_0_0, - } - MinVersion = V0_8_2_0 - MaxVersion = V1_1_0_0 -) - -func ParseKafkaVersion(s string) (KafkaVersion, error) { - if len(s) < 5 { - return MinVersion, fmt.Errorf("invalid version `%s`", s) - } - var major, minor, veryMinor, patch uint - var err error - if s[0] == '0' { - err = scanKafkaVersion(s, `^0\.\d+\.\d+\.\d+$`, "0.%d.%d.%d", [3]*uint{&minor, &veryMinor, &patch}) - } else { - err = scanKafkaVersion(s, `^\d+\.\d+\.\d+$`, "%d.%d.%d", [3]*uint{&major, &minor, &veryMinor}) - } - if err != nil { - return MinVersion, err - } - return newKafkaVersion(major, minor, veryMinor, patch), nil -} - -func scanKafkaVersion(s string, pattern string, format string, v [3]*uint) error { - if !regexp.MustCompile(pattern).MatchString(s) { - return fmt.Errorf("invalid version `%s`", s) - } - _, err := fmt.Sscanf(s, format, v[0], v[1], v[2]) - return err -} - -func (v KafkaVersion) String() string { - if v.version[0] == 0 { - return fmt.Sprintf("0.%d.%d.%d", v.version[1], v.version[2], v.version[3]) - } else { - return fmt.Sprintf("%d.%d.%d", v.version[0], v.version[1], v.version[2]) - } -} diff --git a/vendor/github.com/bsm/sarama-cluster/LICENSE b/vendor/github.com/bsm/sarama-cluster/LICENSE deleted file mode 100644 index 127751c47a8..00000000000 --- a/vendor/github.com/bsm/sarama-cluster/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -(The MIT License) - -Copyright (c) 2017 Black Square Media Ltd - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -'Software'), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/bsm/sarama-cluster/balancer.go b/vendor/github.com/bsm/sarama-cluster/balancer.go deleted file mode 100644 index 0f9b445ee45..00000000000 --- a/vendor/github.com/bsm/sarama-cluster/balancer.go +++ /dev/null @@ -1,174 +0,0 @@ -package cluster - -import ( - "math" - "sort" - - "github.com/Shopify/sarama" -) - -// NotificationType defines the type of notification -type NotificationType uint8 - -// String describes the notification type -func (t NotificationType) String() string { - switch t { - case RebalanceStart: - return "rebalance start" - case RebalanceOK: - return "rebalance OK" - case RebalanceError: - return "rebalance error" - } - return "unknown" -} - -const ( - UnknownNotification NotificationType = iota - RebalanceStart - RebalanceOK - RebalanceError -) - -// Notification are state events emitted by the consumers on rebalance -type Notification struct { - // Type exposes the notification type - Type NotificationType - - // Claimed contains topic/partitions that were claimed by this rebalance cycle - Claimed map[string][]int32 - - // Released contains topic/partitions that were released as part of this rebalance cycle - Released map[string][]int32 - - // Current are topic/partitions that are currently claimed to the consumer - Current map[string][]int32 -} - -func newNotification(current map[string][]int32) *Notification { - return &Notification{ - Type: RebalanceStart, - Current: current, - } -} - -func (n *Notification) success(current map[string][]int32) *Notification { - o := &Notification{ - Type: RebalanceOK, - Claimed: make(map[string][]int32), - Released: make(map[string][]int32), - Current: current, - } - for topic, partitions := range current { - o.Claimed[topic] = int32Slice(partitions).Diff(int32Slice(n.Current[topic])) - } - for topic, partitions := range n.Current { - o.Released[topic] = int32Slice(partitions).Diff(int32Slice(current[topic])) - } - return o -} - -// -------------------------------------------------------------------- - -type topicInfo struct { - Partitions []int32 - MemberIDs []string -} - -func (info topicInfo) Perform(s Strategy) map[string][]int32 { - if s == StrategyRoundRobin { - return info.RoundRobin() - } - return info.Ranges() -} - -func (info topicInfo) Ranges() map[string][]int32 { - sort.Strings(info.MemberIDs) - - mlen := len(info.MemberIDs) - plen := len(info.Partitions) - res := make(map[string][]int32, mlen) - - for pos, memberID := range info.MemberIDs { - n, i := float64(plen)/float64(mlen), float64(pos) - min := int(math.Floor(i*n + 0.5)) - max := int(math.Floor((i+1)*n + 0.5)) - sub := info.Partitions[min:max] - if len(sub) > 0 { - res[memberID] = sub - } - } - return res -} - -func (info topicInfo) RoundRobin() map[string][]int32 { - sort.Strings(info.MemberIDs) - - mlen := len(info.MemberIDs) - res := make(map[string][]int32, mlen) - for i, pnum := range info.Partitions { - memberID := info.MemberIDs[i%mlen] - res[memberID] = append(res[memberID], pnum) - } - return res -} - -// -------------------------------------------------------------------- - -type balancer struct { - client sarama.Client - topics map[string]topicInfo -} - -func newBalancerFromMeta(client sarama.Client, members map[string]sarama.ConsumerGroupMemberMetadata) (*balancer, error) { - balancer := newBalancer(client) - for memberID, meta := range members { - for _, topic := range meta.Topics { - if err := balancer.Topic(topic, memberID); err != nil { - return nil, err - } - } - } - return balancer, nil -} - -func newBalancer(client sarama.Client) *balancer { - return &balancer{ - client: client, - topics: make(map[string]topicInfo), - } -} - -func (r *balancer) Topic(name string, memberID string) error { - topic, ok := r.topics[name] - if !ok { - nums, err := r.client.Partitions(name) - if err != nil { - return err - } - topic = topicInfo{ - Partitions: nums, - MemberIDs: make([]string, 0, 1), - } - } - topic.MemberIDs = append(topic.MemberIDs, memberID) - r.topics[name] = topic - return nil -} - -func (r *balancer) Perform(s Strategy) map[string]map[string][]int32 { - if r == nil { - return nil - } - - res := make(map[string]map[string][]int32, 1) - for topic, info := range r.topics { - for memberID, partitions := range info.Perform(s) { - if _, ok := res[memberID]; !ok { - res[memberID] = make(map[string][]int32, 1) - } - res[memberID][topic] = partitions - } - } - return res -} diff --git a/vendor/github.com/bsm/sarama-cluster/client.go b/vendor/github.com/bsm/sarama-cluster/client.go deleted file mode 100644 index 42ffb30c01a..00000000000 --- a/vendor/github.com/bsm/sarama-cluster/client.go +++ /dev/null @@ -1,50 +0,0 @@ -package cluster - -import ( - "errors" - "sync/atomic" - - "github.com/Shopify/sarama" -) - -var errClientInUse = errors.New("cluster: client is already used by another consumer") - -// Client is a group client -type Client struct { - sarama.Client - config Config - - inUse uint32 -} - -// NewClient creates a new client instance -func NewClient(addrs []string, config *Config) (*Client, error) { - if config == nil { - config = NewConfig() - } - - if err := config.Validate(); err != nil { - return nil, err - } - - client, err := sarama.NewClient(addrs, &config.Config) - if err != nil { - return nil, err - } - - return &Client{Client: client, config: *config}, nil -} - -// ClusterConfig returns the cluster configuration. -func (c *Client) ClusterConfig() *Config { - cfg := c.config - return &cfg -} - -func (c *Client) claim() bool { - return atomic.CompareAndSwapUint32(&c.inUse, 0, 1) -} - -func (c *Client) release() { - atomic.CompareAndSwapUint32(&c.inUse, 1, 0) -} diff --git a/vendor/github.com/bsm/sarama-cluster/cluster.go b/vendor/github.com/bsm/sarama-cluster/cluster.go deleted file mode 100644 index adcf0e9c1cf..00000000000 --- a/vendor/github.com/bsm/sarama-cluster/cluster.go +++ /dev/null @@ -1,25 +0,0 @@ -package cluster - -// Strategy for partition to consumer assignement -type Strategy string - -const ( - // StrategyRange is the default and assigns partition ranges to consumers. - // Example with six partitions and two consumers: - // C1: [0, 1, 2] - // C2: [3, 4, 5] - StrategyRange Strategy = "range" - - // StrategyRoundRobin assigns partitions by alternating over consumers. - // Example with six partitions and two consumers: - // C1: [0, 2, 4] - // C2: [1, 3, 5] - StrategyRoundRobin Strategy = "roundrobin" -) - -// Error instances are wrappers for internal errors with a context and -// may be returned through the consumer's Errors() channel -type Error struct { - Ctx string - error -} diff --git a/vendor/github.com/bsm/sarama-cluster/config.go b/vendor/github.com/bsm/sarama-cluster/config.go deleted file mode 100644 index 084b835f710..00000000000 --- a/vendor/github.com/bsm/sarama-cluster/config.go +++ /dev/null @@ -1,146 +0,0 @@ -package cluster - -import ( - "regexp" - "time" - - "github.com/Shopify/sarama" -) - -var minVersion = sarama.V0_9_0_0 - -type ConsumerMode uint8 - -const ( - ConsumerModeMultiplex ConsumerMode = iota - ConsumerModePartitions -) - -// Config extends sarama.Config with Group specific namespace -type Config struct { - sarama.Config - - // Group is the namespace for group management properties - Group struct { - - // The strategy to use for the allocation of partitions to consumers (defaults to StrategyRange) - PartitionStrategy Strategy - - // By default, messages and errors from the subscribed topics and partitions are all multiplexed and - // made available through the consumer's Messages() and Errors() channels. - // - // Users who require low-level access can enable ConsumerModePartitions where individual partitions - // are exposed on the Partitions() channel. Messages and errors must then be consumed on the partitions - // themselves. - Mode ConsumerMode - - Offsets struct { - Retry struct { - // The numer retries when committing offsets (defaults to 3). - Max int - } - Synchronization struct { - // The duration allowed for other clients to commit their offsets before resumption in this client, e.g. during a rebalance - // NewConfig sets this to the Consumer.MaxProcessingTime duration of the Sarama configuration - DwellTime time.Duration - } - } - - Session struct { - // The allowed session timeout for registered consumers (defaults to 30s). - // Must be within the allowed server range. - Timeout time.Duration - } - - Heartbeat struct { - // Interval between each heartbeat (defaults to 3s). It should be no more - // than 1/3rd of the Group.Session.Timout setting - Interval time.Duration - } - - // Return specifies which group channels will be populated. If they are set to true, - // you must read from the respective channels to prevent deadlock. - Return struct { - // If enabled, rebalance notification will be returned on the - // Notifications channel (default disabled). - Notifications bool - } - - Topics struct { - // An additional whitelist of topics to subscribe to. - Whitelist *regexp.Regexp - // An additional blacklist of topics to avoid. If set, this will precede over - // the Whitelist setting. - Blacklist *regexp.Regexp - } - - Member struct { - // Custom metadata to include when joining the group. The user data for all joined members - // can be retrieved by sending a DescribeGroupRequest to the broker that is the - // coordinator for the group. - UserData []byte - } - } -} - -// NewConfig returns a new configuration instance with sane defaults. -func NewConfig() *Config { - c := &Config{ - Config: *sarama.NewConfig(), - } - c.Group.PartitionStrategy = StrategyRange - c.Group.Offsets.Retry.Max = 3 - c.Group.Offsets.Synchronization.DwellTime = c.Consumer.MaxProcessingTime - c.Group.Session.Timeout = 30 * time.Second - c.Group.Heartbeat.Interval = 3 * time.Second - c.Config.Version = minVersion - return c -} - -// Validate checks a Config instance. It will return a -// sarama.ConfigurationError if the specified values don't make sense. -func (c *Config) Validate() error { - if c.Group.Heartbeat.Interval%time.Millisecond != 0 { - sarama.Logger.Println("Group.Heartbeat.Interval only supports millisecond precision; nanoseconds will be truncated.") - } - if c.Group.Session.Timeout%time.Millisecond != 0 { - sarama.Logger.Println("Group.Session.Timeout only supports millisecond precision; nanoseconds will be truncated.") - } - if c.Group.PartitionStrategy != StrategyRange && c.Group.PartitionStrategy != StrategyRoundRobin { - sarama.Logger.Println("Group.PartitionStrategy is not supported; range will be assumed.") - } - if !c.Version.IsAtLeast(minVersion) { - sarama.Logger.Println("Version is not supported; 0.9. will be assumed.") - c.Version = minVersion - } - if err := c.Config.Validate(); err != nil { - return err - } - - // validate the Group values - switch { - case c.Group.Offsets.Retry.Max < 0: - return sarama.ConfigurationError("Group.Offsets.Retry.Max must be >= 0") - case c.Group.Offsets.Synchronization.DwellTime <= 0: - return sarama.ConfigurationError("Group.Offsets.Synchronization.DwellTime must be > 0") - case c.Group.Offsets.Synchronization.DwellTime > 10*time.Minute: - return sarama.ConfigurationError("Group.Offsets.Synchronization.DwellTime must be <= 10m") - case c.Group.Heartbeat.Interval <= 0: - return sarama.ConfigurationError("Group.Heartbeat.Interval must be > 0") - case c.Group.Session.Timeout <= 0: - return sarama.ConfigurationError("Group.Session.Timeout must be > 0") - case !c.Metadata.Full && c.Group.Topics.Whitelist != nil: - return sarama.ConfigurationError("Metadata.Full must be enabled when Group.Topics.Whitelist is used") - case !c.Metadata.Full && c.Group.Topics.Blacklist != nil: - return sarama.ConfigurationError("Metadata.Full must be enabled when Group.Topics.Blacklist is used") - } - - // ensure offset is correct - switch c.Consumer.Offsets.Initial { - case sarama.OffsetOldest, sarama.OffsetNewest: - default: - return sarama.ConfigurationError("Consumer.Offsets.Initial must be either OffsetOldest or OffsetNewest") - } - - return nil -} diff --git a/vendor/github.com/bsm/sarama-cluster/consumer.go b/vendor/github.com/bsm/sarama-cluster/consumer.go deleted file mode 100644 index 13500cc8e75..00000000000 --- a/vendor/github.com/bsm/sarama-cluster/consumer.go +++ /dev/null @@ -1,924 +0,0 @@ -package cluster - -import ( - "sort" - "sync" - "sync/atomic" - "time" - - "github.com/Shopify/sarama" -) - -// Consumer is a cluster group consumer -type Consumer struct { - client *Client - ownClient bool - - consumer sarama.Consumer - subs *partitionMap - - consumerID string - groupID string - - memberID string - generationID int32 - membershipMu sync.RWMutex - - coreTopics []string - extraTopics []string - - dying, dead chan none - closeOnce sync.Once - - consuming int32 - messages chan *sarama.ConsumerMessage - errors chan error - partitions chan PartitionConsumer - notifications chan *Notification - - commitMu sync.Mutex -} - -// NewConsumer initializes a new consumer -func NewConsumer(addrs []string, groupID string, topics []string, config *Config) (*Consumer, error) { - client, err := NewClient(addrs, config) - if err != nil { - return nil, err - } - - consumer, err := NewConsumerFromClient(client, groupID, topics) - if err != nil { - return nil, err - } - consumer.ownClient = true - return consumer, nil -} - -// NewConsumerFromClient initializes a new consumer from an existing client. -// -// Please note that clients cannot be shared between consumers (due to Kafka internals), -// they can only be re-used which requires the user to call Close() on the first consumer -// before using this method again to initialize another one. Attempts to use a client with -// more than one consumer at a time will return errors. -func NewConsumerFromClient(client *Client, groupID string, topics []string) (*Consumer, error) { - if !client.claim() { - return nil, errClientInUse - } - - consumer, err := sarama.NewConsumerFromClient(client.Client) - if err != nil { - client.release() - return nil, err - } - - sort.Strings(topics) - c := &Consumer{ - client: client, - consumer: consumer, - subs: newPartitionMap(), - groupID: groupID, - - coreTopics: topics, - - dying: make(chan none), - dead: make(chan none), - - messages: make(chan *sarama.ConsumerMessage), - errors: make(chan error, client.config.ChannelBufferSize), - partitions: make(chan PartitionConsumer, 1), - notifications: make(chan *Notification), - } - if err := c.client.RefreshCoordinator(groupID); err != nil { - client.release() - return nil, err - } - - go c.mainLoop() - return c, nil -} - -// Messages returns the read channel for the messages that are returned by -// the broker. -// -// This channel will only return if Config.Group.Mode option is set to -// ConsumerModeMultiplex (default). -func (c *Consumer) Messages() <-chan *sarama.ConsumerMessage { return c.messages } - -// Partitions returns the read channels for individual partitions of this broker. -// -// This will channel will only return if Config.Group.Mode option is set to -// ConsumerModePartitions. -// -// The Partitions() channel must be listened to for the life of this consumer; -// when a rebalance happens old partitions will be closed (naturally come to -// completion) and new ones will be emitted. The returned channel will only close -// when the consumer is completely shut down. -func (c *Consumer) Partitions() <-chan PartitionConsumer { return c.partitions } - -// Errors returns a read channel of errors that occur during offset management, if -// enabled. By default, errors are logged and not returned over this channel. If -// you want to implement any custom error handling, set your config's -// Consumer.Return.Errors setting to true, and read from this channel. -func (c *Consumer) Errors() <-chan error { return c.errors } - -// Notifications returns a channel of Notifications that occur during consumer -// rebalancing. Notifications will only be emitted over this channel, if your config's -// Group.Return.Notifications setting to true. -func (c *Consumer) Notifications() <-chan *Notification { return c.notifications } - -// HighWaterMarks returns the current high water marks for each topic and partition -// Consistency between partitions is not guaranteed since high water marks are updated separately. -func (c *Consumer) HighWaterMarks() map[string]map[int32]int64 { return c.consumer.HighWaterMarks() } - -// MarkOffset marks the provided message as processed, alongside a metadata string -// that represents the state of the partition consumer at that point in time. The -// metadata string can be used by another consumer to restore that state, so it -// can resume consumption. -// -// Note: calling MarkOffset does not necessarily commit the offset to the backend -// store immediately for efficiency reasons, and it may never be committed if -// your application crashes. This means that you may end up processing the same -// message twice, and your processing should ideally be idempotent. -func (c *Consumer) MarkOffset(msg *sarama.ConsumerMessage, metadata string) { - sub := c.subs.Fetch(msg.Topic, msg.Partition) - if sub != nil { - sub.MarkOffset(msg.Offset+1, metadata) - } -} - -// MarkPartitionOffset marks an offset of the provided topic/partition as processed. -// See MarkOffset for additional explanation. -func (c *Consumer) MarkPartitionOffset(topic string, partition int32, offset int64, metadata string) { - sub := c.subs.Fetch(topic, partition) - if sub != nil { - sub.MarkOffset(offset+1, metadata) - } -} - -// MarkOffsets marks stashed offsets as processed. -// See MarkOffset for additional explanation. -func (c *Consumer) MarkOffsets(s *OffsetStash) { - s.mu.Lock() - defer s.mu.Unlock() - - for tp, info := range s.offsets { - sub := c.subs.Fetch(tp.Topic, tp.Partition) - if sub != nil { - sub.MarkOffset(info.Offset+1, info.Metadata) - } - delete(s.offsets, tp) - } -} - -// ResetOffsets marks the provided message as processed, alongside a metadata string -// that represents the state of the partition consumer at that point in time. The -// metadata string can be used by another consumer to restore that state, so it -// can resume consumption. -// -// Difference between ResetOffset and MarkOffset is that it allows to rewind to an earlier offset -func (c *Consumer) ResetOffset(msg *sarama.ConsumerMessage, metadata string) { - sub := c.subs.Fetch(msg.Topic, msg.Partition) - if sub != nil { - sub.ResetOffset(msg.Offset+1, metadata) - } -} - -// ResetPartitionOffset marks an offset of the provided topic/partition as processed. -// See ResetOffset for additional explanation. -func (c *Consumer) ResetPartitionOffset(topic string, partition int32, offset int64, metadata string) { - sub := c.subs.Fetch(topic, partition) - if sub != nil { - sub.ResetOffset(offset+1, metadata) - } -} - -// ResetOffsets marks stashed offsets as processed. -// See ResetOffset for additional explanation. -func (c *Consumer) ResetOffsets(s *OffsetStash) { - s.mu.Lock() - defer s.mu.Unlock() - - for tp, info := range s.offsets { - sub := c.subs.Fetch(tp.Topic, tp.Partition) - if sub != nil { - sub.ResetOffset(info.Offset+1, info.Metadata) - } - delete(s.offsets, tp) - } -} - -// Subscriptions returns the consumed topics and partitions -func (c *Consumer) Subscriptions() map[string][]int32 { - return c.subs.Info() -} - -// CommitOffsets allows to manually commit previously marked offsets. By default there is no -// need to call this function as the consumer will commit offsets automatically -// using the Config.Consumer.Offsets.CommitInterval setting. -// -// Please be aware that calling this function during an internal rebalance cycle may return -// broker errors (e.g. sarama.ErrUnknownMemberId or sarama.ErrIllegalGeneration). -func (c *Consumer) CommitOffsets() error { - c.commitMu.Lock() - defer c.commitMu.Unlock() - - memberID, generationID := c.membership() - req := &sarama.OffsetCommitRequest{ - Version: 2, - ConsumerGroup: c.groupID, - ConsumerGroupGeneration: generationID, - ConsumerID: memberID, - RetentionTime: -1, - } - - if ns := c.client.config.Consumer.Offsets.Retention; ns != 0 { - req.RetentionTime = int64(ns / time.Millisecond) - } - - snap := c.subs.Snapshot() - dirty := false - for tp, state := range snap { - if state.Dirty { - dirty = true - req.AddBlock(tp.Topic, tp.Partition, state.Info.Offset, 0, state.Info.Metadata) - } - } - if !dirty { - return nil - } - - broker, err := c.client.Coordinator(c.groupID) - if err != nil { - c.closeCoordinator(broker, err) - return err - } - - resp, err := broker.CommitOffset(req) - if err != nil { - c.closeCoordinator(broker, err) - return err - } - - for topic, errs := range resp.Errors { - for partition, kerr := range errs { - if kerr != sarama.ErrNoError { - err = kerr - } else if state, ok := snap[topicPartition{topic, partition}]; ok { - sub := c.subs.Fetch(topic, partition) - if sub != nil { - sub.MarkCommitted(state.Info.Offset) - } - } - } - } - return err -} - -// Close safely closes the consumer and releases all resources -func (c *Consumer) Close() (err error) { - c.closeOnce.Do(func() { - close(c.dying) - <-c.dead - - if e := c.release(); e != nil { - err = e - } - if e := c.consumer.Close(); e != nil { - err = e - } - close(c.messages) - close(c.errors) - - if e := c.leaveGroup(); e != nil { - err = e - } - close(c.partitions) - close(c.notifications) - - // drain - for range c.messages { - } - for range c.errors { - } - for p := range c.partitions { - _ = p.Close() - } - for range c.notifications { - } - - c.client.release() - if c.ownClient { - if e := c.client.Close(); e != nil { - err = e - } - } - }) - return -} - -func (c *Consumer) mainLoop() { - defer close(c.dead) - defer atomic.StoreInt32(&c.consuming, 0) - - for { - atomic.StoreInt32(&c.consuming, 0) - - // Check if close was requested - select { - case <-c.dying: - return - default: - } - - // Start next consume cycle - c.nextTick() - } -} - -func (c *Consumer) nextTick() { - // Remember previous subscriptions - var notification *Notification - if c.client.config.Group.Return.Notifications { - notification = newNotification(c.subs.Info()) - } - - // Refresh coordinator - if err := c.refreshCoordinator(); err != nil { - c.rebalanceError(err, nil) - return - } - - // Release subscriptions - if err := c.release(); err != nil { - c.rebalanceError(err, nil) - return - } - - // Issue rebalance start notification - if c.client.config.Group.Return.Notifications { - c.handleNotification(notification) - } - - // Rebalance, fetch new subscriptions - subs, err := c.rebalance() - if err != nil { - c.rebalanceError(err, notification) - return - } - - // Coordinate loops, make sure everything is - // stopped on exit - tomb := newLoopTomb() - defer tomb.Close() - - // Start the heartbeat - tomb.Go(c.hbLoop) - - // Subscribe to topic/partitions - if err := c.subscribe(tomb, subs); err != nil { - c.rebalanceError(err, notification) - return - } - - // Update/issue notification with new claims - if c.client.config.Group.Return.Notifications { - notification = notification.success(subs) - c.handleNotification(notification) - } - - // Start topic watcher loop - tomb.Go(c.twLoop) - - // Start consuming and committing offsets - tomb.Go(c.cmLoop) - atomic.StoreInt32(&c.consuming, 1) - - // Wait for signals - select { - case <-tomb.Dying(): - case <-c.dying: - } -} - -// heartbeat loop, triggered by the mainLoop -func (c *Consumer) hbLoop(stopped <-chan none) { - ticker := time.NewTicker(c.client.config.Group.Heartbeat.Interval) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - switch err := c.heartbeat(); err { - case nil, sarama.ErrNoError: - case sarama.ErrNotCoordinatorForConsumer, sarama.ErrRebalanceInProgress: - return - default: - c.handleError(&Error{Ctx: "heartbeat", error: err}) - return - } - case <-stopped: - return - case <-c.dying: - return - } - } -} - -// topic watcher loop, triggered by the mainLoop -func (c *Consumer) twLoop(stopped <-chan none) { - ticker := time.NewTicker(c.client.config.Metadata.RefreshFrequency / 2) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - topics, err := c.client.Topics() - if err != nil { - c.handleError(&Error{Ctx: "topics", error: err}) - return - } - - for _, topic := range topics { - if !c.isKnownCoreTopic(topic) && - !c.isKnownExtraTopic(topic) && - c.isPotentialExtraTopic(topic) { - return - } - } - case <-stopped: - return - case <-c.dying: - return - } - } -} - -// commit loop, triggered by the mainLoop -func (c *Consumer) cmLoop(stopped <-chan none) { - ticker := time.NewTicker(c.client.config.Consumer.Offsets.CommitInterval) - defer ticker.Stop() - - for { - select { - case <-ticker.C: - if err := c.commitOffsetsWithRetry(c.client.config.Group.Offsets.Retry.Max); err != nil { - c.handleError(&Error{Ctx: "commit", error: err}) - return - } - case <-stopped: - return - case <-c.dying: - return - } - } -} - -func (c *Consumer) rebalanceError(err error, n *Notification) { - if n != nil { - n.Type = RebalanceError - c.handleNotification(n) - } - - switch err { - case sarama.ErrRebalanceInProgress: - default: - c.handleError(&Error{Ctx: "rebalance", error: err}) - } - - select { - case <-c.dying: - case <-time.After(c.client.config.Metadata.Retry.Backoff): - } -} - -func (c *Consumer) handleNotification(n *Notification) { - if c.client.config.Group.Return.Notifications { - select { - case c.notifications <- n: - case <-c.dying: - return - } - } -} - -func (c *Consumer) handleError(e *Error) { - if c.client.config.Consumer.Return.Errors { - select { - case c.errors <- e: - case <-c.dying: - return - } - } else { - sarama.Logger.Printf("%s error: %s\n", e.Ctx, e.Error()) - } -} - -// Releases the consumer and commits offsets, called from rebalance() and Close() -func (c *Consumer) release() (err error) { - // Stop all consumers - c.subs.Stop() - - // Clear subscriptions on exit - defer c.subs.Clear() - - // Wait for messages to be processed - timeout := time.NewTimer(c.client.config.Group.Offsets.Synchronization.DwellTime) - defer timeout.Stop() - - select { - case <-c.dying: - case <-timeout.C: - } - - // Commit offsets, continue on errors - if e := c.commitOffsetsWithRetry(c.client.config.Group.Offsets.Retry.Max); e != nil { - err = e - } - - return -} - -// -------------------------------------------------------------------- - -// Performs a heartbeat, part of the mainLoop() -func (c *Consumer) heartbeat() error { - broker, err := c.client.Coordinator(c.groupID) - if err != nil { - c.closeCoordinator(broker, err) - return err - } - - memberID, generationID := c.membership() - resp, err := broker.Heartbeat(&sarama.HeartbeatRequest{ - GroupId: c.groupID, - MemberId: memberID, - GenerationId: generationID, - }) - if err != nil { - c.closeCoordinator(broker, err) - return err - } - return resp.Err -} - -// Performs a rebalance, part of the mainLoop() -func (c *Consumer) rebalance() (map[string][]int32, error) { - memberID, _ := c.membership() - sarama.Logger.Printf("cluster/consumer %s rebalance\n", memberID) - - allTopics, err := c.client.Topics() - if err != nil { - return nil, err - } - c.extraTopics = c.selectExtraTopics(allTopics) - sort.Strings(c.extraTopics) - - // Re-join consumer group - strategy, err := c.joinGroup() - switch { - case err == sarama.ErrUnknownMemberId: - c.membershipMu.Lock() - c.memberID = "" - c.membershipMu.Unlock() - return nil, err - case err != nil: - return nil, err - } - - // Sync consumer group state, fetch subscriptions - subs, err := c.syncGroup(strategy) - switch { - case err == sarama.ErrRebalanceInProgress: - return nil, err - case err != nil: - _ = c.leaveGroup() - return nil, err - } - return subs, nil -} - -// Performs the subscription, part of the mainLoop() -func (c *Consumer) subscribe(tomb *loopTomb, subs map[string][]int32) error { - // fetch offsets - offsets, err := c.fetchOffsets(subs) - if err != nil { - _ = c.leaveGroup() - return err - } - - // create consumers in parallel - var mu sync.Mutex - var wg sync.WaitGroup - - for topic, partitions := range subs { - for _, partition := range partitions { - wg.Add(1) - - info := offsets[topic][partition] - go func(topic string, partition int32) { - if e := c.createConsumer(tomb, topic, partition, info); e != nil { - mu.Lock() - err = e - mu.Unlock() - } - wg.Done() - }(topic, partition) - } - } - wg.Wait() - - if err != nil { - _ = c.release() - _ = c.leaveGroup() - } - return err -} - -// -------------------------------------------------------------------- - -// Send a request to the broker to join group on rebalance() -func (c *Consumer) joinGroup() (*balancer, error) { - memberID, _ := c.membership() - req := &sarama.JoinGroupRequest{ - GroupId: c.groupID, - MemberId: memberID, - SessionTimeout: int32(c.client.config.Group.Session.Timeout / time.Millisecond), - ProtocolType: "consumer", - } - - meta := &sarama.ConsumerGroupMemberMetadata{ - Version: 1, - Topics: append(c.coreTopics, c.extraTopics...), - UserData: c.client.config.Group.Member.UserData, - } - err := req.AddGroupProtocolMetadata(string(StrategyRange), meta) - if err != nil { - return nil, err - } - err = req.AddGroupProtocolMetadata(string(StrategyRoundRobin), meta) - if err != nil { - return nil, err - } - - broker, err := c.client.Coordinator(c.groupID) - if err != nil { - c.closeCoordinator(broker, err) - return nil, err - } - - resp, err := broker.JoinGroup(req) - if err != nil { - c.closeCoordinator(broker, err) - return nil, err - } else if resp.Err != sarama.ErrNoError { - c.closeCoordinator(broker, resp.Err) - return nil, resp.Err - } - - var strategy *balancer - if resp.LeaderId == resp.MemberId { - members, err := resp.GetMembers() - if err != nil { - return nil, err - } - - strategy, err = newBalancerFromMeta(c.client, members) - if err != nil { - return nil, err - } - } - - c.membershipMu.Lock() - c.memberID = resp.MemberId - c.generationID = resp.GenerationId - c.membershipMu.Unlock() - - return strategy, nil -} - -// Send a request to the broker to sync the group on rebalance(). -// Returns a list of topics and partitions to consume. -func (c *Consumer) syncGroup(strategy *balancer) (map[string][]int32, error) { - memberID, generationID := c.membership() - req := &sarama.SyncGroupRequest{ - GroupId: c.groupID, - MemberId: memberID, - GenerationId: generationID, - } - - for memberID, topics := range strategy.Perform(c.client.config.Group.PartitionStrategy) { - if err := req.AddGroupAssignmentMember(memberID, &sarama.ConsumerGroupMemberAssignment{ - Version: 1, - Topics: topics, - }); err != nil { - return nil, err - } - } - - broker, err := c.client.Coordinator(c.groupID) - if err != nil { - c.closeCoordinator(broker, err) - return nil, err - } - - resp, err := broker.SyncGroup(req) - if err != nil { - c.closeCoordinator(broker, err) - return nil, err - } else if resp.Err != sarama.ErrNoError { - c.closeCoordinator(broker, resp.Err) - return nil, resp.Err - } - - // Return if there is nothing to subscribe to - if len(resp.MemberAssignment) == 0 { - return nil, nil - } - - // Get assigned subscriptions - members, err := resp.GetMemberAssignment() - if err != nil { - return nil, err - } - - // Sort partitions, for each topic - for topic := range members.Topics { - sort.Sort(int32Slice(members.Topics[topic])) - } - return members.Topics, nil -} - -// Fetches latest committed offsets for all subscriptions -func (c *Consumer) fetchOffsets(subs map[string][]int32) (map[string]map[int32]offsetInfo, error) { - offsets := make(map[string]map[int32]offsetInfo, len(subs)) - req := &sarama.OffsetFetchRequest{ - Version: 1, - ConsumerGroup: c.groupID, - } - - for topic, partitions := range subs { - offsets[topic] = make(map[int32]offsetInfo, len(partitions)) - for _, partition := range partitions { - offsets[topic][partition] = offsetInfo{Offset: -1} - req.AddPartition(topic, partition) - } - } - - broker, err := c.client.Coordinator(c.groupID) - if err != nil { - c.closeCoordinator(broker, err) - return nil, err - } - - resp, err := broker.FetchOffset(req) - if err != nil { - c.closeCoordinator(broker, err) - return nil, err - } - - for topic, partitions := range subs { - for _, partition := range partitions { - block := resp.GetBlock(topic, partition) - if block == nil { - return nil, sarama.ErrIncompleteResponse - } - - if block.Err == sarama.ErrNoError { - offsets[topic][partition] = offsetInfo{Offset: block.Offset, Metadata: block.Metadata} - } else { - return nil, block.Err - } - } - } - return offsets, nil -} - -// Send a request to the broker to leave the group on failes rebalance() and on Close() -func (c *Consumer) leaveGroup() error { - broker, err := c.client.Coordinator(c.groupID) - if err != nil { - c.closeCoordinator(broker, err) - return err - } - - memberID, _ := c.membership() - if _, err = broker.LeaveGroup(&sarama.LeaveGroupRequest{ - GroupId: c.groupID, - MemberId: memberID, - }); err != nil { - c.closeCoordinator(broker, err) - } - return err -} - -// -------------------------------------------------------------------- - -func (c *Consumer) createConsumer(tomb *loopTomb, topic string, partition int32, info offsetInfo) error { - memberID, _ := c.membership() - sarama.Logger.Printf("cluster/consumer %s consume %s/%d from %d\n", memberID, topic, partition, info.NextOffset(c.client.config.Consumer.Offsets.Initial)) - - // Create partitionConsumer - pc, err := newPartitionConsumer(c.consumer, topic, partition, info, c.client.config.Consumer.Offsets.Initial) - if err != nil { - return err - } - - // Store in subscriptions - c.subs.Store(topic, partition, pc) - - // Start partition consumer goroutine - tomb.Go(func(stopper <-chan none) { - if c.client.config.Group.Mode == ConsumerModePartitions { - pc.WaitFor(stopper, c.errors) - } else { - pc.Multiplex(stopper, c.messages, c.errors) - } - }) - - if c.client.config.Group.Mode == ConsumerModePartitions { - c.partitions <- pc - } - return nil -} - -func (c *Consumer) commitOffsetsWithRetry(retries int) error { - err := c.CommitOffsets() - if err != nil && retries > 0 { - return c.commitOffsetsWithRetry(retries - 1) - } - return err -} - -func (c *Consumer) closeCoordinator(broker *sarama.Broker, err error) { - if broker != nil { - _ = broker.Close() - } - - switch err { - case sarama.ErrConsumerCoordinatorNotAvailable, sarama.ErrNotCoordinatorForConsumer: - _ = c.client.RefreshCoordinator(c.groupID) - } -} - -func (c *Consumer) selectExtraTopics(allTopics []string) []string { - extra := allTopics[:0] - for _, topic := range allTopics { - if !c.isKnownCoreTopic(topic) && c.isPotentialExtraTopic(topic) { - extra = append(extra, topic) - } - } - return extra -} - -func (c *Consumer) isKnownCoreTopic(topic string) bool { - pos := sort.SearchStrings(c.coreTopics, topic) - return pos < len(c.coreTopics) && c.coreTopics[pos] == topic -} - -func (c *Consumer) isKnownExtraTopic(topic string) bool { - pos := sort.SearchStrings(c.extraTopics, topic) - return pos < len(c.extraTopics) && c.extraTopics[pos] == topic -} - -func (c *Consumer) isPotentialExtraTopic(topic string) bool { - rx := c.client.config.Group.Topics - if rx.Blacklist != nil && rx.Blacklist.MatchString(topic) { - return false - } - if rx.Whitelist != nil && rx.Whitelist.MatchString(topic) { - return true - } - return false -} - -func (c *Consumer) refreshCoordinator() error { - if err := c.refreshMetadata(); err != nil { - return err - } - return c.client.RefreshCoordinator(c.groupID) -} - -func (c *Consumer) refreshMetadata() (err error) { - if c.client.config.Metadata.Full { - err = c.client.RefreshMetadata() - } else { - var topics []string - if topics, err = c.client.Topics(); err == nil && len(topics) != 0 { - err = c.client.RefreshMetadata(topics...) - } - } - - // maybe we didn't have authorization to describe all topics - switch err { - case sarama.ErrTopicAuthorizationFailed: - err = c.client.RefreshMetadata(c.coreTopics...) - } - return -} - -func (c *Consumer) membership() (memberID string, generationID int32) { - c.membershipMu.RLock() - memberID, generationID = c.memberID, c.generationID - c.membershipMu.RUnlock() - return -} diff --git a/vendor/github.com/bsm/sarama-cluster/doc.go b/vendor/github.com/bsm/sarama-cluster/doc.go deleted file mode 100644 index 9c8ff16a77e..00000000000 --- a/vendor/github.com/bsm/sarama-cluster/doc.go +++ /dev/null @@ -1,8 +0,0 @@ -/* -Package cluster provides cluster extensions for Sarama, enabing users -to consume topics across from multiple, balanced nodes. - -It requires Kafka v0.9+ and follows the steps guide, described in: -https://cwiki.apache.org/confluence/display/KAFKA/Kafka+0.9+Consumer+Rewrite+Design -*/ -package cluster diff --git a/vendor/github.com/bsm/sarama-cluster/offsets.go b/vendor/github.com/bsm/sarama-cluster/offsets.go deleted file mode 100644 index 4223ac5e012..00000000000 --- a/vendor/github.com/bsm/sarama-cluster/offsets.go +++ /dev/null @@ -1,69 +0,0 @@ -package cluster - -import ( - "sync" - - "github.com/Shopify/sarama" -) - -// OffsetStash allows to accumulate offsets and -// mark them as processed in a bulk -type OffsetStash struct { - offsets map[topicPartition]offsetInfo - mu sync.Mutex -} - -// NewOffsetStash inits a blank stash -func NewOffsetStash() *OffsetStash { - return &OffsetStash{offsets: make(map[topicPartition]offsetInfo)} -} - -// MarkOffset stashes the provided message offset -func (s *OffsetStash) MarkOffset(msg *sarama.ConsumerMessage, metadata string) { - s.MarkPartitionOffset(msg.Topic, msg.Partition, msg.Offset, metadata) -} - -// MarkPartitionOffset stashes the offset for the provided topic/partition combination -func (s *OffsetStash) MarkPartitionOffset(topic string, partition int32, offset int64, metadata string) { - s.mu.Lock() - defer s.mu.Unlock() - - key := topicPartition{Topic: topic, Partition: partition} - if info := s.offsets[key]; offset >= info.Offset { - info.Offset = offset - info.Metadata = metadata - s.offsets[key] = info - } -} - -// ResetPartitionOffset stashes the offset for the provided topic/partition combination. -// Difference between ResetPartitionOffset and MarkPartitionOffset is that, ResetPartitionOffset supports earlier offsets -func (s *OffsetStash) ResetPartitionOffset(topic string, partition int32, offset int64, metadata string) { - s.mu.Lock() - defer s.mu.Unlock() - - key := topicPartition{Topic: topic, Partition: partition} - if info := s.offsets[key]; offset <= info.Offset { - info.Offset = offset - info.Metadata = metadata - s.offsets[key] = info - } -} - -// ResetOffset stashes the provided message offset -// See ResetPartitionOffset for explanation -func (s *OffsetStash) ResetOffset(msg *sarama.ConsumerMessage, metadata string) { - s.ResetPartitionOffset(msg.Topic, msg.Partition, msg.Offset, metadata) -} - -// Offsets returns the latest stashed offsets by topic-partition -func (s *OffsetStash) Offsets() map[string]int64 { - s.mu.Lock() - defer s.mu.Unlock() - - res := make(map[string]int64, len(s.offsets)) - for tp, info := range s.offsets { - res[tp.String()] = info.Offset - } - return res -} diff --git a/vendor/github.com/bsm/sarama-cluster/partitions.go b/vendor/github.com/bsm/sarama-cluster/partitions.go deleted file mode 100644 index 987780bde1e..00000000000 --- a/vendor/github.com/bsm/sarama-cluster/partitions.go +++ /dev/null @@ -1,287 +0,0 @@ -package cluster - -import ( - "sort" - "sync" - "time" - - "github.com/Shopify/sarama" -) - -// PartitionConsumer allows code to consume individual partitions from the cluster. -// -// See docs for Consumer.Partitions() for more on how to implement this. -type PartitionConsumer interface { - sarama.PartitionConsumer - - // Topic returns the consumed topic name - Topic() string - - // Partition returns the consumed partition - Partition() int32 -} - -type partitionConsumer struct { - sarama.PartitionConsumer - - state partitionState - mu sync.Mutex - - topic string - partition int32 - - closeOnce sync.Once - closeErr error - - dying, dead chan none -} - -func newPartitionConsumer(manager sarama.Consumer, topic string, partition int32, info offsetInfo, defaultOffset int64) (*partitionConsumer, error) { - pcm, err := manager.ConsumePartition(topic, partition, info.NextOffset(defaultOffset)) - - // Resume from default offset, if requested offset is out-of-range - if err == sarama.ErrOffsetOutOfRange { - info.Offset = -1 - pcm, err = manager.ConsumePartition(topic, partition, defaultOffset) - } - if err != nil { - return nil, err - } - - return &partitionConsumer{ - PartitionConsumer: pcm, - state: partitionState{Info: info}, - - topic: topic, - partition: partition, - - dying: make(chan none), - dead: make(chan none), - }, nil -} - -// Topic implements PartitionConsumer -func (c *partitionConsumer) Topic() string { return c.topic } - -// Partition implements PartitionConsumer -func (c *partitionConsumer) Partition() int32 { return c.partition } - -// AsyncClose implements PartitionConsumer -func (c *partitionConsumer) AsyncClose() { - c.closeOnce.Do(func() { - c.closeErr = c.PartitionConsumer.Close() - close(c.dying) - }) -} - -// Close implements PartitionConsumer -func (c *partitionConsumer) Close() error { - c.AsyncClose() - <-c.dead - return c.closeErr -} - -func (c *partitionConsumer) WaitFor(stopper <-chan none, errors chan<- error) { - defer close(c.dead) - - for { - select { - case err, ok := <-c.Errors(): - if !ok { - return - } - select { - case errors <- err: - case <-stopper: - return - case <-c.dying: - return - } - case <-stopper: - return - case <-c.dying: - return - } - } -} - -func (c *partitionConsumer) Multiplex(stopper <-chan none, messages chan<- *sarama.ConsumerMessage, errors chan<- error) { - defer close(c.dead) - - for { - select { - case msg, ok := <-c.Messages(): - if !ok { - return - } - select { - case messages <- msg: - case <-stopper: - return - case <-c.dying: - return - } - case err, ok := <-c.Errors(): - if !ok { - return - } - select { - case errors <- err: - case <-stopper: - return - case <-c.dying: - return - } - case <-stopper: - return - case <-c.dying: - return - } - } -} - -func (c *partitionConsumer) State() partitionState { - if c == nil { - return partitionState{} - } - - c.mu.Lock() - state := c.state - c.mu.Unlock() - - return state -} - -func (c *partitionConsumer) MarkCommitted(offset int64) { - if c == nil { - return - } - - c.mu.Lock() - if offset == c.state.Info.Offset { - c.state.Dirty = false - } - c.mu.Unlock() -} - -func (c *partitionConsumer) MarkOffset(offset int64, metadata string) { - if c == nil { - return - } - - c.mu.Lock() - if offset > c.state.Info.Offset { - c.state.Info.Offset = offset - c.state.Info.Metadata = metadata - c.state.Dirty = true - } - c.mu.Unlock() -} - -func (c *partitionConsumer) ResetOffset(offset int64, metadata string) { - if c == nil { - return - } - - c.mu.Lock() - if offset <= c.state.Info.Offset { - c.state.Info.Offset = offset - c.state.Info.Metadata = metadata - c.state.Dirty = true - } - c.mu.Unlock() -} - -// -------------------------------------------------------------------- - -type partitionState struct { - Info offsetInfo - Dirty bool - LastCommit time.Time -} - -// -------------------------------------------------------------------- - -type partitionMap struct { - data map[topicPartition]*partitionConsumer - mu sync.RWMutex -} - -func newPartitionMap() *partitionMap { - return &partitionMap{ - data: make(map[topicPartition]*partitionConsumer), - } -} - -func (m *partitionMap) IsSubscribedTo(topic string) bool { - m.mu.RLock() - defer m.mu.RUnlock() - - for tp := range m.data { - if tp.Topic == topic { - return true - } - } - return false -} - -func (m *partitionMap) Fetch(topic string, partition int32) *partitionConsumer { - m.mu.RLock() - pc, _ := m.data[topicPartition{topic, partition}] - m.mu.RUnlock() - return pc -} - -func (m *partitionMap) Store(topic string, partition int32, pc *partitionConsumer) { - m.mu.Lock() - m.data[topicPartition{topic, partition}] = pc - m.mu.Unlock() -} - -func (m *partitionMap) Snapshot() map[topicPartition]partitionState { - m.mu.RLock() - defer m.mu.RUnlock() - - snap := make(map[topicPartition]partitionState, len(m.data)) - for tp, pc := range m.data { - snap[tp] = pc.State() - } - return snap -} - -func (m *partitionMap) Stop() { - m.mu.RLock() - defer m.mu.RUnlock() - - var wg sync.WaitGroup - for tp := range m.data { - wg.Add(1) - go func(p *partitionConsumer) { - _ = p.Close() - wg.Done() - }(m.data[tp]) - } - wg.Wait() -} - -func (m *partitionMap) Clear() { - m.mu.Lock() - for tp := range m.data { - delete(m.data, tp) - } - m.mu.Unlock() -} - -func (m *partitionMap) Info() map[string][]int32 { - info := make(map[string][]int32) - m.mu.RLock() - for tp := range m.data { - info[tp.Topic] = append(info[tp.Topic], tp.Partition) - } - m.mu.RUnlock() - - for topic := range info { - sort.Sort(int32Slice(info[topic])) - } - return info -} diff --git a/vendor/github.com/bsm/sarama-cluster/util.go b/vendor/github.com/bsm/sarama-cluster/util.go deleted file mode 100644 index e7cb5dd1b8f..00000000000 --- a/vendor/github.com/bsm/sarama-cluster/util.go +++ /dev/null @@ -1,75 +0,0 @@ -package cluster - -import ( - "fmt" - "sort" - "sync" -) - -type none struct{} - -type topicPartition struct { - Topic string - Partition int32 -} - -func (tp *topicPartition) String() string { - return fmt.Sprintf("%s-%d", tp.Topic, tp.Partition) -} - -type offsetInfo struct { - Offset int64 - Metadata string -} - -func (i offsetInfo) NextOffset(fallback int64) int64 { - if i.Offset > -1 { - return i.Offset - } - return fallback -} - -type int32Slice []int32 - -func (p int32Slice) Len() int { return len(p) } -func (p int32Slice) Less(i, j int) bool { return p[i] < p[j] } -func (p int32Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } - -func (p int32Slice) Diff(o int32Slice) (res []int32) { - on := len(o) - for _, x := range p { - n := sort.Search(on, func(i int) bool { return o[i] >= x }) - if n < on && o[n] == x { - continue - } - res = append(res, x) - } - return -} - -// -------------------------------------------------------------------- - -type loopTomb struct { - c chan none - o sync.Once - w sync.WaitGroup -} - -func newLoopTomb() *loopTomb { - return &loopTomb{c: make(chan none)} -} - -func (t *loopTomb) stop() { t.o.Do(func() { close(t.c) }) } -func (t *loopTomb) Close() { t.stop(); t.w.Wait() } - -func (t *loopTomb) Dying() <-chan none { return t.c } -func (t *loopTomb) Go(f func(<-chan none)) { - t.w.Add(1) - - go func() { - defer t.stop() - defer t.w.Done() - - f(t.c) - }() -} diff --git a/vendor/github.com/eapache/go-resiliency/LICENSE b/vendor/github.com/eapache/go-resiliency/LICENSE deleted file mode 100644 index 698a3f51397..00000000000 --- a/vendor/github.com/eapache/go-resiliency/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Evan Huus - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/vendor/github.com/eapache/go-resiliency/breaker/breaker.go b/vendor/github.com/eapache/go-resiliency/breaker/breaker.go deleted file mode 100644 index f88ca7248b0..00000000000 --- a/vendor/github.com/eapache/go-resiliency/breaker/breaker.go +++ /dev/null @@ -1,161 +0,0 @@ -// Package breaker implements the circuit-breaker resiliency pattern for Go. -package breaker - -import ( - "errors" - "sync" - "sync/atomic" - "time" -) - -// ErrBreakerOpen is the error returned from Run() when the function is not executed -// because the breaker is currently open. -var ErrBreakerOpen = errors.New("circuit breaker is open") - -const ( - closed uint32 = iota - open - halfOpen -) - -// Breaker implements the circuit-breaker resiliency pattern -type Breaker struct { - errorThreshold, successThreshold int - timeout time.Duration - - lock sync.Mutex - state uint32 - errors, successes int - lastError time.Time -} - -// New constructs a new circuit-breaker that starts closed. -// From closed, the breaker opens if "errorThreshold" errors are seen -// without an error-free period of at least "timeout". From open, the -// breaker half-closes after "timeout". From half-open, the breaker closes -// after "successThreshold" consecutive successes, or opens on a single error. -func New(errorThreshold, successThreshold int, timeout time.Duration) *Breaker { - return &Breaker{ - errorThreshold: errorThreshold, - successThreshold: successThreshold, - timeout: timeout, - } -} - -// Run will either return ErrBreakerOpen immediately if the circuit-breaker is -// already open, or it will run the given function and pass along its return -// value. It is safe to call Run concurrently on the same Breaker. -func (b *Breaker) Run(work func() error) error { - state := atomic.LoadUint32(&b.state) - - if state == open { - return ErrBreakerOpen - } - - return b.doWork(state, work) -} - -// Go will either return ErrBreakerOpen immediately if the circuit-breaker is -// already open, or it will run the given function in a separate goroutine. -// If the function is run, Go will return nil immediately, and will *not* return -// the return value of the function. It is safe to call Go concurrently on the -// same Breaker. -func (b *Breaker) Go(work func() error) error { - state := atomic.LoadUint32(&b.state) - - if state == open { - return ErrBreakerOpen - } - - // errcheck complains about ignoring the error return value, but - // that's on purpose; if you want an error from a goroutine you have to - // get it over a channel or something - go b.doWork(state, work) - - return nil -} - -func (b *Breaker) doWork(state uint32, work func() error) error { - var panicValue interface{} - - result := func() error { - defer func() { - panicValue = recover() - }() - return work() - }() - - if result == nil && panicValue == nil && state == closed { - // short-circuit the normal, success path without contending - // on the lock - return nil - } - - // oh well, I guess we have to contend on the lock - b.processResult(result, panicValue) - - if panicValue != nil { - // as close as Go lets us come to a "rethrow" although unfortunately - // we lose the original panicing location - panic(panicValue) - } - - return result -} - -func (b *Breaker) processResult(result error, panicValue interface{}) { - b.lock.Lock() - defer b.lock.Unlock() - - if result == nil && panicValue == nil { - if b.state == halfOpen { - b.successes++ - if b.successes == b.successThreshold { - b.closeBreaker() - } - } - } else { - if b.errors > 0 { - expiry := b.lastError.Add(b.timeout) - if time.Now().After(expiry) { - b.errors = 0 - } - } - - switch b.state { - case closed: - b.errors++ - if b.errors == b.errorThreshold { - b.openBreaker() - } else { - b.lastError = time.Now() - } - case halfOpen: - b.openBreaker() - } - } -} - -func (b *Breaker) openBreaker() { - b.changeState(open) - go b.timer() -} - -func (b *Breaker) closeBreaker() { - b.changeState(closed) -} - -func (b *Breaker) timer() { - time.Sleep(b.timeout) - - b.lock.Lock() - defer b.lock.Unlock() - - b.changeState(halfOpen) -} - -func (b *Breaker) changeState(newState uint32) { - b.errors = 0 - b.successes = 0 - atomic.StoreUint32(&b.state, newState) -} diff --git a/vendor/github.com/eapache/go-xerial-snappy/LICENSE b/vendor/github.com/eapache/go-xerial-snappy/LICENSE deleted file mode 100644 index 5bf3688d9e4..00000000000 --- a/vendor/github.com/eapache/go-xerial-snappy/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2016 Evan Huus - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/vendor/github.com/eapache/go-xerial-snappy/snappy.go b/vendor/github.com/eapache/go-xerial-snappy/snappy.go deleted file mode 100644 index b8f8b51fcef..00000000000 --- a/vendor/github.com/eapache/go-xerial-snappy/snappy.go +++ /dev/null @@ -1,43 +0,0 @@ -package snappy - -import ( - "bytes" - "encoding/binary" - - master "github.com/golang/snappy" -) - -var xerialHeader = []byte{130, 83, 78, 65, 80, 80, 89, 0} - -// Encode encodes data as snappy with no framing header. -func Encode(src []byte) []byte { - return master.Encode(nil, src) -} - -// Decode decodes snappy data whether it is traditional unframed -// or includes the xerial framing format. -func Decode(src []byte) ([]byte, error) { - if !bytes.Equal(src[:8], xerialHeader) { - return master.Decode(nil, src) - } - - var ( - pos = uint32(16) - max = uint32(len(src)) - dst = make([]byte, 0, len(src)) - chunk []byte - err error - ) - for pos < max { - size := binary.BigEndian.Uint32(src[pos : pos+4]) - pos += 4 - - chunk, err = master.Decode(chunk, src[pos:pos+size]) - if err != nil { - return nil, err - } - pos += size - dst = append(dst, chunk...) - } - return dst, nil -} diff --git a/vendor/github.com/eapache/queue/LICENSE b/vendor/github.com/eapache/queue/LICENSE deleted file mode 100644 index d5f36dbcaaf..00000000000 --- a/vendor/github.com/eapache/queue/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 Evan Huus - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/eapache/queue/queue.go b/vendor/github.com/eapache/queue/queue.go deleted file mode 100644 index 71d1acdf27b..00000000000 --- a/vendor/github.com/eapache/queue/queue.go +++ /dev/null @@ -1,102 +0,0 @@ -/* -Package queue provides a fast, ring-buffer queue based on the version suggested by Dariusz Górecki. -Using this instead of other, simpler, queue implementations (slice+append or linked list) provides -substantial memory and time benefits, and fewer GC pauses. - -The queue implemented here is as fast as it is for an additional reason: it is *not* thread-safe. -*/ -package queue - -// minQueueLen is smallest capacity that queue may have. -// Must be power of 2 for bitwise modulus: x % n == x & (n - 1). -const minQueueLen = 16 - -// Queue represents a single instance of the queue data structure. -type Queue struct { - buf []interface{} - head, tail, count int -} - -// New constructs and returns a new Queue. -func New() *Queue { - return &Queue{ - buf: make([]interface{}, minQueueLen), - } -} - -// Length returns the number of elements currently stored in the queue. -func (q *Queue) Length() int { - return q.count -} - -// resizes the queue to fit exactly twice its current contents -// this can result in shrinking if the queue is less than half-full -func (q *Queue) resize() { - newBuf := make([]interface{}, q.count<<1) - - if q.tail > q.head { - copy(newBuf, q.buf[q.head:q.tail]) - } else { - n := copy(newBuf, q.buf[q.head:]) - copy(newBuf[n:], q.buf[:q.tail]) - } - - q.head = 0 - q.tail = q.count - q.buf = newBuf -} - -// Add puts an element on the end of the queue. -func (q *Queue) Add(elem interface{}) { - if q.count == len(q.buf) { - q.resize() - } - - q.buf[q.tail] = elem - // bitwise modulus - q.tail = (q.tail + 1) & (len(q.buf) - 1) - q.count++ -} - -// Peek returns the element at the head of the queue. This call panics -// if the queue is empty. -func (q *Queue) Peek() interface{} { - if q.count <= 0 { - panic("queue: Peek() called on empty queue") - } - return q.buf[q.head] -} - -// Get returns the element at index i in the queue. If the index is -// invalid, the call will panic. This method accepts both positive and -// negative index values. Index 0 refers to the first element, and -// index -1 refers to the last. -func (q *Queue) Get(i int) interface{} { - // If indexing backwards, convert to positive index. - if i < 0 { - i += q.count - } - if i < 0 || i >= q.count { - panic("queue: Get() called with index out of range") - } - // bitwise modulus - return q.buf[(q.head+i)&(len(q.buf)-1)] -} - -// Remove removes and returns the element from the front of the queue. If the -// queue is empty, the call will panic. -func (q *Queue) Remove() interface{} { - if q.count <= 0 { - panic("queue: Remove() called on empty queue") - } - ret := q.buf[q.head] - q.buf[q.head] = nil - // bitwise modulus - q.head = (q.head + 1) & (len(q.buf) - 1) - q.count-- - // Resize down if buffer 1/4 full. - if len(q.buf) > minQueueLen && (q.count<<2) == len(q.buf) { - q.resize() - } - return ret -} diff --git a/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go b/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go deleted file mode 100644 index e855b1f5c4a..00000000000 --- a/vendor/github.com/golang/protobuf/protoc-gen-go/descriptor/descriptor.pb.go +++ /dev/null @@ -1,2812 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: google/protobuf/descriptor.proto - -package descriptor // import "github.com/golang/protobuf/protoc-gen-go/descriptor" - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -type FieldDescriptorProto_Type int32 - -const ( - // 0 is reserved for errors. - // Order is weird for historical reasons. - FieldDescriptorProto_TYPE_DOUBLE FieldDescriptorProto_Type = 1 - FieldDescriptorProto_TYPE_FLOAT FieldDescriptorProto_Type = 2 - // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT64 if - // negative values are likely. - FieldDescriptorProto_TYPE_INT64 FieldDescriptorProto_Type = 3 - FieldDescriptorProto_TYPE_UINT64 FieldDescriptorProto_Type = 4 - // Not ZigZag encoded. Negative numbers take 10 bytes. Use TYPE_SINT32 if - // negative values are likely. - FieldDescriptorProto_TYPE_INT32 FieldDescriptorProto_Type = 5 - FieldDescriptorProto_TYPE_FIXED64 FieldDescriptorProto_Type = 6 - FieldDescriptorProto_TYPE_FIXED32 FieldDescriptorProto_Type = 7 - FieldDescriptorProto_TYPE_BOOL FieldDescriptorProto_Type = 8 - FieldDescriptorProto_TYPE_STRING FieldDescriptorProto_Type = 9 - // Tag-delimited aggregate. - // Group type is deprecated and not supported in proto3. However, Proto3 - // implementations should still be able to parse the group wire format and - // treat group fields as unknown fields. - FieldDescriptorProto_TYPE_GROUP FieldDescriptorProto_Type = 10 - FieldDescriptorProto_TYPE_MESSAGE FieldDescriptorProto_Type = 11 - // New in version 2. - FieldDescriptorProto_TYPE_BYTES FieldDescriptorProto_Type = 12 - FieldDescriptorProto_TYPE_UINT32 FieldDescriptorProto_Type = 13 - FieldDescriptorProto_TYPE_ENUM FieldDescriptorProto_Type = 14 - FieldDescriptorProto_TYPE_SFIXED32 FieldDescriptorProto_Type = 15 - FieldDescriptorProto_TYPE_SFIXED64 FieldDescriptorProto_Type = 16 - FieldDescriptorProto_TYPE_SINT32 FieldDescriptorProto_Type = 17 - FieldDescriptorProto_TYPE_SINT64 FieldDescriptorProto_Type = 18 -) - -var FieldDescriptorProto_Type_name = map[int32]string{ - 1: "TYPE_DOUBLE", - 2: "TYPE_FLOAT", - 3: "TYPE_INT64", - 4: "TYPE_UINT64", - 5: "TYPE_INT32", - 6: "TYPE_FIXED64", - 7: "TYPE_FIXED32", - 8: "TYPE_BOOL", - 9: "TYPE_STRING", - 10: "TYPE_GROUP", - 11: "TYPE_MESSAGE", - 12: "TYPE_BYTES", - 13: "TYPE_UINT32", - 14: "TYPE_ENUM", - 15: "TYPE_SFIXED32", - 16: "TYPE_SFIXED64", - 17: "TYPE_SINT32", - 18: "TYPE_SINT64", -} -var FieldDescriptorProto_Type_value = map[string]int32{ - "TYPE_DOUBLE": 1, - "TYPE_FLOAT": 2, - "TYPE_INT64": 3, - "TYPE_UINT64": 4, - "TYPE_INT32": 5, - "TYPE_FIXED64": 6, - "TYPE_FIXED32": 7, - "TYPE_BOOL": 8, - "TYPE_STRING": 9, - "TYPE_GROUP": 10, - "TYPE_MESSAGE": 11, - "TYPE_BYTES": 12, - "TYPE_UINT32": 13, - "TYPE_ENUM": 14, - "TYPE_SFIXED32": 15, - "TYPE_SFIXED64": 16, - "TYPE_SINT32": 17, - "TYPE_SINT64": 18, -} - -func (x FieldDescriptorProto_Type) Enum() *FieldDescriptorProto_Type { - p := new(FieldDescriptorProto_Type) - *p = x - return p -} -func (x FieldDescriptorProto_Type) String() string { - return proto.EnumName(FieldDescriptorProto_Type_name, int32(x)) -} -func (x *FieldDescriptorProto_Type) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(FieldDescriptorProto_Type_value, data, "FieldDescriptorProto_Type") - if err != nil { - return err - } - *x = FieldDescriptorProto_Type(value) - return nil -} -func (FieldDescriptorProto_Type) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{4, 0} -} - -type FieldDescriptorProto_Label int32 - -const ( - // 0 is reserved for errors - FieldDescriptorProto_LABEL_OPTIONAL FieldDescriptorProto_Label = 1 - FieldDescriptorProto_LABEL_REQUIRED FieldDescriptorProto_Label = 2 - FieldDescriptorProto_LABEL_REPEATED FieldDescriptorProto_Label = 3 -) - -var FieldDescriptorProto_Label_name = map[int32]string{ - 1: "LABEL_OPTIONAL", - 2: "LABEL_REQUIRED", - 3: "LABEL_REPEATED", -} -var FieldDescriptorProto_Label_value = map[string]int32{ - "LABEL_OPTIONAL": 1, - "LABEL_REQUIRED": 2, - "LABEL_REPEATED": 3, -} - -func (x FieldDescriptorProto_Label) Enum() *FieldDescriptorProto_Label { - p := new(FieldDescriptorProto_Label) - *p = x - return p -} -func (x FieldDescriptorProto_Label) String() string { - return proto.EnumName(FieldDescriptorProto_Label_name, int32(x)) -} -func (x *FieldDescriptorProto_Label) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(FieldDescriptorProto_Label_value, data, "FieldDescriptorProto_Label") - if err != nil { - return err - } - *x = FieldDescriptorProto_Label(value) - return nil -} -func (FieldDescriptorProto_Label) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{4, 1} -} - -// Generated classes can be optimized for speed or code size. -type FileOptions_OptimizeMode int32 - -const ( - FileOptions_SPEED FileOptions_OptimizeMode = 1 - // etc. - FileOptions_CODE_SIZE FileOptions_OptimizeMode = 2 - FileOptions_LITE_RUNTIME FileOptions_OptimizeMode = 3 -) - -var FileOptions_OptimizeMode_name = map[int32]string{ - 1: "SPEED", - 2: "CODE_SIZE", - 3: "LITE_RUNTIME", -} -var FileOptions_OptimizeMode_value = map[string]int32{ - "SPEED": 1, - "CODE_SIZE": 2, - "LITE_RUNTIME": 3, -} - -func (x FileOptions_OptimizeMode) Enum() *FileOptions_OptimizeMode { - p := new(FileOptions_OptimizeMode) - *p = x - return p -} -func (x FileOptions_OptimizeMode) String() string { - return proto.EnumName(FileOptions_OptimizeMode_name, int32(x)) -} -func (x *FileOptions_OptimizeMode) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(FileOptions_OptimizeMode_value, data, "FileOptions_OptimizeMode") - if err != nil { - return err - } - *x = FileOptions_OptimizeMode(value) - return nil -} -func (FileOptions_OptimizeMode) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{10, 0} -} - -type FieldOptions_CType int32 - -const ( - // Default mode. - FieldOptions_STRING FieldOptions_CType = 0 - FieldOptions_CORD FieldOptions_CType = 1 - FieldOptions_STRING_PIECE FieldOptions_CType = 2 -) - -var FieldOptions_CType_name = map[int32]string{ - 0: "STRING", - 1: "CORD", - 2: "STRING_PIECE", -} -var FieldOptions_CType_value = map[string]int32{ - "STRING": 0, - "CORD": 1, - "STRING_PIECE": 2, -} - -func (x FieldOptions_CType) Enum() *FieldOptions_CType { - p := new(FieldOptions_CType) - *p = x - return p -} -func (x FieldOptions_CType) String() string { - return proto.EnumName(FieldOptions_CType_name, int32(x)) -} -func (x *FieldOptions_CType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(FieldOptions_CType_value, data, "FieldOptions_CType") - if err != nil { - return err - } - *x = FieldOptions_CType(value) - return nil -} -func (FieldOptions_CType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{12, 0} -} - -type FieldOptions_JSType int32 - -const ( - // Use the default type. - FieldOptions_JS_NORMAL FieldOptions_JSType = 0 - // Use JavaScript strings. - FieldOptions_JS_STRING FieldOptions_JSType = 1 - // Use JavaScript numbers. - FieldOptions_JS_NUMBER FieldOptions_JSType = 2 -) - -var FieldOptions_JSType_name = map[int32]string{ - 0: "JS_NORMAL", - 1: "JS_STRING", - 2: "JS_NUMBER", -} -var FieldOptions_JSType_value = map[string]int32{ - "JS_NORMAL": 0, - "JS_STRING": 1, - "JS_NUMBER": 2, -} - -func (x FieldOptions_JSType) Enum() *FieldOptions_JSType { - p := new(FieldOptions_JSType) - *p = x - return p -} -func (x FieldOptions_JSType) String() string { - return proto.EnumName(FieldOptions_JSType_name, int32(x)) -} -func (x *FieldOptions_JSType) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(FieldOptions_JSType_value, data, "FieldOptions_JSType") - if err != nil { - return err - } - *x = FieldOptions_JSType(value) - return nil -} -func (FieldOptions_JSType) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{12, 1} -} - -// Is this method side-effect-free (or safe in HTTP parlance), or idempotent, -// or neither? HTTP based RPC implementation may choose GET verb for safe -// methods, and PUT verb for idempotent methods instead of the default POST. -type MethodOptions_IdempotencyLevel int32 - -const ( - MethodOptions_IDEMPOTENCY_UNKNOWN MethodOptions_IdempotencyLevel = 0 - MethodOptions_NO_SIDE_EFFECTS MethodOptions_IdempotencyLevel = 1 - MethodOptions_IDEMPOTENT MethodOptions_IdempotencyLevel = 2 -) - -var MethodOptions_IdempotencyLevel_name = map[int32]string{ - 0: "IDEMPOTENCY_UNKNOWN", - 1: "NO_SIDE_EFFECTS", - 2: "IDEMPOTENT", -} -var MethodOptions_IdempotencyLevel_value = map[string]int32{ - "IDEMPOTENCY_UNKNOWN": 0, - "NO_SIDE_EFFECTS": 1, - "IDEMPOTENT": 2, -} - -func (x MethodOptions_IdempotencyLevel) Enum() *MethodOptions_IdempotencyLevel { - p := new(MethodOptions_IdempotencyLevel) - *p = x - return p -} -func (x MethodOptions_IdempotencyLevel) String() string { - return proto.EnumName(MethodOptions_IdempotencyLevel_name, int32(x)) -} -func (x *MethodOptions_IdempotencyLevel) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(MethodOptions_IdempotencyLevel_value, data, "MethodOptions_IdempotencyLevel") - if err != nil { - return err - } - *x = MethodOptions_IdempotencyLevel(value) - return nil -} -func (MethodOptions_IdempotencyLevel) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{17, 0} -} - -// The protocol compiler can output a FileDescriptorSet containing the .proto -// files it parses. -type FileDescriptorSet struct { - File []*FileDescriptorProto `protobuf:"bytes,1,rep,name=file" json:"file,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FileDescriptorSet) Reset() { *m = FileDescriptorSet{} } -func (m *FileDescriptorSet) String() string { return proto.CompactTextString(m) } -func (*FileDescriptorSet) ProtoMessage() {} -func (*FileDescriptorSet) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{0} -} -func (m *FileDescriptorSet) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FileDescriptorSet.Unmarshal(m, b) -} -func (m *FileDescriptorSet) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FileDescriptorSet.Marshal(b, m, deterministic) -} -func (dst *FileDescriptorSet) XXX_Merge(src proto.Message) { - xxx_messageInfo_FileDescriptorSet.Merge(dst, src) -} -func (m *FileDescriptorSet) XXX_Size() int { - return xxx_messageInfo_FileDescriptorSet.Size(m) -} -func (m *FileDescriptorSet) XXX_DiscardUnknown() { - xxx_messageInfo_FileDescriptorSet.DiscardUnknown(m) -} - -var xxx_messageInfo_FileDescriptorSet proto.InternalMessageInfo - -func (m *FileDescriptorSet) GetFile() []*FileDescriptorProto { - if m != nil { - return m.File - } - return nil -} - -// Describes a complete .proto file. -type FileDescriptorProto struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Package *string `protobuf:"bytes,2,opt,name=package" json:"package,omitempty"` - // Names of files imported by this file. - Dependency []string `protobuf:"bytes,3,rep,name=dependency" json:"dependency,omitempty"` - // Indexes of the public imported files in the dependency list above. - PublicDependency []int32 `protobuf:"varint,10,rep,name=public_dependency,json=publicDependency" json:"public_dependency,omitempty"` - // Indexes of the weak imported files in the dependency list. - // For Google-internal migration only. Do not use. - WeakDependency []int32 `protobuf:"varint,11,rep,name=weak_dependency,json=weakDependency" json:"weak_dependency,omitempty"` - // All top-level definitions in this file. - MessageType []*DescriptorProto `protobuf:"bytes,4,rep,name=message_type,json=messageType" json:"message_type,omitempty"` - EnumType []*EnumDescriptorProto `protobuf:"bytes,5,rep,name=enum_type,json=enumType" json:"enum_type,omitempty"` - Service []*ServiceDescriptorProto `protobuf:"bytes,6,rep,name=service" json:"service,omitempty"` - Extension []*FieldDescriptorProto `protobuf:"bytes,7,rep,name=extension" json:"extension,omitempty"` - Options *FileOptions `protobuf:"bytes,8,opt,name=options" json:"options,omitempty"` - // This field contains optional information about the original source code. - // You may safely remove this entire field without harming runtime - // functionality of the descriptors -- the information is needed only by - // development tools. - SourceCodeInfo *SourceCodeInfo `protobuf:"bytes,9,opt,name=source_code_info,json=sourceCodeInfo" json:"source_code_info,omitempty"` - // The syntax of the proto file. - // The supported values are "proto2" and "proto3". - Syntax *string `protobuf:"bytes,12,opt,name=syntax" json:"syntax,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FileDescriptorProto) Reset() { *m = FileDescriptorProto{} } -func (m *FileDescriptorProto) String() string { return proto.CompactTextString(m) } -func (*FileDescriptorProto) ProtoMessage() {} -func (*FileDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{1} -} -func (m *FileDescriptorProto) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FileDescriptorProto.Unmarshal(m, b) -} -func (m *FileDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FileDescriptorProto.Marshal(b, m, deterministic) -} -func (dst *FileDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_FileDescriptorProto.Merge(dst, src) -} -func (m *FileDescriptorProto) XXX_Size() int { - return xxx_messageInfo_FileDescriptorProto.Size(m) -} -func (m *FileDescriptorProto) XXX_DiscardUnknown() { - xxx_messageInfo_FileDescriptorProto.DiscardUnknown(m) -} - -var xxx_messageInfo_FileDescriptorProto proto.InternalMessageInfo - -func (m *FileDescriptorProto) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *FileDescriptorProto) GetPackage() string { - if m != nil && m.Package != nil { - return *m.Package - } - return "" -} - -func (m *FileDescriptorProto) GetDependency() []string { - if m != nil { - return m.Dependency - } - return nil -} - -func (m *FileDescriptorProto) GetPublicDependency() []int32 { - if m != nil { - return m.PublicDependency - } - return nil -} - -func (m *FileDescriptorProto) GetWeakDependency() []int32 { - if m != nil { - return m.WeakDependency - } - return nil -} - -func (m *FileDescriptorProto) GetMessageType() []*DescriptorProto { - if m != nil { - return m.MessageType - } - return nil -} - -func (m *FileDescriptorProto) GetEnumType() []*EnumDescriptorProto { - if m != nil { - return m.EnumType - } - return nil -} - -func (m *FileDescriptorProto) GetService() []*ServiceDescriptorProto { - if m != nil { - return m.Service - } - return nil -} - -func (m *FileDescriptorProto) GetExtension() []*FieldDescriptorProto { - if m != nil { - return m.Extension - } - return nil -} - -func (m *FileDescriptorProto) GetOptions() *FileOptions { - if m != nil { - return m.Options - } - return nil -} - -func (m *FileDescriptorProto) GetSourceCodeInfo() *SourceCodeInfo { - if m != nil { - return m.SourceCodeInfo - } - return nil -} - -func (m *FileDescriptorProto) GetSyntax() string { - if m != nil && m.Syntax != nil { - return *m.Syntax - } - return "" -} - -// Describes a message type. -type DescriptorProto struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Field []*FieldDescriptorProto `protobuf:"bytes,2,rep,name=field" json:"field,omitempty"` - Extension []*FieldDescriptorProto `protobuf:"bytes,6,rep,name=extension" json:"extension,omitempty"` - NestedType []*DescriptorProto `protobuf:"bytes,3,rep,name=nested_type,json=nestedType" json:"nested_type,omitempty"` - EnumType []*EnumDescriptorProto `protobuf:"bytes,4,rep,name=enum_type,json=enumType" json:"enum_type,omitempty"` - ExtensionRange []*DescriptorProto_ExtensionRange `protobuf:"bytes,5,rep,name=extension_range,json=extensionRange" json:"extension_range,omitempty"` - OneofDecl []*OneofDescriptorProto `protobuf:"bytes,8,rep,name=oneof_decl,json=oneofDecl" json:"oneof_decl,omitempty"` - Options *MessageOptions `protobuf:"bytes,7,opt,name=options" json:"options,omitempty"` - ReservedRange []*DescriptorProto_ReservedRange `protobuf:"bytes,9,rep,name=reserved_range,json=reservedRange" json:"reserved_range,omitempty"` - // Reserved field names, which may not be used by fields in the same message. - // A given name may only be reserved once. - ReservedName []string `protobuf:"bytes,10,rep,name=reserved_name,json=reservedName" json:"reserved_name,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DescriptorProto) Reset() { *m = DescriptorProto{} } -func (m *DescriptorProto) String() string { return proto.CompactTextString(m) } -func (*DescriptorProto) ProtoMessage() {} -func (*DescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{2} -} -func (m *DescriptorProto) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DescriptorProto.Unmarshal(m, b) -} -func (m *DescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DescriptorProto.Marshal(b, m, deterministic) -} -func (dst *DescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_DescriptorProto.Merge(dst, src) -} -func (m *DescriptorProto) XXX_Size() int { - return xxx_messageInfo_DescriptorProto.Size(m) -} -func (m *DescriptorProto) XXX_DiscardUnknown() { - xxx_messageInfo_DescriptorProto.DiscardUnknown(m) -} - -var xxx_messageInfo_DescriptorProto proto.InternalMessageInfo - -func (m *DescriptorProto) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *DescriptorProto) GetField() []*FieldDescriptorProto { - if m != nil { - return m.Field - } - return nil -} - -func (m *DescriptorProto) GetExtension() []*FieldDescriptorProto { - if m != nil { - return m.Extension - } - return nil -} - -func (m *DescriptorProto) GetNestedType() []*DescriptorProto { - if m != nil { - return m.NestedType - } - return nil -} - -func (m *DescriptorProto) GetEnumType() []*EnumDescriptorProto { - if m != nil { - return m.EnumType - } - return nil -} - -func (m *DescriptorProto) GetExtensionRange() []*DescriptorProto_ExtensionRange { - if m != nil { - return m.ExtensionRange - } - return nil -} - -func (m *DescriptorProto) GetOneofDecl() []*OneofDescriptorProto { - if m != nil { - return m.OneofDecl - } - return nil -} - -func (m *DescriptorProto) GetOptions() *MessageOptions { - if m != nil { - return m.Options - } - return nil -} - -func (m *DescriptorProto) GetReservedRange() []*DescriptorProto_ReservedRange { - if m != nil { - return m.ReservedRange - } - return nil -} - -func (m *DescriptorProto) GetReservedName() []string { - if m != nil { - return m.ReservedName - } - return nil -} - -type DescriptorProto_ExtensionRange struct { - Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` - End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` - Options *ExtensionRangeOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DescriptorProto_ExtensionRange) Reset() { *m = DescriptorProto_ExtensionRange{} } -func (m *DescriptorProto_ExtensionRange) String() string { return proto.CompactTextString(m) } -func (*DescriptorProto_ExtensionRange) ProtoMessage() {} -func (*DescriptorProto_ExtensionRange) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{2, 0} -} -func (m *DescriptorProto_ExtensionRange) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DescriptorProto_ExtensionRange.Unmarshal(m, b) -} -func (m *DescriptorProto_ExtensionRange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DescriptorProto_ExtensionRange.Marshal(b, m, deterministic) -} -func (dst *DescriptorProto_ExtensionRange) XXX_Merge(src proto.Message) { - xxx_messageInfo_DescriptorProto_ExtensionRange.Merge(dst, src) -} -func (m *DescriptorProto_ExtensionRange) XXX_Size() int { - return xxx_messageInfo_DescriptorProto_ExtensionRange.Size(m) -} -func (m *DescriptorProto_ExtensionRange) XXX_DiscardUnknown() { - xxx_messageInfo_DescriptorProto_ExtensionRange.DiscardUnknown(m) -} - -var xxx_messageInfo_DescriptorProto_ExtensionRange proto.InternalMessageInfo - -func (m *DescriptorProto_ExtensionRange) GetStart() int32 { - if m != nil && m.Start != nil { - return *m.Start - } - return 0 -} - -func (m *DescriptorProto_ExtensionRange) GetEnd() int32 { - if m != nil && m.End != nil { - return *m.End - } - return 0 -} - -func (m *DescriptorProto_ExtensionRange) GetOptions() *ExtensionRangeOptions { - if m != nil { - return m.Options - } - return nil -} - -// Range of reserved tag numbers. Reserved tag numbers may not be used by -// fields or extension ranges in the same message. Reserved ranges may -// not overlap. -type DescriptorProto_ReservedRange struct { - Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` - End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DescriptorProto_ReservedRange) Reset() { *m = DescriptorProto_ReservedRange{} } -func (m *DescriptorProto_ReservedRange) String() string { return proto.CompactTextString(m) } -func (*DescriptorProto_ReservedRange) ProtoMessage() {} -func (*DescriptorProto_ReservedRange) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{2, 1} -} -func (m *DescriptorProto_ReservedRange) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DescriptorProto_ReservedRange.Unmarshal(m, b) -} -func (m *DescriptorProto_ReservedRange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DescriptorProto_ReservedRange.Marshal(b, m, deterministic) -} -func (dst *DescriptorProto_ReservedRange) XXX_Merge(src proto.Message) { - xxx_messageInfo_DescriptorProto_ReservedRange.Merge(dst, src) -} -func (m *DescriptorProto_ReservedRange) XXX_Size() int { - return xxx_messageInfo_DescriptorProto_ReservedRange.Size(m) -} -func (m *DescriptorProto_ReservedRange) XXX_DiscardUnknown() { - xxx_messageInfo_DescriptorProto_ReservedRange.DiscardUnknown(m) -} - -var xxx_messageInfo_DescriptorProto_ReservedRange proto.InternalMessageInfo - -func (m *DescriptorProto_ReservedRange) GetStart() int32 { - if m != nil && m.Start != nil { - return *m.Start - } - return 0 -} - -func (m *DescriptorProto_ReservedRange) GetEnd() int32 { - if m != nil && m.End != nil { - return *m.End - } - return 0 -} - -type ExtensionRangeOptions struct { - // The parser stores options it doesn't recognize here. See above. - UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - proto.XXX_InternalExtensions `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ExtensionRangeOptions) Reset() { *m = ExtensionRangeOptions{} } -func (m *ExtensionRangeOptions) String() string { return proto.CompactTextString(m) } -func (*ExtensionRangeOptions) ProtoMessage() {} -func (*ExtensionRangeOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{3} -} - -var extRange_ExtensionRangeOptions = []proto.ExtensionRange{ - {Start: 1000, End: 536870911}, -} - -func (*ExtensionRangeOptions) ExtensionRangeArray() []proto.ExtensionRange { - return extRange_ExtensionRangeOptions -} -func (m *ExtensionRangeOptions) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ExtensionRangeOptions.Unmarshal(m, b) -} -func (m *ExtensionRangeOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ExtensionRangeOptions.Marshal(b, m, deterministic) -} -func (dst *ExtensionRangeOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_ExtensionRangeOptions.Merge(dst, src) -} -func (m *ExtensionRangeOptions) XXX_Size() int { - return xxx_messageInfo_ExtensionRangeOptions.Size(m) -} -func (m *ExtensionRangeOptions) XXX_DiscardUnknown() { - xxx_messageInfo_ExtensionRangeOptions.DiscardUnknown(m) -} - -var xxx_messageInfo_ExtensionRangeOptions proto.InternalMessageInfo - -func (m *ExtensionRangeOptions) GetUninterpretedOption() []*UninterpretedOption { - if m != nil { - return m.UninterpretedOption - } - return nil -} - -// Describes a field within a message. -type FieldDescriptorProto struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Number *int32 `protobuf:"varint,3,opt,name=number" json:"number,omitempty"` - Label *FieldDescriptorProto_Label `protobuf:"varint,4,opt,name=label,enum=google.protobuf.FieldDescriptorProto_Label" json:"label,omitempty"` - // If type_name is set, this need not be set. If both this and type_name - // are set, this must be one of TYPE_ENUM, TYPE_MESSAGE or TYPE_GROUP. - Type *FieldDescriptorProto_Type `protobuf:"varint,5,opt,name=type,enum=google.protobuf.FieldDescriptorProto_Type" json:"type,omitempty"` - // For message and enum types, this is the name of the type. If the name - // starts with a '.', it is fully-qualified. Otherwise, C++-like scoping - // rules are used to find the type (i.e. first the nested types within this - // message are searched, then within the parent, on up to the root - // namespace). - TypeName *string `protobuf:"bytes,6,opt,name=type_name,json=typeName" json:"type_name,omitempty"` - // For extensions, this is the name of the type being extended. It is - // resolved in the same manner as type_name. - Extendee *string `protobuf:"bytes,2,opt,name=extendee" json:"extendee,omitempty"` - // For numeric types, contains the original text representation of the value. - // For booleans, "true" or "false". - // For strings, contains the default text contents (not escaped in any way). - // For bytes, contains the C escaped value. All bytes >= 128 are escaped. - // TODO(kenton): Base-64 encode? - DefaultValue *string `protobuf:"bytes,7,opt,name=default_value,json=defaultValue" json:"default_value,omitempty"` - // If set, gives the index of a oneof in the containing type's oneof_decl - // list. This field is a member of that oneof. - OneofIndex *int32 `protobuf:"varint,9,opt,name=oneof_index,json=oneofIndex" json:"oneof_index,omitempty"` - // JSON name of this field. The value is set by protocol compiler. If the - // user has set a "json_name" option on this field, that option's value - // will be used. Otherwise, it's deduced from the field's name by converting - // it to camelCase. - JsonName *string `protobuf:"bytes,10,opt,name=json_name,json=jsonName" json:"json_name,omitempty"` - Options *FieldOptions `protobuf:"bytes,8,opt,name=options" json:"options,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FieldDescriptorProto) Reset() { *m = FieldDescriptorProto{} } -func (m *FieldDescriptorProto) String() string { return proto.CompactTextString(m) } -func (*FieldDescriptorProto) ProtoMessage() {} -func (*FieldDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{4} -} -func (m *FieldDescriptorProto) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FieldDescriptorProto.Unmarshal(m, b) -} -func (m *FieldDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FieldDescriptorProto.Marshal(b, m, deterministic) -} -func (dst *FieldDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_FieldDescriptorProto.Merge(dst, src) -} -func (m *FieldDescriptorProto) XXX_Size() int { - return xxx_messageInfo_FieldDescriptorProto.Size(m) -} -func (m *FieldDescriptorProto) XXX_DiscardUnknown() { - xxx_messageInfo_FieldDescriptorProto.DiscardUnknown(m) -} - -var xxx_messageInfo_FieldDescriptorProto proto.InternalMessageInfo - -func (m *FieldDescriptorProto) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *FieldDescriptorProto) GetNumber() int32 { - if m != nil && m.Number != nil { - return *m.Number - } - return 0 -} - -func (m *FieldDescriptorProto) GetLabel() FieldDescriptorProto_Label { - if m != nil && m.Label != nil { - return *m.Label - } - return FieldDescriptorProto_LABEL_OPTIONAL -} - -func (m *FieldDescriptorProto) GetType() FieldDescriptorProto_Type { - if m != nil && m.Type != nil { - return *m.Type - } - return FieldDescriptorProto_TYPE_DOUBLE -} - -func (m *FieldDescriptorProto) GetTypeName() string { - if m != nil && m.TypeName != nil { - return *m.TypeName - } - return "" -} - -func (m *FieldDescriptorProto) GetExtendee() string { - if m != nil && m.Extendee != nil { - return *m.Extendee - } - return "" -} - -func (m *FieldDescriptorProto) GetDefaultValue() string { - if m != nil && m.DefaultValue != nil { - return *m.DefaultValue - } - return "" -} - -func (m *FieldDescriptorProto) GetOneofIndex() int32 { - if m != nil && m.OneofIndex != nil { - return *m.OneofIndex - } - return 0 -} - -func (m *FieldDescriptorProto) GetJsonName() string { - if m != nil && m.JsonName != nil { - return *m.JsonName - } - return "" -} - -func (m *FieldDescriptorProto) GetOptions() *FieldOptions { - if m != nil { - return m.Options - } - return nil -} - -// Describes a oneof. -type OneofDescriptorProto struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Options *OneofOptions `protobuf:"bytes,2,opt,name=options" json:"options,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *OneofDescriptorProto) Reset() { *m = OneofDescriptorProto{} } -func (m *OneofDescriptorProto) String() string { return proto.CompactTextString(m) } -func (*OneofDescriptorProto) ProtoMessage() {} -func (*OneofDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{5} -} -func (m *OneofDescriptorProto) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_OneofDescriptorProto.Unmarshal(m, b) -} -func (m *OneofDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_OneofDescriptorProto.Marshal(b, m, deterministic) -} -func (dst *OneofDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_OneofDescriptorProto.Merge(dst, src) -} -func (m *OneofDescriptorProto) XXX_Size() int { - return xxx_messageInfo_OneofDescriptorProto.Size(m) -} -func (m *OneofDescriptorProto) XXX_DiscardUnknown() { - xxx_messageInfo_OneofDescriptorProto.DiscardUnknown(m) -} - -var xxx_messageInfo_OneofDescriptorProto proto.InternalMessageInfo - -func (m *OneofDescriptorProto) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *OneofDescriptorProto) GetOptions() *OneofOptions { - if m != nil { - return m.Options - } - return nil -} - -// Describes an enum type. -type EnumDescriptorProto struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Value []*EnumValueDescriptorProto `protobuf:"bytes,2,rep,name=value" json:"value,omitempty"` - Options *EnumOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` - // Range of reserved numeric values. Reserved numeric values may not be used - // by enum values in the same enum declaration. Reserved ranges may not - // overlap. - ReservedRange []*EnumDescriptorProto_EnumReservedRange `protobuf:"bytes,4,rep,name=reserved_range,json=reservedRange" json:"reserved_range,omitempty"` - // Reserved enum value names, which may not be reused. A given name may only - // be reserved once. - ReservedName []string `protobuf:"bytes,5,rep,name=reserved_name,json=reservedName" json:"reserved_name,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EnumDescriptorProto) Reset() { *m = EnumDescriptorProto{} } -func (m *EnumDescriptorProto) String() string { return proto.CompactTextString(m) } -func (*EnumDescriptorProto) ProtoMessage() {} -func (*EnumDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{6} -} -func (m *EnumDescriptorProto) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EnumDescriptorProto.Unmarshal(m, b) -} -func (m *EnumDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EnumDescriptorProto.Marshal(b, m, deterministic) -} -func (dst *EnumDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnumDescriptorProto.Merge(dst, src) -} -func (m *EnumDescriptorProto) XXX_Size() int { - return xxx_messageInfo_EnumDescriptorProto.Size(m) -} -func (m *EnumDescriptorProto) XXX_DiscardUnknown() { - xxx_messageInfo_EnumDescriptorProto.DiscardUnknown(m) -} - -var xxx_messageInfo_EnumDescriptorProto proto.InternalMessageInfo - -func (m *EnumDescriptorProto) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *EnumDescriptorProto) GetValue() []*EnumValueDescriptorProto { - if m != nil { - return m.Value - } - return nil -} - -func (m *EnumDescriptorProto) GetOptions() *EnumOptions { - if m != nil { - return m.Options - } - return nil -} - -func (m *EnumDescriptorProto) GetReservedRange() []*EnumDescriptorProto_EnumReservedRange { - if m != nil { - return m.ReservedRange - } - return nil -} - -func (m *EnumDescriptorProto) GetReservedName() []string { - if m != nil { - return m.ReservedName - } - return nil -} - -// Range of reserved numeric values. Reserved values may not be used by -// entries in the same enum. Reserved ranges may not overlap. -// -// Note that this is distinct from DescriptorProto.ReservedRange in that it -// is inclusive such that it can appropriately represent the entire int32 -// domain. -type EnumDescriptorProto_EnumReservedRange struct { - Start *int32 `protobuf:"varint,1,opt,name=start" json:"start,omitempty"` - End *int32 `protobuf:"varint,2,opt,name=end" json:"end,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EnumDescriptorProto_EnumReservedRange) Reset() { *m = EnumDescriptorProto_EnumReservedRange{} } -func (m *EnumDescriptorProto_EnumReservedRange) String() string { return proto.CompactTextString(m) } -func (*EnumDescriptorProto_EnumReservedRange) ProtoMessage() {} -func (*EnumDescriptorProto_EnumReservedRange) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{6, 0} -} -func (m *EnumDescriptorProto_EnumReservedRange) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Unmarshal(m, b) -} -func (m *EnumDescriptorProto_EnumReservedRange) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Marshal(b, m, deterministic) -} -func (dst *EnumDescriptorProto_EnumReservedRange) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Merge(dst, src) -} -func (m *EnumDescriptorProto_EnumReservedRange) XXX_Size() int { - return xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.Size(m) -} -func (m *EnumDescriptorProto_EnumReservedRange) XXX_DiscardUnknown() { - xxx_messageInfo_EnumDescriptorProto_EnumReservedRange.DiscardUnknown(m) -} - -var xxx_messageInfo_EnumDescriptorProto_EnumReservedRange proto.InternalMessageInfo - -func (m *EnumDescriptorProto_EnumReservedRange) GetStart() int32 { - if m != nil && m.Start != nil { - return *m.Start - } - return 0 -} - -func (m *EnumDescriptorProto_EnumReservedRange) GetEnd() int32 { - if m != nil && m.End != nil { - return *m.End - } - return 0 -} - -// Describes a value within an enum. -type EnumValueDescriptorProto struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Number *int32 `protobuf:"varint,2,opt,name=number" json:"number,omitempty"` - Options *EnumValueOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EnumValueDescriptorProto) Reset() { *m = EnumValueDescriptorProto{} } -func (m *EnumValueDescriptorProto) String() string { return proto.CompactTextString(m) } -func (*EnumValueDescriptorProto) ProtoMessage() {} -func (*EnumValueDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{7} -} -func (m *EnumValueDescriptorProto) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EnumValueDescriptorProto.Unmarshal(m, b) -} -func (m *EnumValueDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EnumValueDescriptorProto.Marshal(b, m, deterministic) -} -func (dst *EnumValueDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnumValueDescriptorProto.Merge(dst, src) -} -func (m *EnumValueDescriptorProto) XXX_Size() int { - return xxx_messageInfo_EnumValueDescriptorProto.Size(m) -} -func (m *EnumValueDescriptorProto) XXX_DiscardUnknown() { - xxx_messageInfo_EnumValueDescriptorProto.DiscardUnknown(m) -} - -var xxx_messageInfo_EnumValueDescriptorProto proto.InternalMessageInfo - -func (m *EnumValueDescriptorProto) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *EnumValueDescriptorProto) GetNumber() int32 { - if m != nil && m.Number != nil { - return *m.Number - } - return 0 -} - -func (m *EnumValueDescriptorProto) GetOptions() *EnumValueOptions { - if m != nil { - return m.Options - } - return nil -} - -// Describes a service. -type ServiceDescriptorProto struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - Method []*MethodDescriptorProto `protobuf:"bytes,2,rep,name=method" json:"method,omitempty"` - Options *ServiceOptions `protobuf:"bytes,3,opt,name=options" json:"options,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ServiceDescriptorProto) Reset() { *m = ServiceDescriptorProto{} } -func (m *ServiceDescriptorProto) String() string { return proto.CompactTextString(m) } -func (*ServiceDescriptorProto) ProtoMessage() {} -func (*ServiceDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{8} -} -func (m *ServiceDescriptorProto) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ServiceDescriptorProto.Unmarshal(m, b) -} -func (m *ServiceDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ServiceDescriptorProto.Marshal(b, m, deterministic) -} -func (dst *ServiceDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_ServiceDescriptorProto.Merge(dst, src) -} -func (m *ServiceDescriptorProto) XXX_Size() int { - return xxx_messageInfo_ServiceDescriptorProto.Size(m) -} -func (m *ServiceDescriptorProto) XXX_DiscardUnknown() { - xxx_messageInfo_ServiceDescriptorProto.DiscardUnknown(m) -} - -var xxx_messageInfo_ServiceDescriptorProto proto.InternalMessageInfo - -func (m *ServiceDescriptorProto) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *ServiceDescriptorProto) GetMethod() []*MethodDescriptorProto { - if m != nil { - return m.Method - } - return nil -} - -func (m *ServiceDescriptorProto) GetOptions() *ServiceOptions { - if m != nil { - return m.Options - } - return nil -} - -// Describes a method of a service. -type MethodDescriptorProto struct { - Name *string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - // Input and output type names. These are resolved in the same way as - // FieldDescriptorProto.type_name, but must refer to a message type. - InputType *string `protobuf:"bytes,2,opt,name=input_type,json=inputType" json:"input_type,omitempty"` - OutputType *string `protobuf:"bytes,3,opt,name=output_type,json=outputType" json:"output_type,omitempty"` - Options *MethodOptions `protobuf:"bytes,4,opt,name=options" json:"options,omitempty"` - // Identifies if client streams multiple client messages - ClientStreaming *bool `protobuf:"varint,5,opt,name=client_streaming,json=clientStreaming,def=0" json:"client_streaming,omitempty"` - // Identifies if server streams multiple server messages - ServerStreaming *bool `protobuf:"varint,6,opt,name=server_streaming,json=serverStreaming,def=0" json:"server_streaming,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *MethodDescriptorProto) Reset() { *m = MethodDescriptorProto{} } -func (m *MethodDescriptorProto) String() string { return proto.CompactTextString(m) } -func (*MethodDescriptorProto) ProtoMessage() {} -func (*MethodDescriptorProto) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{9} -} -func (m *MethodDescriptorProto) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_MethodDescriptorProto.Unmarshal(m, b) -} -func (m *MethodDescriptorProto) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_MethodDescriptorProto.Marshal(b, m, deterministic) -} -func (dst *MethodDescriptorProto) XXX_Merge(src proto.Message) { - xxx_messageInfo_MethodDescriptorProto.Merge(dst, src) -} -func (m *MethodDescriptorProto) XXX_Size() int { - return xxx_messageInfo_MethodDescriptorProto.Size(m) -} -func (m *MethodDescriptorProto) XXX_DiscardUnknown() { - xxx_messageInfo_MethodDescriptorProto.DiscardUnknown(m) -} - -var xxx_messageInfo_MethodDescriptorProto proto.InternalMessageInfo - -const Default_MethodDescriptorProto_ClientStreaming bool = false -const Default_MethodDescriptorProto_ServerStreaming bool = false - -func (m *MethodDescriptorProto) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *MethodDescriptorProto) GetInputType() string { - if m != nil && m.InputType != nil { - return *m.InputType - } - return "" -} - -func (m *MethodDescriptorProto) GetOutputType() string { - if m != nil && m.OutputType != nil { - return *m.OutputType - } - return "" -} - -func (m *MethodDescriptorProto) GetOptions() *MethodOptions { - if m != nil { - return m.Options - } - return nil -} - -func (m *MethodDescriptorProto) GetClientStreaming() bool { - if m != nil && m.ClientStreaming != nil { - return *m.ClientStreaming - } - return Default_MethodDescriptorProto_ClientStreaming -} - -func (m *MethodDescriptorProto) GetServerStreaming() bool { - if m != nil && m.ServerStreaming != nil { - return *m.ServerStreaming - } - return Default_MethodDescriptorProto_ServerStreaming -} - -type FileOptions struct { - // Sets the Java package where classes generated from this .proto will be - // placed. By default, the proto package is used, but this is often - // inappropriate because proto packages do not normally start with backwards - // domain names. - JavaPackage *string `protobuf:"bytes,1,opt,name=java_package,json=javaPackage" json:"java_package,omitempty"` - // If set, all the classes from the .proto file are wrapped in a single - // outer class with the given name. This applies to both Proto1 - // (equivalent to the old "--one_java_file" option) and Proto2 (where - // a .proto always translates to a single class, but you may want to - // explicitly choose the class name). - JavaOuterClassname *string `protobuf:"bytes,8,opt,name=java_outer_classname,json=javaOuterClassname" json:"java_outer_classname,omitempty"` - // If set true, then the Java code generator will generate a separate .java - // file for each top-level message, enum, and service defined in the .proto - // file. Thus, these types will *not* be nested inside the outer class - // named by java_outer_classname. However, the outer class will still be - // generated to contain the file's getDescriptor() method as well as any - // top-level extensions defined in the file. - JavaMultipleFiles *bool `protobuf:"varint,10,opt,name=java_multiple_files,json=javaMultipleFiles,def=0" json:"java_multiple_files,omitempty"` - // This option does nothing. - JavaGenerateEqualsAndHash *bool `protobuf:"varint,20,opt,name=java_generate_equals_and_hash,json=javaGenerateEqualsAndHash" json:"java_generate_equals_and_hash,omitempty"` // Deprecated: Do not use. - // If set true, then the Java2 code generator will generate code that - // throws an exception whenever an attempt is made to assign a non-UTF-8 - // byte sequence to a string field. - // Message reflection will do the same. - // However, an extension field still accepts non-UTF-8 byte sequences. - // This option has no effect on when used with the lite runtime. - JavaStringCheckUtf8 *bool `protobuf:"varint,27,opt,name=java_string_check_utf8,json=javaStringCheckUtf8,def=0" json:"java_string_check_utf8,omitempty"` - OptimizeFor *FileOptions_OptimizeMode `protobuf:"varint,9,opt,name=optimize_for,json=optimizeFor,enum=google.protobuf.FileOptions_OptimizeMode,def=1" json:"optimize_for,omitempty"` - // Sets the Go package where structs generated from this .proto will be - // placed. If omitted, the Go package will be derived from the following: - // - The basename of the package import path, if provided. - // - Otherwise, the package statement in the .proto file, if present. - // - Otherwise, the basename of the .proto file, without extension. - GoPackage *string `protobuf:"bytes,11,opt,name=go_package,json=goPackage" json:"go_package,omitempty"` - // Should generic services be generated in each language? "Generic" services - // are not specific to any particular RPC system. They are generated by the - // main code generators in each language (without additional plugins). - // Generic services were the only kind of service generation supported by - // early versions of google.protobuf. - // - // Generic services are now considered deprecated in favor of using plugins - // that generate code specific to your particular RPC system. Therefore, - // these default to false. Old code which depends on generic services should - // explicitly set them to true. - CcGenericServices *bool `protobuf:"varint,16,opt,name=cc_generic_services,json=ccGenericServices,def=0" json:"cc_generic_services,omitempty"` - JavaGenericServices *bool `protobuf:"varint,17,opt,name=java_generic_services,json=javaGenericServices,def=0" json:"java_generic_services,omitempty"` - PyGenericServices *bool `protobuf:"varint,18,opt,name=py_generic_services,json=pyGenericServices,def=0" json:"py_generic_services,omitempty"` - PhpGenericServices *bool `protobuf:"varint,42,opt,name=php_generic_services,json=phpGenericServices,def=0" json:"php_generic_services,omitempty"` - // Is this file deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for everything in the file, or it will be completely ignored; in the very - // least, this is a formalization for deprecating files. - Deprecated *bool `protobuf:"varint,23,opt,name=deprecated,def=0" json:"deprecated,omitempty"` - // Enables the use of arenas for the proto messages in this file. This applies - // only to generated classes for C++. - CcEnableArenas *bool `protobuf:"varint,31,opt,name=cc_enable_arenas,json=ccEnableArenas,def=0" json:"cc_enable_arenas,omitempty"` - // Sets the objective c class prefix which is prepended to all objective c - // generated classes from this .proto. There is no default. - ObjcClassPrefix *string `protobuf:"bytes,36,opt,name=objc_class_prefix,json=objcClassPrefix" json:"objc_class_prefix,omitempty"` - // Namespace for generated classes; defaults to the package. - CsharpNamespace *string `protobuf:"bytes,37,opt,name=csharp_namespace,json=csharpNamespace" json:"csharp_namespace,omitempty"` - // By default Swift generators will take the proto package and CamelCase it - // replacing '.' with underscore and use that to prefix the types/symbols - // defined. When this options is provided, they will use this value instead - // to prefix the types/symbols defined. - SwiftPrefix *string `protobuf:"bytes,39,opt,name=swift_prefix,json=swiftPrefix" json:"swift_prefix,omitempty"` - // Sets the php class prefix which is prepended to all php generated classes - // from this .proto. Default is empty. - PhpClassPrefix *string `protobuf:"bytes,40,opt,name=php_class_prefix,json=phpClassPrefix" json:"php_class_prefix,omitempty"` - // Use this option to change the namespace of php generated classes. Default - // is empty. When this option is empty, the package name will be used for - // determining the namespace. - PhpNamespace *string `protobuf:"bytes,41,opt,name=php_namespace,json=phpNamespace" json:"php_namespace,omitempty"` - // The parser stores options it doesn't recognize here. - // See the documentation for the "Options" section above. - UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - proto.XXX_InternalExtensions `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FileOptions) Reset() { *m = FileOptions{} } -func (m *FileOptions) String() string { return proto.CompactTextString(m) } -func (*FileOptions) ProtoMessage() {} -func (*FileOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{10} -} - -var extRange_FileOptions = []proto.ExtensionRange{ - {Start: 1000, End: 536870911}, -} - -func (*FileOptions) ExtensionRangeArray() []proto.ExtensionRange { - return extRange_FileOptions -} -func (m *FileOptions) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FileOptions.Unmarshal(m, b) -} -func (m *FileOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FileOptions.Marshal(b, m, deterministic) -} -func (dst *FileOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_FileOptions.Merge(dst, src) -} -func (m *FileOptions) XXX_Size() int { - return xxx_messageInfo_FileOptions.Size(m) -} -func (m *FileOptions) XXX_DiscardUnknown() { - xxx_messageInfo_FileOptions.DiscardUnknown(m) -} - -var xxx_messageInfo_FileOptions proto.InternalMessageInfo - -const Default_FileOptions_JavaMultipleFiles bool = false -const Default_FileOptions_JavaStringCheckUtf8 bool = false -const Default_FileOptions_OptimizeFor FileOptions_OptimizeMode = FileOptions_SPEED -const Default_FileOptions_CcGenericServices bool = false -const Default_FileOptions_JavaGenericServices bool = false -const Default_FileOptions_PyGenericServices bool = false -const Default_FileOptions_PhpGenericServices bool = false -const Default_FileOptions_Deprecated bool = false -const Default_FileOptions_CcEnableArenas bool = false - -func (m *FileOptions) GetJavaPackage() string { - if m != nil && m.JavaPackage != nil { - return *m.JavaPackage - } - return "" -} - -func (m *FileOptions) GetJavaOuterClassname() string { - if m != nil && m.JavaOuterClassname != nil { - return *m.JavaOuterClassname - } - return "" -} - -func (m *FileOptions) GetJavaMultipleFiles() bool { - if m != nil && m.JavaMultipleFiles != nil { - return *m.JavaMultipleFiles - } - return Default_FileOptions_JavaMultipleFiles -} - -// Deprecated: Do not use. -func (m *FileOptions) GetJavaGenerateEqualsAndHash() bool { - if m != nil && m.JavaGenerateEqualsAndHash != nil { - return *m.JavaGenerateEqualsAndHash - } - return false -} - -func (m *FileOptions) GetJavaStringCheckUtf8() bool { - if m != nil && m.JavaStringCheckUtf8 != nil { - return *m.JavaStringCheckUtf8 - } - return Default_FileOptions_JavaStringCheckUtf8 -} - -func (m *FileOptions) GetOptimizeFor() FileOptions_OptimizeMode { - if m != nil && m.OptimizeFor != nil { - return *m.OptimizeFor - } - return Default_FileOptions_OptimizeFor -} - -func (m *FileOptions) GetGoPackage() string { - if m != nil && m.GoPackage != nil { - return *m.GoPackage - } - return "" -} - -func (m *FileOptions) GetCcGenericServices() bool { - if m != nil && m.CcGenericServices != nil { - return *m.CcGenericServices - } - return Default_FileOptions_CcGenericServices -} - -func (m *FileOptions) GetJavaGenericServices() bool { - if m != nil && m.JavaGenericServices != nil { - return *m.JavaGenericServices - } - return Default_FileOptions_JavaGenericServices -} - -func (m *FileOptions) GetPyGenericServices() bool { - if m != nil && m.PyGenericServices != nil { - return *m.PyGenericServices - } - return Default_FileOptions_PyGenericServices -} - -func (m *FileOptions) GetPhpGenericServices() bool { - if m != nil && m.PhpGenericServices != nil { - return *m.PhpGenericServices - } - return Default_FileOptions_PhpGenericServices -} - -func (m *FileOptions) GetDeprecated() bool { - if m != nil && m.Deprecated != nil { - return *m.Deprecated - } - return Default_FileOptions_Deprecated -} - -func (m *FileOptions) GetCcEnableArenas() bool { - if m != nil && m.CcEnableArenas != nil { - return *m.CcEnableArenas - } - return Default_FileOptions_CcEnableArenas -} - -func (m *FileOptions) GetObjcClassPrefix() string { - if m != nil && m.ObjcClassPrefix != nil { - return *m.ObjcClassPrefix - } - return "" -} - -func (m *FileOptions) GetCsharpNamespace() string { - if m != nil && m.CsharpNamespace != nil { - return *m.CsharpNamespace - } - return "" -} - -func (m *FileOptions) GetSwiftPrefix() string { - if m != nil && m.SwiftPrefix != nil { - return *m.SwiftPrefix - } - return "" -} - -func (m *FileOptions) GetPhpClassPrefix() string { - if m != nil && m.PhpClassPrefix != nil { - return *m.PhpClassPrefix - } - return "" -} - -func (m *FileOptions) GetPhpNamespace() string { - if m != nil && m.PhpNamespace != nil { - return *m.PhpNamespace - } - return "" -} - -func (m *FileOptions) GetUninterpretedOption() []*UninterpretedOption { - if m != nil { - return m.UninterpretedOption - } - return nil -} - -type MessageOptions struct { - // Set true to use the old proto1 MessageSet wire format for extensions. - // This is provided for backwards-compatibility with the MessageSet wire - // format. You should not use this for any other reason: It's less - // efficient, has fewer features, and is more complicated. - // - // The message must be defined exactly as follows: - // message Foo { - // option message_set_wire_format = true; - // extensions 4 to max; - // } - // Note that the message cannot have any defined fields; MessageSets only - // have extensions. - // - // All extensions of your type must be singular messages; e.g. they cannot - // be int32s, enums, or repeated messages. - // - // Because this is an option, the above two restrictions are not enforced by - // the protocol compiler. - MessageSetWireFormat *bool `protobuf:"varint,1,opt,name=message_set_wire_format,json=messageSetWireFormat,def=0" json:"message_set_wire_format,omitempty"` - // Disables the generation of the standard "descriptor()" accessor, which can - // conflict with a field of the same name. This is meant to make migration - // from proto1 easier; new code should avoid fields named "descriptor". - NoStandardDescriptorAccessor *bool `protobuf:"varint,2,opt,name=no_standard_descriptor_accessor,json=noStandardDescriptorAccessor,def=0" json:"no_standard_descriptor_accessor,omitempty"` - // Is this message deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the message, or it will be completely ignored; in the very least, - // this is a formalization for deprecating messages. - Deprecated *bool `protobuf:"varint,3,opt,name=deprecated,def=0" json:"deprecated,omitempty"` - // Whether the message is an automatically generated map entry type for the - // maps field. - // - // For maps fields: - // map map_field = 1; - // The parsed descriptor looks like: - // message MapFieldEntry { - // option map_entry = true; - // optional KeyType key = 1; - // optional ValueType value = 2; - // } - // repeated MapFieldEntry map_field = 1; - // - // Implementations may choose not to generate the map_entry=true message, but - // use a native map in the target language to hold the keys and values. - // The reflection APIs in such implementions still need to work as - // if the field is a repeated message field. - // - // NOTE: Do not set the option in .proto files. Always use the maps syntax - // instead. The option should only be implicitly set by the proto compiler - // parser. - MapEntry *bool `protobuf:"varint,7,opt,name=map_entry,json=mapEntry" json:"map_entry,omitempty"` - // The parser stores options it doesn't recognize here. See above. - UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - proto.XXX_InternalExtensions `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *MessageOptions) Reset() { *m = MessageOptions{} } -func (m *MessageOptions) String() string { return proto.CompactTextString(m) } -func (*MessageOptions) ProtoMessage() {} -func (*MessageOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{11} -} - -var extRange_MessageOptions = []proto.ExtensionRange{ - {Start: 1000, End: 536870911}, -} - -func (*MessageOptions) ExtensionRangeArray() []proto.ExtensionRange { - return extRange_MessageOptions -} -func (m *MessageOptions) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_MessageOptions.Unmarshal(m, b) -} -func (m *MessageOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_MessageOptions.Marshal(b, m, deterministic) -} -func (dst *MessageOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_MessageOptions.Merge(dst, src) -} -func (m *MessageOptions) XXX_Size() int { - return xxx_messageInfo_MessageOptions.Size(m) -} -func (m *MessageOptions) XXX_DiscardUnknown() { - xxx_messageInfo_MessageOptions.DiscardUnknown(m) -} - -var xxx_messageInfo_MessageOptions proto.InternalMessageInfo - -const Default_MessageOptions_MessageSetWireFormat bool = false -const Default_MessageOptions_NoStandardDescriptorAccessor bool = false -const Default_MessageOptions_Deprecated bool = false - -func (m *MessageOptions) GetMessageSetWireFormat() bool { - if m != nil && m.MessageSetWireFormat != nil { - return *m.MessageSetWireFormat - } - return Default_MessageOptions_MessageSetWireFormat -} - -func (m *MessageOptions) GetNoStandardDescriptorAccessor() bool { - if m != nil && m.NoStandardDescriptorAccessor != nil { - return *m.NoStandardDescriptorAccessor - } - return Default_MessageOptions_NoStandardDescriptorAccessor -} - -func (m *MessageOptions) GetDeprecated() bool { - if m != nil && m.Deprecated != nil { - return *m.Deprecated - } - return Default_MessageOptions_Deprecated -} - -func (m *MessageOptions) GetMapEntry() bool { - if m != nil && m.MapEntry != nil { - return *m.MapEntry - } - return false -} - -func (m *MessageOptions) GetUninterpretedOption() []*UninterpretedOption { - if m != nil { - return m.UninterpretedOption - } - return nil -} - -type FieldOptions struct { - // The ctype option instructs the C++ code generator to use a different - // representation of the field than it normally would. See the specific - // options below. This option is not yet implemented in the open source - // release -- sorry, we'll try to include it in a future version! - Ctype *FieldOptions_CType `protobuf:"varint,1,opt,name=ctype,enum=google.protobuf.FieldOptions_CType,def=0" json:"ctype,omitempty"` - // The packed option can be enabled for repeated primitive fields to enable - // a more efficient representation on the wire. Rather than repeatedly - // writing the tag and type for each element, the entire array is encoded as - // a single length-delimited blob. In proto3, only explicit setting it to - // false will avoid using packed encoding. - Packed *bool `protobuf:"varint,2,opt,name=packed" json:"packed,omitempty"` - // The jstype option determines the JavaScript type used for values of the - // field. The option is permitted only for 64 bit integral and fixed types - // (int64, uint64, sint64, fixed64, sfixed64). A field with jstype JS_STRING - // is represented as JavaScript string, which avoids loss of precision that - // can happen when a large value is converted to a floating point JavaScript. - // Specifying JS_NUMBER for the jstype causes the generated JavaScript code to - // use the JavaScript "number" type. The behavior of the default option - // JS_NORMAL is implementation dependent. - // - // This option is an enum to permit additional types to be added, e.g. - // goog.math.Integer. - Jstype *FieldOptions_JSType `protobuf:"varint,6,opt,name=jstype,enum=google.protobuf.FieldOptions_JSType,def=0" json:"jstype,omitempty"` - // Should this field be parsed lazily? Lazy applies only to message-type - // fields. It means that when the outer message is initially parsed, the - // inner message's contents will not be parsed but instead stored in encoded - // form. The inner message will actually be parsed when it is first accessed. - // - // This is only a hint. Implementations are free to choose whether to use - // eager or lazy parsing regardless of the value of this option. However, - // setting this option true suggests that the protocol author believes that - // using lazy parsing on this field is worth the additional bookkeeping - // overhead typically needed to implement it. - // - // This option does not affect the public interface of any generated code; - // all method signatures remain the same. Furthermore, thread-safety of the - // interface is not affected by this option; const methods remain safe to - // call from multiple threads concurrently, while non-const methods continue - // to require exclusive access. - // - // - // Note that implementations may choose not to check required fields within - // a lazy sub-message. That is, calling IsInitialized() on the outer message - // may return true even if the inner message has missing required fields. - // This is necessary because otherwise the inner message would have to be - // parsed in order to perform the check, defeating the purpose of lazy - // parsing. An implementation which chooses not to check required fields - // must be consistent about it. That is, for any particular sub-message, the - // implementation must either *always* check its required fields, or *never* - // check its required fields, regardless of whether or not the message has - // been parsed. - Lazy *bool `protobuf:"varint,5,opt,name=lazy,def=0" json:"lazy,omitempty"` - // Is this field deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for accessors, or it will be completely ignored; in the very least, this - // is a formalization for deprecating fields. - Deprecated *bool `protobuf:"varint,3,opt,name=deprecated,def=0" json:"deprecated,omitempty"` - // For Google-internal migration only. Do not use. - Weak *bool `protobuf:"varint,10,opt,name=weak,def=0" json:"weak,omitempty"` - // The parser stores options it doesn't recognize here. See above. - UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - proto.XXX_InternalExtensions `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FieldOptions) Reset() { *m = FieldOptions{} } -func (m *FieldOptions) String() string { return proto.CompactTextString(m) } -func (*FieldOptions) ProtoMessage() {} -func (*FieldOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{12} -} - -var extRange_FieldOptions = []proto.ExtensionRange{ - {Start: 1000, End: 536870911}, -} - -func (*FieldOptions) ExtensionRangeArray() []proto.ExtensionRange { - return extRange_FieldOptions -} -func (m *FieldOptions) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FieldOptions.Unmarshal(m, b) -} -func (m *FieldOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FieldOptions.Marshal(b, m, deterministic) -} -func (dst *FieldOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_FieldOptions.Merge(dst, src) -} -func (m *FieldOptions) XXX_Size() int { - return xxx_messageInfo_FieldOptions.Size(m) -} -func (m *FieldOptions) XXX_DiscardUnknown() { - xxx_messageInfo_FieldOptions.DiscardUnknown(m) -} - -var xxx_messageInfo_FieldOptions proto.InternalMessageInfo - -const Default_FieldOptions_Ctype FieldOptions_CType = FieldOptions_STRING -const Default_FieldOptions_Jstype FieldOptions_JSType = FieldOptions_JS_NORMAL -const Default_FieldOptions_Lazy bool = false -const Default_FieldOptions_Deprecated bool = false -const Default_FieldOptions_Weak bool = false - -func (m *FieldOptions) GetCtype() FieldOptions_CType { - if m != nil && m.Ctype != nil { - return *m.Ctype - } - return Default_FieldOptions_Ctype -} - -func (m *FieldOptions) GetPacked() bool { - if m != nil && m.Packed != nil { - return *m.Packed - } - return false -} - -func (m *FieldOptions) GetJstype() FieldOptions_JSType { - if m != nil && m.Jstype != nil { - return *m.Jstype - } - return Default_FieldOptions_Jstype -} - -func (m *FieldOptions) GetLazy() bool { - if m != nil && m.Lazy != nil { - return *m.Lazy - } - return Default_FieldOptions_Lazy -} - -func (m *FieldOptions) GetDeprecated() bool { - if m != nil && m.Deprecated != nil { - return *m.Deprecated - } - return Default_FieldOptions_Deprecated -} - -func (m *FieldOptions) GetWeak() bool { - if m != nil && m.Weak != nil { - return *m.Weak - } - return Default_FieldOptions_Weak -} - -func (m *FieldOptions) GetUninterpretedOption() []*UninterpretedOption { - if m != nil { - return m.UninterpretedOption - } - return nil -} - -type OneofOptions struct { - // The parser stores options it doesn't recognize here. See above. - UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - proto.XXX_InternalExtensions `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *OneofOptions) Reset() { *m = OneofOptions{} } -func (m *OneofOptions) String() string { return proto.CompactTextString(m) } -func (*OneofOptions) ProtoMessage() {} -func (*OneofOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{13} -} - -var extRange_OneofOptions = []proto.ExtensionRange{ - {Start: 1000, End: 536870911}, -} - -func (*OneofOptions) ExtensionRangeArray() []proto.ExtensionRange { - return extRange_OneofOptions -} -func (m *OneofOptions) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_OneofOptions.Unmarshal(m, b) -} -func (m *OneofOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_OneofOptions.Marshal(b, m, deterministic) -} -func (dst *OneofOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_OneofOptions.Merge(dst, src) -} -func (m *OneofOptions) XXX_Size() int { - return xxx_messageInfo_OneofOptions.Size(m) -} -func (m *OneofOptions) XXX_DiscardUnknown() { - xxx_messageInfo_OneofOptions.DiscardUnknown(m) -} - -var xxx_messageInfo_OneofOptions proto.InternalMessageInfo - -func (m *OneofOptions) GetUninterpretedOption() []*UninterpretedOption { - if m != nil { - return m.UninterpretedOption - } - return nil -} - -type EnumOptions struct { - // Set this option to true to allow mapping different tag names to the same - // value. - AllowAlias *bool `protobuf:"varint,2,opt,name=allow_alias,json=allowAlias" json:"allow_alias,omitempty"` - // Is this enum deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the enum, or it will be completely ignored; in the very least, this - // is a formalization for deprecating enums. - Deprecated *bool `protobuf:"varint,3,opt,name=deprecated,def=0" json:"deprecated,omitempty"` - // The parser stores options it doesn't recognize here. See above. - UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - proto.XXX_InternalExtensions `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EnumOptions) Reset() { *m = EnumOptions{} } -func (m *EnumOptions) String() string { return proto.CompactTextString(m) } -func (*EnumOptions) ProtoMessage() {} -func (*EnumOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{14} -} - -var extRange_EnumOptions = []proto.ExtensionRange{ - {Start: 1000, End: 536870911}, -} - -func (*EnumOptions) ExtensionRangeArray() []proto.ExtensionRange { - return extRange_EnumOptions -} -func (m *EnumOptions) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EnumOptions.Unmarshal(m, b) -} -func (m *EnumOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EnumOptions.Marshal(b, m, deterministic) -} -func (dst *EnumOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnumOptions.Merge(dst, src) -} -func (m *EnumOptions) XXX_Size() int { - return xxx_messageInfo_EnumOptions.Size(m) -} -func (m *EnumOptions) XXX_DiscardUnknown() { - xxx_messageInfo_EnumOptions.DiscardUnknown(m) -} - -var xxx_messageInfo_EnumOptions proto.InternalMessageInfo - -const Default_EnumOptions_Deprecated bool = false - -func (m *EnumOptions) GetAllowAlias() bool { - if m != nil && m.AllowAlias != nil { - return *m.AllowAlias - } - return false -} - -func (m *EnumOptions) GetDeprecated() bool { - if m != nil && m.Deprecated != nil { - return *m.Deprecated - } - return Default_EnumOptions_Deprecated -} - -func (m *EnumOptions) GetUninterpretedOption() []*UninterpretedOption { - if m != nil { - return m.UninterpretedOption - } - return nil -} - -type EnumValueOptions struct { - // Is this enum value deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the enum value, or it will be completely ignored; in the very least, - // this is a formalization for deprecating enum values. - Deprecated *bool `protobuf:"varint,1,opt,name=deprecated,def=0" json:"deprecated,omitempty"` - // The parser stores options it doesn't recognize here. See above. - UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - proto.XXX_InternalExtensions `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *EnumValueOptions) Reset() { *m = EnumValueOptions{} } -func (m *EnumValueOptions) String() string { return proto.CompactTextString(m) } -func (*EnumValueOptions) ProtoMessage() {} -func (*EnumValueOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{15} -} - -var extRange_EnumValueOptions = []proto.ExtensionRange{ - {Start: 1000, End: 536870911}, -} - -func (*EnumValueOptions) ExtensionRangeArray() []proto.ExtensionRange { - return extRange_EnumValueOptions -} -func (m *EnumValueOptions) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_EnumValueOptions.Unmarshal(m, b) -} -func (m *EnumValueOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_EnumValueOptions.Marshal(b, m, deterministic) -} -func (dst *EnumValueOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_EnumValueOptions.Merge(dst, src) -} -func (m *EnumValueOptions) XXX_Size() int { - return xxx_messageInfo_EnumValueOptions.Size(m) -} -func (m *EnumValueOptions) XXX_DiscardUnknown() { - xxx_messageInfo_EnumValueOptions.DiscardUnknown(m) -} - -var xxx_messageInfo_EnumValueOptions proto.InternalMessageInfo - -const Default_EnumValueOptions_Deprecated bool = false - -func (m *EnumValueOptions) GetDeprecated() bool { - if m != nil && m.Deprecated != nil { - return *m.Deprecated - } - return Default_EnumValueOptions_Deprecated -} - -func (m *EnumValueOptions) GetUninterpretedOption() []*UninterpretedOption { - if m != nil { - return m.UninterpretedOption - } - return nil -} - -type ServiceOptions struct { - // Is this service deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the service, or it will be completely ignored; in the very least, - // this is a formalization for deprecating services. - Deprecated *bool `protobuf:"varint,33,opt,name=deprecated,def=0" json:"deprecated,omitempty"` - // The parser stores options it doesn't recognize here. See above. - UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - proto.XXX_InternalExtensions `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ServiceOptions) Reset() { *m = ServiceOptions{} } -func (m *ServiceOptions) String() string { return proto.CompactTextString(m) } -func (*ServiceOptions) ProtoMessage() {} -func (*ServiceOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{16} -} - -var extRange_ServiceOptions = []proto.ExtensionRange{ - {Start: 1000, End: 536870911}, -} - -func (*ServiceOptions) ExtensionRangeArray() []proto.ExtensionRange { - return extRange_ServiceOptions -} -func (m *ServiceOptions) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ServiceOptions.Unmarshal(m, b) -} -func (m *ServiceOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ServiceOptions.Marshal(b, m, deterministic) -} -func (dst *ServiceOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_ServiceOptions.Merge(dst, src) -} -func (m *ServiceOptions) XXX_Size() int { - return xxx_messageInfo_ServiceOptions.Size(m) -} -func (m *ServiceOptions) XXX_DiscardUnknown() { - xxx_messageInfo_ServiceOptions.DiscardUnknown(m) -} - -var xxx_messageInfo_ServiceOptions proto.InternalMessageInfo - -const Default_ServiceOptions_Deprecated bool = false - -func (m *ServiceOptions) GetDeprecated() bool { - if m != nil && m.Deprecated != nil { - return *m.Deprecated - } - return Default_ServiceOptions_Deprecated -} - -func (m *ServiceOptions) GetUninterpretedOption() []*UninterpretedOption { - if m != nil { - return m.UninterpretedOption - } - return nil -} - -type MethodOptions struct { - // Is this method deprecated? - // Depending on the target platform, this can emit Deprecated annotations - // for the method, or it will be completely ignored; in the very least, - // this is a formalization for deprecating methods. - Deprecated *bool `protobuf:"varint,33,opt,name=deprecated,def=0" json:"deprecated,omitempty"` - IdempotencyLevel *MethodOptions_IdempotencyLevel `protobuf:"varint,34,opt,name=idempotency_level,json=idempotencyLevel,enum=google.protobuf.MethodOptions_IdempotencyLevel,def=0" json:"idempotency_level,omitempty"` - // The parser stores options it doesn't recognize here. See above. - UninterpretedOption []*UninterpretedOption `protobuf:"bytes,999,rep,name=uninterpreted_option,json=uninterpretedOption" json:"uninterpreted_option,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - proto.XXX_InternalExtensions `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *MethodOptions) Reset() { *m = MethodOptions{} } -func (m *MethodOptions) String() string { return proto.CompactTextString(m) } -func (*MethodOptions) ProtoMessage() {} -func (*MethodOptions) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{17} -} - -var extRange_MethodOptions = []proto.ExtensionRange{ - {Start: 1000, End: 536870911}, -} - -func (*MethodOptions) ExtensionRangeArray() []proto.ExtensionRange { - return extRange_MethodOptions -} -func (m *MethodOptions) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_MethodOptions.Unmarshal(m, b) -} -func (m *MethodOptions) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_MethodOptions.Marshal(b, m, deterministic) -} -func (dst *MethodOptions) XXX_Merge(src proto.Message) { - xxx_messageInfo_MethodOptions.Merge(dst, src) -} -func (m *MethodOptions) XXX_Size() int { - return xxx_messageInfo_MethodOptions.Size(m) -} -func (m *MethodOptions) XXX_DiscardUnknown() { - xxx_messageInfo_MethodOptions.DiscardUnknown(m) -} - -var xxx_messageInfo_MethodOptions proto.InternalMessageInfo - -const Default_MethodOptions_Deprecated bool = false -const Default_MethodOptions_IdempotencyLevel MethodOptions_IdempotencyLevel = MethodOptions_IDEMPOTENCY_UNKNOWN - -func (m *MethodOptions) GetDeprecated() bool { - if m != nil && m.Deprecated != nil { - return *m.Deprecated - } - return Default_MethodOptions_Deprecated -} - -func (m *MethodOptions) GetIdempotencyLevel() MethodOptions_IdempotencyLevel { - if m != nil && m.IdempotencyLevel != nil { - return *m.IdempotencyLevel - } - return Default_MethodOptions_IdempotencyLevel -} - -func (m *MethodOptions) GetUninterpretedOption() []*UninterpretedOption { - if m != nil { - return m.UninterpretedOption - } - return nil -} - -// A message representing a option the parser does not recognize. This only -// appears in options protos created by the compiler::Parser class. -// DescriptorPool resolves these when building Descriptor objects. Therefore, -// options protos in descriptor objects (e.g. returned by Descriptor::options(), -// or produced by Descriptor::CopyTo()) will never have UninterpretedOptions -// in them. -type UninterpretedOption struct { - Name []*UninterpretedOption_NamePart `protobuf:"bytes,2,rep,name=name" json:"name,omitempty"` - // The value of the uninterpreted option, in whatever type the tokenizer - // identified it as during parsing. Exactly one of these should be set. - IdentifierValue *string `protobuf:"bytes,3,opt,name=identifier_value,json=identifierValue" json:"identifier_value,omitempty"` - PositiveIntValue *uint64 `protobuf:"varint,4,opt,name=positive_int_value,json=positiveIntValue" json:"positive_int_value,omitempty"` - NegativeIntValue *int64 `protobuf:"varint,5,opt,name=negative_int_value,json=negativeIntValue" json:"negative_int_value,omitempty"` - DoubleValue *float64 `protobuf:"fixed64,6,opt,name=double_value,json=doubleValue" json:"double_value,omitempty"` - StringValue []byte `protobuf:"bytes,7,opt,name=string_value,json=stringValue" json:"string_value,omitempty"` - AggregateValue *string `protobuf:"bytes,8,opt,name=aggregate_value,json=aggregateValue" json:"aggregate_value,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UninterpretedOption) Reset() { *m = UninterpretedOption{} } -func (m *UninterpretedOption) String() string { return proto.CompactTextString(m) } -func (*UninterpretedOption) ProtoMessage() {} -func (*UninterpretedOption) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{18} -} -func (m *UninterpretedOption) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UninterpretedOption.Unmarshal(m, b) -} -func (m *UninterpretedOption) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UninterpretedOption.Marshal(b, m, deterministic) -} -func (dst *UninterpretedOption) XXX_Merge(src proto.Message) { - xxx_messageInfo_UninterpretedOption.Merge(dst, src) -} -func (m *UninterpretedOption) XXX_Size() int { - return xxx_messageInfo_UninterpretedOption.Size(m) -} -func (m *UninterpretedOption) XXX_DiscardUnknown() { - xxx_messageInfo_UninterpretedOption.DiscardUnknown(m) -} - -var xxx_messageInfo_UninterpretedOption proto.InternalMessageInfo - -func (m *UninterpretedOption) GetName() []*UninterpretedOption_NamePart { - if m != nil { - return m.Name - } - return nil -} - -func (m *UninterpretedOption) GetIdentifierValue() string { - if m != nil && m.IdentifierValue != nil { - return *m.IdentifierValue - } - return "" -} - -func (m *UninterpretedOption) GetPositiveIntValue() uint64 { - if m != nil && m.PositiveIntValue != nil { - return *m.PositiveIntValue - } - return 0 -} - -func (m *UninterpretedOption) GetNegativeIntValue() int64 { - if m != nil && m.NegativeIntValue != nil { - return *m.NegativeIntValue - } - return 0 -} - -func (m *UninterpretedOption) GetDoubleValue() float64 { - if m != nil && m.DoubleValue != nil { - return *m.DoubleValue - } - return 0 -} - -func (m *UninterpretedOption) GetStringValue() []byte { - if m != nil { - return m.StringValue - } - return nil -} - -func (m *UninterpretedOption) GetAggregateValue() string { - if m != nil && m.AggregateValue != nil { - return *m.AggregateValue - } - return "" -} - -// The name of the uninterpreted option. Each string represents a segment in -// a dot-separated name. is_extension is true iff a segment represents an -// extension (denoted with parentheses in options specs in .proto files). -// E.g.,{ ["foo", false], ["bar.baz", true], ["qux", false] } represents -// "foo.(bar.baz).qux". -type UninterpretedOption_NamePart struct { - NamePart *string `protobuf:"bytes,1,req,name=name_part,json=namePart" json:"name_part,omitempty"` - IsExtension *bool `protobuf:"varint,2,req,name=is_extension,json=isExtension" json:"is_extension,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UninterpretedOption_NamePart) Reset() { *m = UninterpretedOption_NamePart{} } -func (m *UninterpretedOption_NamePart) String() string { return proto.CompactTextString(m) } -func (*UninterpretedOption_NamePart) ProtoMessage() {} -func (*UninterpretedOption_NamePart) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{18, 0} -} -func (m *UninterpretedOption_NamePart) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UninterpretedOption_NamePart.Unmarshal(m, b) -} -func (m *UninterpretedOption_NamePart) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UninterpretedOption_NamePart.Marshal(b, m, deterministic) -} -func (dst *UninterpretedOption_NamePart) XXX_Merge(src proto.Message) { - xxx_messageInfo_UninterpretedOption_NamePart.Merge(dst, src) -} -func (m *UninterpretedOption_NamePart) XXX_Size() int { - return xxx_messageInfo_UninterpretedOption_NamePart.Size(m) -} -func (m *UninterpretedOption_NamePart) XXX_DiscardUnknown() { - xxx_messageInfo_UninterpretedOption_NamePart.DiscardUnknown(m) -} - -var xxx_messageInfo_UninterpretedOption_NamePart proto.InternalMessageInfo - -func (m *UninterpretedOption_NamePart) GetNamePart() string { - if m != nil && m.NamePart != nil { - return *m.NamePart - } - return "" -} - -func (m *UninterpretedOption_NamePart) GetIsExtension() bool { - if m != nil && m.IsExtension != nil { - return *m.IsExtension - } - return false -} - -// Encapsulates information about the original source file from which a -// FileDescriptorProto was generated. -type SourceCodeInfo struct { - // A Location identifies a piece of source code in a .proto file which - // corresponds to a particular definition. This information is intended - // to be useful to IDEs, code indexers, documentation generators, and similar - // tools. - // - // For example, say we have a file like: - // message Foo { - // optional string foo = 1; - // } - // Let's look at just the field definition: - // optional string foo = 1; - // ^ ^^ ^^ ^ ^^^ - // a bc de f ghi - // We have the following locations: - // span path represents - // [a,i) [ 4, 0, 2, 0 ] The whole field definition. - // [a,b) [ 4, 0, 2, 0, 4 ] The label (optional). - // [c,d) [ 4, 0, 2, 0, 5 ] The type (string). - // [e,f) [ 4, 0, 2, 0, 1 ] The name (foo). - // [g,h) [ 4, 0, 2, 0, 3 ] The number (1). - // - // Notes: - // - A location may refer to a repeated field itself (i.e. not to any - // particular index within it). This is used whenever a set of elements are - // logically enclosed in a single code segment. For example, an entire - // extend block (possibly containing multiple extension definitions) will - // have an outer location whose path refers to the "extensions" repeated - // field without an index. - // - Multiple locations may have the same path. This happens when a single - // logical declaration is spread out across multiple places. The most - // obvious example is the "extend" block again -- there may be multiple - // extend blocks in the same scope, each of which will have the same path. - // - A location's span is not always a subset of its parent's span. For - // example, the "extendee" of an extension declaration appears at the - // beginning of the "extend" block and is shared by all extensions within - // the block. - // - Just because a location's span is a subset of some other location's span - // does not mean that it is a descendent. For example, a "group" defines - // both a type and a field in a single declaration. Thus, the locations - // corresponding to the type and field and their components will overlap. - // - Code which tries to interpret locations should probably be designed to - // ignore those that it doesn't understand, as more types of locations could - // be recorded in the future. - Location []*SourceCodeInfo_Location `protobuf:"bytes,1,rep,name=location" json:"location,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *SourceCodeInfo) Reset() { *m = SourceCodeInfo{} } -func (m *SourceCodeInfo) String() string { return proto.CompactTextString(m) } -func (*SourceCodeInfo) ProtoMessage() {} -func (*SourceCodeInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{19} -} -func (m *SourceCodeInfo) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SourceCodeInfo.Unmarshal(m, b) -} -func (m *SourceCodeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SourceCodeInfo.Marshal(b, m, deterministic) -} -func (dst *SourceCodeInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_SourceCodeInfo.Merge(dst, src) -} -func (m *SourceCodeInfo) XXX_Size() int { - return xxx_messageInfo_SourceCodeInfo.Size(m) -} -func (m *SourceCodeInfo) XXX_DiscardUnknown() { - xxx_messageInfo_SourceCodeInfo.DiscardUnknown(m) -} - -var xxx_messageInfo_SourceCodeInfo proto.InternalMessageInfo - -func (m *SourceCodeInfo) GetLocation() []*SourceCodeInfo_Location { - if m != nil { - return m.Location - } - return nil -} - -type SourceCodeInfo_Location struct { - // Identifies which part of the FileDescriptorProto was defined at this - // location. - // - // Each element is a field number or an index. They form a path from - // the root FileDescriptorProto to the place where the definition. For - // example, this path: - // [ 4, 3, 2, 7, 1 ] - // refers to: - // file.message_type(3) // 4, 3 - // .field(7) // 2, 7 - // .name() // 1 - // This is because FileDescriptorProto.message_type has field number 4: - // repeated DescriptorProto message_type = 4; - // and DescriptorProto.field has field number 2: - // repeated FieldDescriptorProto field = 2; - // and FieldDescriptorProto.name has field number 1: - // optional string name = 1; - // - // Thus, the above path gives the location of a field name. If we removed - // the last element: - // [ 4, 3, 2, 7 ] - // this path refers to the whole field declaration (from the beginning - // of the label to the terminating semicolon). - Path []int32 `protobuf:"varint,1,rep,packed,name=path" json:"path,omitempty"` - // Always has exactly three or four elements: start line, start column, - // end line (optional, otherwise assumed same as start line), end column. - // These are packed into a single field for efficiency. Note that line - // and column numbers are zero-based -- typically you will want to add - // 1 to each before displaying to a user. - Span []int32 `protobuf:"varint,2,rep,packed,name=span" json:"span,omitempty"` - // If this SourceCodeInfo represents a complete declaration, these are any - // comments appearing before and after the declaration which appear to be - // attached to the declaration. - // - // A series of line comments appearing on consecutive lines, with no other - // tokens appearing on those lines, will be treated as a single comment. - // - // leading_detached_comments will keep paragraphs of comments that appear - // before (but not connected to) the current element. Each paragraph, - // separated by empty lines, will be one comment element in the repeated - // field. - // - // Only the comment content is provided; comment markers (e.g. //) are - // stripped out. For block comments, leading whitespace and an asterisk - // will be stripped from the beginning of each line other than the first. - // Newlines are included in the output. - // - // Examples: - // - // optional int32 foo = 1; // Comment attached to foo. - // // Comment attached to bar. - // optional int32 bar = 2; - // - // optional string baz = 3; - // // Comment attached to baz. - // // Another line attached to baz. - // - // // Comment attached to qux. - // // - // // Another line attached to qux. - // optional double qux = 4; - // - // // Detached comment for corge. This is not leading or trailing comments - // // to qux or corge because there are blank lines separating it from - // // both. - // - // // Detached comment for corge paragraph 2. - // - // optional string corge = 5; - // /* Block comment attached - // * to corge. Leading asterisks - // * will be removed. */ - // /* Block comment attached to - // * grault. */ - // optional int32 grault = 6; - // - // // ignored detached comments. - LeadingComments *string `protobuf:"bytes,3,opt,name=leading_comments,json=leadingComments" json:"leading_comments,omitempty"` - TrailingComments *string `protobuf:"bytes,4,opt,name=trailing_comments,json=trailingComments" json:"trailing_comments,omitempty"` - LeadingDetachedComments []string `protobuf:"bytes,6,rep,name=leading_detached_comments,json=leadingDetachedComments" json:"leading_detached_comments,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *SourceCodeInfo_Location) Reset() { *m = SourceCodeInfo_Location{} } -func (m *SourceCodeInfo_Location) String() string { return proto.CompactTextString(m) } -func (*SourceCodeInfo_Location) ProtoMessage() {} -func (*SourceCodeInfo_Location) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{19, 0} -} -func (m *SourceCodeInfo_Location) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SourceCodeInfo_Location.Unmarshal(m, b) -} -func (m *SourceCodeInfo_Location) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SourceCodeInfo_Location.Marshal(b, m, deterministic) -} -func (dst *SourceCodeInfo_Location) XXX_Merge(src proto.Message) { - xxx_messageInfo_SourceCodeInfo_Location.Merge(dst, src) -} -func (m *SourceCodeInfo_Location) XXX_Size() int { - return xxx_messageInfo_SourceCodeInfo_Location.Size(m) -} -func (m *SourceCodeInfo_Location) XXX_DiscardUnknown() { - xxx_messageInfo_SourceCodeInfo_Location.DiscardUnknown(m) -} - -var xxx_messageInfo_SourceCodeInfo_Location proto.InternalMessageInfo - -func (m *SourceCodeInfo_Location) GetPath() []int32 { - if m != nil { - return m.Path - } - return nil -} - -func (m *SourceCodeInfo_Location) GetSpan() []int32 { - if m != nil { - return m.Span - } - return nil -} - -func (m *SourceCodeInfo_Location) GetLeadingComments() string { - if m != nil && m.LeadingComments != nil { - return *m.LeadingComments - } - return "" -} - -func (m *SourceCodeInfo_Location) GetTrailingComments() string { - if m != nil && m.TrailingComments != nil { - return *m.TrailingComments - } - return "" -} - -func (m *SourceCodeInfo_Location) GetLeadingDetachedComments() []string { - if m != nil { - return m.LeadingDetachedComments - } - return nil -} - -// Describes the relationship between generated code and its original source -// file. A GeneratedCodeInfo message is associated with only one generated -// source file, but may contain references to different source .proto files. -type GeneratedCodeInfo struct { - // An Annotation connects some span of text in generated code to an element - // of its generating .proto file. - Annotation []*GeneratedCodeInfo_Annotation `protobuf:"bytes,1,rep,name=annotation" json:"annotation,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GeneratedCodeInfo) Reset() { *m = GeneratedCodeInfo{} } -func (m *GeneratedCodeInfo) String() string { return proto.CompactTextString(m) } -func (*GeneratedCodeInfo) ProtoMessage() {} -func (*GeneratedCodeInfo) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{20} -} -func (m *GeneratedCodeInfo) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GeneratedCodeInfo.Unmarshal(m, b) -} -func (m *GeneratedCodeInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GeneratedCodeInfo.Marshal(b, m, deterministic) -} -func (dst *GeneratedCodeInfo) XXX_Merge(src proto.Message) { - xxx_messageInfo_GeneratedCodeInfo.Merge(dst, src) -} -func (m *GeneratedCodeInfo) XXX_Size() int { - return xxx_messageInfo_GeneratedCodeInfo.Size(m) -} -func (m *GeneratedCodeInfo) XXX_DiscardUnknown() { - xxx_messageInfo_GeneratedCodeInfo.DiscardUnknown(m) -} - -var xxx_messageInfo_GeneratedCodeInfo proto.InternalMessageInfo - -func (m *GeneratedCodeInfo) GetAnnotation() []*GeneratedCodeInfo_Annotation { - if m != nil { - return m.Annotation - } - return nil -} - -type GeneratedCodeInfo_Annotation struct { - // Identifies the element in the original source .proto file. This field - // is formatted the same as SourceCodeInfo.Location.path. - Path []int32 `protobuf:"varint,1,rep,packed,name=path" json:"path,omitempty"` - // Identifies the filesystem path to the original source .proto. - SourceFile *string `protobuf:"bytes,2,opt,name=source_file,json=sourceFile" json:"source_file,omitempty"` - // Identifies the starting offset in bytes in the generated code - // that relates to the identified object. - Begin *int32 `protobuf:"varint,3,opt,name=begin" json:"begin,omitempty"` - // Identifies the ending offset in bytes in the generated code that - // relates to the identified offset. The end offset should be one past - // the last relevant byte (so the length of the text = end - begin). - End *int32 `protobuf:"varint,4,opt,name=end" json:"end,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GeneratedCodeInfo_Annotation) Reset() { *m = GeneratedCodeInfo_Annotation{} } -func (m *GeneratedCodeInfo_Annotation) String() string { return proto.CompactTextString(m) } -func (*GeneratedCodeInfo_Annotation) ProtoMessage() {} -func (*GeneratedCodeInfo_Annotation) Descriptor() ([]byte, []int) { - return fileDescriptor_descriptor_4df4cb5f42392df6, []int{20, 0} -} -func (m *GeneratedCodeInfo_Annotation) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GeneratedCodeInfo_Annotation.Unmarshal(m, b) -} -func (m *GeneratedCodeInfo_Annotation) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GeneratedCodeInfo_Annotation.Marshal(b, m, deterministic) -} -func (dst *GeneratedCodeInfo_Annotation) XXX_Merge(src proto.Message) { - xxx_messageInfo_GeneratedCodeInfo_Annotation.Merge(dst, src) -} -func (m *GeneratedCodeInfo_Annotation) XXX_Size() int { - return xxx_messageInfo_GeneratedCodeInfo_Annotation.Size(m) -} -func (m *GeneratedCodeInfo_Annotation) XXX_DiscardUnknown() { - xxx_messageInfo_GeneratedCodeInfo_Annotation.DiscardUnknown(m) -} - -var xxx_messageInfo_GeneratedCodeInfo_Annotation proto.InternalMessageInfo - -func (m *GeneratedCodeInfo_Annotation) GetPath() []int32 { - if m != nil { - return m.Path - } - return nil -} - -func (m *GeneratedCodeInfo_Annotation) GetSourceFile() string { - if m != nil && m.SourceFile != nil { - return *m.SourceFile - } - return "" -} - -func (m *GeneratedCodeInfo_Annotation) GetBegin() int32 { - if m != nil && m.Begin != nil { - return *m.Begin - } - return 0 -} - -func (m *GeneratedCodeInfo_Annotation) GetEnd() int32 { - if m != nil && m.End != nil { - return *m.End - } - return 0 -} - -func init() { - proto.RegisterType((*FileDescriptorSet)(nil), "google.protobuf.FileDescriptorSet") - proto.RegisterType((*FileDescriptorProto)(nil), "google.protobuf.FileDescriptorProto") - proto.RegisterType((*DescriptorProto)(nil), "google.protobuf.DescriptorProto") - proto.RegisterType((*DescriptorProto_ExtensionRange)(nil), "google.protobuf.DescriptorProto.ExtensionRange") - proto.RegisterType((*DescriptorProto_ReservedRange)(nil), "google.protobuf.DescriptorProto.ReservedRange") - proto.RegisterType((*ExtensionRangeOptions)(nil), "google.protobuf.ExtensionRangeOptions") - proto.RegisterType((*FieldDescriptorProto)(nil), "google.protobuf.FieldDescriptorProto") - proto.RegisterType((*OneofDescriptorProto)(nil), "google.protobuf.OneofDescriptorProto") - proto.RegisterType((*EnumDescriptorProto)(nil), "google.protobuf.EnumDescriptorProto") - proto.RegisterType((*EnumDescriptorProto_EnumReservedRange)(nil), "google.protobuf.EnumDescriptorProto.EnumReservedRange") - proto.RegisterType((*EnumValueDescriptorProto)(nil), "google.protobuf.EnumValueDescriptorProto") - proto.RegisterType((*ServiceDescriptorProto)(nil), "google.protobuf.ServiceDescriptorProto") - proto.RegisterType((*MethodDescriptorProto)(nil), "google.protobuf.MethodDescriptorProto") - proto.RegisterType((*FileOptions)(nil), "google.protobuf.FileOptions") - proto.RegisterType((*MessageOptions)(nil), "google.protobuf.MessageOptions") - proto.RegisterType((*FieldOptions)(nil), "google.protobuf.FieldOptions") - proto.RegisterType((*OneofOptions)(nil), "google.protobuf.OneofOptions") - proto.RegisterType((*EnumOptions)(nil), "google.protobuf.EnumOptions") - proto.RegisterType((*EnumValueOptions)(nil), "google.protobuf.EnumValueOptions") - proto.RegisterType((*ServiceOptions)(nil), "google.protobuf.ServiceOptions") - proto.RegisterType((*MethodOptions)(nil), "google.protobuf.MethodOptions") - proto.RegisterType((*UninterpretedOption)(nil), "google.protobuf.UninterpretedOption") - proto.RegisterType((*UninterpretedOption_NamePart)(nil), "google.protobuf.UninterpretedOption.NamePart") - proto.RegisterType((*SourceCodeInfo)(nil), "google.protobuf.SourceCodeInfo") - proto.RegisterType((*SourceCodeInfo_Location)(nil), "google.protobuf.SourceCodeInfo.Location") - proto.RegisterType((*GeneratedCodeInfo)(nil), "google.protobuf.GeneratedCodeInfo") - proto.RegisterType((*GeneratedCodeInfo_Annotation)(nil), "google.protobuf.GeneratedCodeInfo.Annotation") - proto.RegisterEnum("google.protobuf.FieldDescriptorProto_Type", FieldDescriptorProto_Type_name, FieldDescriptorProto_Type_value) - proto.RegisterEnum("google.protobuf.FieldDescriptorProto_Label", FieldDescriptorProto_Label_name, FieldDescriptorProto_Label_value) - proto.RegisterEnum("google.protobuf.FileOptions_OptimizeMode", FileOptions_OptimizeMode_name, FileOptions_OptimizeMode_value) - proto.RegisterEnum("google.protobuf.FieldOptions_CType", FieldOptions_CType_name, FieldOptions_CType_value) - proto.RegisterEnum("google.protobuf.FieldOptions_JSType", FieldOptions_JSType_name, FieldOptions_JSType_value) - proto.RegisterEnum("google.protobuf.MethodOptions_IdempotencyLevel", MethodOptions_IdempotencyLevel_name, MethodOptions_IdempotencyLevel_value) -} - -func init() { - proto.RegisterFile("google/protobuf/descriptor.proto", fileDescriptor_descriptor_4df4cb5f42392df6) -} - -var fileDescriptor_descriptor_4df4cb5f42392df6 = []byte{ - // 2555 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x59, 0xdd, 0x6e, 0x1b, 0xc7, - 0xf5, 0xcf, 0xf2, 0x4b, 0xe4, 0x21, 0x45, 0x8d, 0x46, 0x8a, 0xbd, 0x56, 0x3e, 0x2c, 0x33, 0x1f, - 0x96, 0x9d, 0x7f, 0xa8, 0xc0, 0xb1, 0x1d, 0x47, 0xfe, 0x23, 0x2d, 0x45, 0xae, 0x15, 0xaa, 0x12, - 0xc9, 0x2e, 0xa9, 0xe6, 0x03, 0x28, 0x16, 0xa3, 0xdd, 0x21, 0xb9, 0xf6, 0x72, 0x77, 0xb3, 0xbb, - 0xb4, 0xad, 0xa0, 0x17, 0x06, 0x7a, 0xd5, 0xab, 0xde, 0x16, 0x45, 0xd1, 0x8b, 0xde, 0x04, 0xe8, - 0x03, 0x14, 0xc8, 0x5d, 0x9f, 0xa0, 0x40, 0xde, 0xa0, 0x68, 0x0b, 0xb4, 0x8f, 0xd0, 0xcb, 0x62, - 0x66, 0x76, 0x97, 0xbb, 0x24, 0x15, 0x2b, 0x01, 0xe2, 0x5c, 0x91, 0xf3, 0x9b, 0xdf, 0x39, 0x73, - 0xe6, 0xcc, 0x99, 0x33, 0x67, 0x66, 0x61, 0x7b, 0xe4, 0x38, 0x23, 0x8b, 0xee, 0xba, 0x9e, 0x13, - 0x38, 0xa7, 0xd3, 0xe1, 0xae, 0x41, 0x7d, 0xdd, 0x33, 0xdd, 0xc0, 0xf1, 0xea, 0x1c, 0xc3, 0x6b, - 0x82, 0x51, 0x8f, 0x18, 0xb5, 0x63, 0x58, 0x7f, 0x60, 0x5a, 0xb4, 0x15, 0x13, 0xfb, 0x34, 0xc0, - 0xf7, 0x20, 0x37, 0x34, 0x2d, 0x2a, 0x4b, 0xdb, 0xd9, 0x9d, 0xf2, 0xad, 0x37, 0xeb, 0x73, 0x42, - 0xf5, 0xb4, 0x44, 0x8f, 0xc1, 0x2a, 0x97, 0xa8, 0xfd, 0x2b, 0x07, 0x1b, 0x4b, 0x7a, 0x31, 0x86, - 0x9c, 0x4d, 0x26, 0x4c, 0xa3, 0xb4, 0x53, 0x52, 0xf9, 0x7f, 0x2c, 0xc3, 0x8a, 0x4b, 0xf4, 0x47, - 0x64, 0x44, 0xe5, 0x0c, 0x87, 0xa3, 0x26, 0x7e, 0x1d, 0xc0, 0xa0, 0x2e, 0xb5, 0x0d, 0x6a, 0xeb, - 0x67, 0x72, 0x76, 0x3b, 0xbb, 0x53, 0x52, 0x13, 0x08, 0x7e, 0x07, 0xd6, 0xdd, 0xe9, 0xa9, 0x65, - 0xea, 0x5a, 0x82, 0x06, 0xdb, 0xd9, 0x9d, 0xbc, 0x8a, 0x44, 0x47, 0x6b, 0x46, 0xbe, 0x0e, 0x6b, - 0x4f, 0x28, 0x79, 0x94, 0xa4, 0x96, 0x39, 0xb5, 0xca, 0xe0, 0x04, 0xb1, 0x09, 0x95, 0x09, 0xf5, - 0x7d, 0x32, 0xa2, 0x5a, 0x70, 0xe6, 0x52, 0x39, 0xc7, 0x67, 0xbf, 0xbd, 0x30, 0xfb, 0xf9, 0x99, - 0x97, 0x43, 0xa9, 0xc1, 0x99, 0x4b, 0x71, 0x03, 0x4a, 0xd4, 0x9e, 0x4e, 0x84, 0x86, 0xfc, 0x39, - 0xfe, 0x53, 0xec, 0xe9, 0x64, 0x5e, 0x4b, 0x91, 0x89, 0x85, 0x2a, 0x56, 0x7c, 0xea, 0x3d, 0x36, - 0x75, 0x2a, 0x17, 0xb8, 0x82, 0xeb, 0x0b, 0x0a, 0xfa, 0xa2, 0x7f, 0x5e, 0x47, 0x24, 0x87, 0x9b, - 0x50, 0xa2, 0x4f, 0x03, 0x6a, 0xfb, 0xa6, 0x63, 0xcb, 0x2b, 0x5c, 0xc9, 0x5b, 0x4b, 0x56, 0x91, - 0x5a, 0xc6, 0xbc, 0x8a, 0x99, 0x1c, 0xbe, 0x0b, 0x2b, 0x8e, 0x1b, 0x98, 0x8e, 0xed, 0xcb, 0xc5, - 0x6d, 0x69, 0xa7, 0x7c, 0xeb, 0xd5, 0xa5, 0x81, 0xd0, 0x15, 0x1c, 0x35, 0x22, 0xe3, 0x36, 0x20, - 0xdf, 0x99, 0x7a, 0x3a, 0xd5, 0x74, 0xc7, 0xa0, 0x9a, 0x69, 0x0f, 0x1d, 0xb9, 0xc4, 0x15, 0x5c, - 0x5d, 0x9c, 0x08, 0x27, 0x36, 0x1d, 0x83, 0xb6, 0xed, 0xa1, 0xa3, 0x56, 0xfd, 0x54, 0x1b, 0x5f, - 0x82, 0x82, 0x7f, 0x66, 0x07, 0xe4, 0xa9, 0x5c, 0xe1, 0x11, 0x12, 0xb6, 0x6a, 0x5f, 0x17, 0x60, - 0xed, 0x22, 0x21, 0x76, 0x1f, 0xf2, 0x43, 0x36, 0x4b, 0x39, 0xf3, 0x5d, 0x7c, 0x20, 0x64, 0xd2, - 0x4e, 0x2c, 0x7c, 0x4f, 0x27, 0x36, 0xa0, 0x6c, 0x53, 0x3f, 0xa0, 0x86, 0x88, 0x88, 0xec, 0x05, - 0x63, 0x0a, 0x84, 0xd0, 0x62, 0x48, 0xe5, 0xbe, 0x57, 0x48, 0x7d, 0x0a, 0x6b, 0xb1, 0x49, 0x9a, - 0x47, 0xec, 0x51, 0x14, 0x9b, 0xbb, 0xcf, 0xb3, 0xa4, 0xae, 0x44, 0x72, 0x2a, 0x13, 0x53, 0xab, - 0x34, 0xd5, 0xc6, 0x2d, 0x00, 0xc7, 0xa6, 0xce, 0x50, 0x33, 0xa8, 0x6e, 0xc9, 0xc5, 0x73, 0xbc, - 0xd4, 0x65, 0x94, 0x05, 0x2f, 0x39, 0x02, 0xd5, 0x2d, 0xfc, 0xe1, 0x2c, 0xd4, 0x56, 0xce, 0x89, - 0x94, 0x63, 0xb1, 0xc9, 0x16, 0xa2, 0xed, 0x04, 0xaa, 0x1e, 0x65, 0x71, 0x4f, 0x8d, 0x70, 0x66, - 0x25, 0x6e, 0x44, 0xfd, 0xb9, 0x33, 0x53, 0x43, 0x31, 0x31, 0xb1, 0x55, 0x2f, 0xd9, 0xc4, 0x6f, - 0x40, 0x0c, 0x68, 0x3c, 0xac, 0x80, 0x67, 0xa1, 0x4a, 0x04, 0x76, 0xc8, 0x84, 0x6e, 0x7d, 0x09, - 0xd5, 0xb4, 0x7b, 0xf0, 0x26, 0xe4, 0xfd, 0x80, 0x78, 0x01, 0x8f, 0xc2, 0xbc, 0x2a, 0x1a, 0x18, - 0x41, 0x96, 0xda, 0x06, 0xcf, 0x72, 0x79, 0x95, 0xfd, 0xc5, 0x3f, 0x9d, 0x4d, 0x38, 0xcb, 0x27, - 0xfc, 0xf6, 0xe2, 0x8a, 0xa6, 0x34, 0xcf, 0xcf, 0x7b, 0xeb, 0x03, 0x58, 0x4d, 0x4d, 0xe0, 0xa2, - 0x43, 0xd7, 0x7e, 0x05, 0x2f, 0x2f, 0x55, 0x8d, 0x3f, 0x85, 0xcd, 0xa9, 0x6d, 0xda, 0x01, 0xf5, - 0x5c, 0x8f, 0xb2, 0x88, 0x15, 0x43, 0xc9, 0xff, 0x5e, 0x39, 0x27, 0xe6, 0x4e, 0x92, 0x6c, 0xa1, - 0x45, 0xdd, 0x98, 0x2e, 0x82, 0x37, 0x4b, 0xc5, 0xff, 0xac, 0xa0, 0x67, 0xcf, 0x9e, 0x3d, 0xcb, - 0xd4, 0x7e, 0x57, 0x80, 0xcd, 0x65, 0x7b, 0x66, 0xe9, 0xf6, 0xbd, 0x04, 0x05, 0x7b, 0x3a, 0x39, - 0xa5, 0x1e, 0x77, 0x52, 0x5e, 0x0d, 0x5b, 0xb8, 0x01, 0x79, 0x8b, 0x9c, 0x52, 0x4b, 0xce, 0x6d, - 0x4b, 0x3b, 0xd5, 0x5b, 0xef, 0x5c, 0x68, 0x57, 0xd6, 0x8f, 0x98, 0x88, 0x2a, 0x24, 0xf1, 0x47, - 0x90, 0x0b, 0x53, 0x34, 0xd3, 0x70, 0xf3, 0x62, 0x1a, 0xd8, 0x5e, 0x52, 0xb9, 0x1c, 0x7e, 0x05, - 0x4a, 0xec, 0x57, 0xc4, 0x46, 0x81, 0xdb, 0x5c, 0x64, 0x00, 0x8b, 0x0b, 0xbc, 0x05, 0x45, 0xbe, - 0x4d, 0x0c, 0x1a, 0x1d, 0x6d, 0x71, 0x9b, 0x05, 0x96, 0x41, 0x87, 0x64, 0x6a, 0x05, 0xda, 0x63, - 0x62, 0x4d, 0x29, 0x0f, 0xf8, 0x92, 0x5a, 0x09, 0xc1, 0x5f, 0x30, 0x0c, 0x5f, 0x85, 0xb2, 0xd8, - 0x55, 0xa6, 0x6d, 0xd0, 0xa7, 0x3c, 0x7b, 0xe6, 0x55, 0xb1, 0xd1, 0xda, 0x0c, 0x61, 0xc3, 0x3f, - 0xf4, 0x1d, 0x3b, 0x0a, 0x4d, 0x3e, 0x04, 0x03, 0xf8, 0xf0, 0x1f, 0xcc, 0x27, 0xee, 0xd7, 0x96, - 0x4f, 0x6f, 0x3e, 0xa6, 0x6a, 0x7f, 0xc9, 0x40, 0x8e, 0xe7, 0x8b, 0x35, 0x28, 0x0f, 0x3e, 0xeb, - 0x29, 0x5a, 0xab, 0x7b, 0xb2, 0x7f, 0xa4, 0x20, 0x09, 0x57, 0x01, 0x38, 0xf0, 0xe0, 0xa8, 0xdb, - 0x18, 0xa0, 0x4c, 0xdc, 0x6e, 0x77, 0x06, 0x77, 0x6f, 0xa3, 0x6c, 0x2c, 0x70, 0x22, 0x80, 0x5c, - 0x92, 0xf0, 0xfe, 0x2d, 0x94, 0xc7, 0x08, 0x2a, 0x42, 0x41, 0xfb, 0x53, 0xa5, 0x75, 0xf7, 0x36, - 0x2a, 0xa4, 0x91, 0xf7, 0x6f, 0xa1, 0x15, 0xbc, 0x0a, 0x25, 0x8e, 0xec, 0x77, 0xbb, 0x47, 0xa8, - 0x18, 0xeb, 0xec, 0x0f, 0xd4, 0x76, 0xe7, 0x00, 0x95, 0x62, 0x9d, 0x07, 0x6a, 0xf7, 0xa4, 0x87, - 0x20, 0xd6, 0x70, 0xac, 0xf4, 0xfb, 0x8d, 0x03, 0x05, 0x95, 0x63, 0xc6, 0xfe, 0x67, 0x03, 0xa5, - 0x8f, 0x2a, 0x29, 0xb3, 0xde, 0xbf, 0x85, 0x56, 0xe3, 0x21, 0x94, 0xce, 0xc9, 0x31, 0xaa, 0xe2, - 0x75, 0x58, 0x15, 0x43, 0x44, 0x46, 0xac, 0xcd, 0x41, 0x77, 0x6f, 0x23, 0x34, 0x33, 0x44, 0x68, - 0x59, 0x4f, 0x01, 0x77, 0x6f, 0x23, 0x5c, 0x6b, 0x42, 0x9e, 0x47, 0x17, 0xc6, 0x50, 0x3d, 0x6a, - 0xec, 0x2b, 0x47, 0x5a, 0xb7, 0x37, 0x68, 0x77, 0x3b, 0x8d, 0x23, 0x24, 0xcd, 0x30, 0x55, 0xf9, - 0xf9, 0x49, 0x5b, 0x55, 0x5a, 0x28, 0x93, 0xc4, 0x7a, 0x4a, 0x63, 0xa0, 0xb4, 0x50, 0xb6, 0xa6, - 0xc3, 0xe6, 0xb2, 0x3c, 0xb9, 0x74, 0x67, 0x24, 0x96, 0x38, 0x73, 0xce, 0x12, 0x73, 0x5d, 0x0b, - 0x4b, 0xfc, 0xcf, 0x0c, 0x6c, 0x2c, 0x39, 0x2b, 0x96, 0x0e, 0xf2, 0x13, 0xc8, 0x8b, 0x10, 0x15, - 0xa7, 0xe7, 0x8d, 0xa5, 0x87, 0x0e, 0x0f, 0xd8, 0x85, 0x13, 0x94, 0xcb, 0x25, 0x2b, 0x88, 0xec, - 0x39, 0x15, 0x04, 0x53, 0xb1, 0x90, 0xd3, 0x7f, 0xb9, 0x90, 0xd3, 0xc5, 0xb1, 0x77, 0xf7, 0x22, - 0xc7, 0x1e, 0xc7, 0xbe, 0x5b, 0x6e, 0xcf, 0x2f, 0xc9, 0xed, 0xf7, 0x61, 0x7d, 0x41, 0xd1, 0x85, - 0x73, 0xec, 0xaf, 0x25, 0x90, 0xcf, 0x73, 0xce, 0x73, 0x32, 0x5d, 0x26, 0x95, 0xe9, 0xee, 0xcf, - 0x7b, 0xf0, 0xda, 0xf9, 0x8b, 0xb0, 0xb0, 0xd6, 0x5f, 0x49, 0x70, 0x69, 0x79, 0xa5, 0xb8, 0xd4, - 0x86, 0x8f, 0xa0, 0x30, 0xa1, 0xc1, 0xd8, 0x89, 0xaa, 0xa5, 0xb7, 0x97, 0x9c, 0xc1, 0xac, 0x7b, - 0x7e, 0xb1, 0x43, 0xa9, 0xe4, 0x21, 0x9e, 0x3d, 0xaf, 0xdc, 0x13, 0xd6, 0x2c, 0x58, 0xfa, 0x9b, - 0x0c, 0xbc, 0xbc, 0x54, 0xf9, 0x52, 0x43, 0x5f, 0x03, 0x30, 0x6d, 0x77, 0x1a, 0x88, 0x8a, 0x48, - 0x24, 0xd8, 0x12, 0x47, 0x78, 0xf2, 0x62, 0xc9, 0x73, 0x1a, 0xc4, 0xfd, 0x59, 0xde, 0x0f, 0x02, - 0xe2, 0x84, 0x7b, 0x33, 0x43, 0x73, 0xdc, 0xd0, 0xd7, 0xcf, 0x99, 0xe9, 0x42, 0x60, 0xbe, 0x07, - 0x48, 0xb7, 0x4c, 0x6a, 0x07, 0x9a, 0x1f, 0x78, 0x94, 0x4c, 0x4c, 0x7b, 0xc4, 0x4f, 0x90, 0xe2, - 0x5e, 0x7e, 0x48, 0x2c, 0x9f, 0xaa, 0x6b, 0xa2, 0xbb, 0x1f, 0xf5, 0x32, 0x09, 0x1e, 0x40, 0x5e, - 0x42, 0xa2, 0x90, 0x92, 0x10, 0xdd, 0xb1, 0x44, 0xed, 0xeb, 0x22, 0x94, 0x13, 0x75, 0x35, 0xbe, - 0x06, 0x95, 0x87, 0xe4, 0x31, 0xd1, 0xa2, 0xbb, 0x92, 0xf0, 0x44, 0x99, 0x61, 0xbd, 0xf0, 0xbe, - 0xf4, 0x1e, 0x6c, 0x72, 0x8a, 0x33, 0x0d, 0xa8, 0xa7, 0xe9, 0x16, 0xf1, 0x7d, 0xee, 0xb4, 0x22, - 0xa7, 0x62, 0xd6, 0xd7, 0x65, 0x5d, 0xcd, 0xa8, 0x07, 0xdf, 0x81, 0x0d, 0x2e, 0x31, 0x99, 0x5a, - 0x81, 0xe9, 0x5a, 0x54, 0x63, 0xb7, 0x37, 0x9f, 0x9f, 0x24, 0xb1, 0x65, 0xeb, 0x8c, 0x71, 0x1c, - 0x12, 0x98, 0x45, 0x3e, 0x6e, 0xc1, 0x6b, 0x5c, 0x6c, 0x44, 0x6d, 0xea, 0x91, 0x80, 0x6a, 0xf4, - 0x8b, 0x29, 0xb1, 0x7c, 0x8d, 0xd8, 0x86, 0x36, 0x26, 0xfe, 0x58, 0xde, 0x64, 0x0a, 0xf6, 0x33, - 0xb2, 0xa4, 0x5e, 0x61, 0xc4, 0x83, 0x90, 0xa7, 0x70, 0x5a, 0xc3, 0x36, 0x3e, 0x26, 0xfe, 0x18, - 0xef, 0xc1, 0x25, 0xae, 0xc5, 0x0f, 0x3c, 0xd3, 0x1e, 0x69, 0xfa, 0x98, 0xea, 0x8f, 0xb4, 0x69, - 0x30, 0xbc, 0x27, 0xbf, 0x92, 0x1c, 0x9f, 0x5b, 0xd8, 0xe7, 0x9c, 0x26, 0xa3, 0x9c, 0x04, 0xc3, - 0x7b, 0xb8, 0x0f, 0x15, 0xb6, 0x18, 0x13, 0xf3, 0x4b, 0xaa, 0x0d, 0x1d, 0x8f, 0x1f, 0x8d, 0xd5, - 0x25, 0xa9, 0x29, 0xe1, 0xc1, 0x7a, 0x37, 0x14, 0x38, 0x76, 0x0c, 0xba, 0x97, 0xef, 0xf7, 0x14, - 0xa5, 0xa5, 0x96, 0x23, 0x2d, 0x0f, 0x1c, 0x8f, 0x05, 0xd4, 0xc8, 0x89, 0x1d, 0x5c, 0x16, 0x01, - 0x35, 0x72, 0x22, 0xf7, 0xde, 0x81, 0x0d, 0x5d, 0x17, 0x73, 0x36, 0x75, 0x2d, 0xbc, 0x63, 0xf9, - 0x32, 0x4a, 0x39, 0x4b, 0xd7, 0x0f, 0x04, 0x21, 0x8c, 0x71, 0x1f, 0x7f, 0x08, 0x2f, 0xcf, 0x9c, - 0x95, 0x14, 0x5c, 0x5f, 0x98, 0xe5, 0xbc, 0xe8, 0x1d, 0xd8, 0x70, 0xcf, 0x16, 0x05, 0x71, 0x6a, - 0x44, 0xf7, 0x6c, 0x5e, 0xec, 0x03, 0xd8, 0x74, 0xc7, 0xee, 0xa2, 0xdc, 0xcd, 0xa4, 0x1c, 0x76, - 0xc7, 0xee, 0xbc, 0xe0, 0x5b, 0xfc, 0xc2, 0xed, 0x51, 0x9d, 0x04, 0xd4, 0x90, 0x2f, 0x27, 0xe9, - 0x89, 0x0e, 0xbc, 0x0b, 0x48, 0xd7, 0x35, 0x6a, 0x93, 0x53, 0x8b, 0x6a, 0xc4, 0xa3, 0x36, 0xf1, - 0xe5, 0xab, 0x49, 0x72, 0x55, 0xd7, 0x15, 0xde, 0xdb, 0xe0, 0x9d, 0xf8, 0x26, 0xac, 0x3b, 0xa7, - 0x0f, 0x75, 0x11, 0x92, 0x9a, 0xeb, 0xd1, 0xa1, 0xf9, 0x54, 0x7e, 0x93, 0xfb, 0x77, 0x8d, 0x75, - 0xf0, 0x80, 0xec, 0x71, 0x18, 0xdf, 0x00, 0xa4, 0xfb, 0x63, 0xe2, 0xb9, 0x3c, 0x27, 0xfb, 0x2e, - 0xd1, 0xa9, 0xfc, 0x96, 0xa0, 0x0a, 0xbc, 0x13, 0xc1, 0x6c, 0x4b, 0xf8, 0x4f, 0xcc, 0x61, 0x10, - 0x69, 0xbc, 0x2e, 0xb6, 0x04, 0xc7, 0x42, 0x6d, 0x3b, 0x80, 0x98, 0x2b, 0x52, 0x03, 0xef, 0x70, - 0x5a, 0xd5, 0x1d, 0xbb, 0xc9, 0x71, 0xdf, 0x80, 0x55, 0xc6, 0x9c, 0x0d, 0x7a, 0x43, 0x14, 0x64, - 0xee, 0x38, 0x31, 0xe2, 0x0f, 0x56, 0x1b, 0xd7, 0xf6, 0xa0, 0x92, 0x8c, 0x4f, 0x5c, 0x02, 0x11, - 0xa1, 0x48, 0x62, 0xc5, 0x4a, 0xb3, 0xdb, 0x62, 0x65, 0xc6, 0xe7, 0x0a, 0xca, 0xb0, 0x72, 0xe7, - 0xa8, 0x3d, 0x50, 0x34, 0xf5, 0xa4, 0x33, 0x68, 0x1f, 0x2b, 0x28, 0x9b, 0xa8, 0xab, 0x0f, 0x73, - 0xc5, 0xb7, 0xd1, 0xf5, 0xda, 0x37, 0x19, 0xa8, 0xa6, 0x2f, 0x4a, 0xf8, 0xff, 0xe1, 0x72, 0xf4, - 0xaa, 0xe1, 0xd3, 0x40, 0x7b, 0x62, 0x7a, 0x7c, 0xe3, 0x4c, 0x88, 0x38, 0xc4, 0xe2, 0xa5, 0xdb, - 0x0c, 0x59, 0x7d, 0x1a, 0x7c, 0x62, 0x7a, 0x6c, 0x5b, 0x4c, 0x48, 0x80, 0x8f, 0xe0, 0xaa, 0xed, - 0x68, 0x7e, 0x40, 0x6c, 0x83, 0x78, 0x86, 0x36, 0x7b, 0x4f, 0xd2, 0x88, 0xae, 0x53, 0xdf, 0x77, - 0xc4, 0x81, 0x15, 0x6b, 0x79, 0xd5, 0x76, 0xfa, 0x21, 0x79, 0x96, 0xc9, 0x1b, 0x21, 0x75, 0x2e, - 0xcc, 0xb2, 0xe7, 0x85, 0xd9, 0x2b, 0x50, 0x9a, 0x10, 0x57, 0xa3, 0x76, 0xe0, 0x9d, 0xf1, 0xf2, - 0xb8, 0xa8, 0x16, 0x27, 0xc4, 0x55, 0x58, 0xfb, 0x85, 0xdc, 0x52, 0x0e, 0x73, 0xc5, 0x22, 0x2a, - 0x1d, 0xe6, 0x8a, 0x25, 0x04, 0xb5, 0x7f, 0x64, 0xa1, 0x92, 0x2c, 0x97, 0xd9, 0xed, 0x43, 0xe7, - 0x27, 0x8b, 0xc4, 0x73, 0xcf, 0x1b, 0xdf, 0x5a, 0x5c, 0xd7, 0x9b, 0xec, 0xc8, 0xd9, 0x2b, 0x88, - 0x22, 0x56, 0x15, 0x92, 0xec, 0xb8, 0x67, 0xd9, 0x86, 0x8a, 0xa2, 0xa1, 0xa8, 0x86, 0x2d, 0x7c, - 0x00, 0x85, 0x87, 0x3e, 0xd7, 0x5d, 0xe0, 0xba, 0xdf, 0xfc, 0x76, 0xdd, 0x87, 0x7d, 0xae, 0xbc, - 0x74, 0xd8, 0xd7, 0x3a, 0x5d, 0xf5, 0xb8, 0x71, 0xa4, 0x86, 0xe2, 0xf8, 0x0a, 0xe4, 0x2c, 0xf2, - 0xe5, 0x59, 0xfa, 0x70, 0xe2, 0xd0, 0x45, 0x17, 0xe1, 0x0a, 0xe4, 0x9e, 0x50, 0xf2, 0x28, 0x7d, - 0x24, 0x70, 0xe8, 0x07, 0xdc, 0x0c, 0xbb, 0x90, 0xe7, 0xfe, 0xc2, 0x00, 0xa1, 0xc7, 0xd0, 0x4b, - 0xb8, 0x08, 0xb9, 0x66, 0x57, 0x65, 0x1b, 0x02, 0x41, 0x45, 0xa0, 0x5a, 0xaf, 0xad, 0x34, 0x15, - 0x94, 0xa9, 0xdd, 0x81, 0x82, 0x70, 0x02, 0xdb, 0x2c, 0xb1, 0x1b, 0xd0, 0x4b, 0x61, 0x33, 0xd4, - 0x21, 0x45, 0xbd, 0x27, 0xc7, 0xfb, 0x8a, 0x8a, 0x32, 0xe9, 0xa5, 0xce, 0xa1, 0x7c, 0xcd, 0x87, - 0x4a, 0xb2, 0x5e, 0x7e, 0x31, 0x77, 0xe1, 0xbf, 0x4a, 0x50, 0x4e, 0xd4, 0xbf, 0xac, 0x70, 0x21, - 0x96, 0xe5, 0x3c, 0xd1, 0x88, 0x65, 0x12, 0x3f, 0x0c, 0x0d, 0xe0, 0x50, 0x83, 0x21, 0x17, 0x5d, - 0xba, 0x17, 0xb4, 0x45, 0xf2, 0xa8, 0x50, 0xfb, 0xa3, 0x04, 0x68, 0xbe, 0x00, 0x9d, 0x33, 0x53, - 0xfa, 0x31, 0xcd, 0xac, 0xfd, 0x41, 0x82, 0x6a, 0xba, 0xea, 0x9c, 0x33, 0xef, 0xda, 0x8f, 0x6a, - 0xde, 0xdf, 0x33, 0xb0, 0x9a, 0xaa, 0x35, 0x2f, 0x6a, 0xdd, 0x17, 0xb0, 0x6e, 0x1a, 0x74, 0xe2, - 0x3a, 0x01, 0xb5, 0xf5, 0x33, 0xcd, 0xa2, 0x8f, 0xa9, 0x25, 0xd7, 0x78, 0xd2, 0xd8, 0xfd, 0xf6, - 0x6a, 0xb6, 0xde, 0x9e, 0xc9, 0x1d, 0x31, 0xb1, 0xbd, 0x8d, 0x76, 0x4b, 0x39, 0xee, 0x75, 0x07, - 0x4a, 0xa7, 0xf9, 0x99, 0x76, 0xd2, 0xf9, 0x59, 0xa7, 0xfb, 0x49, 0x47, 0x45, 0xe6, 0x1c, 0xed, - 0x07, 0xdc, 0xf6, 0x3d, 0x40, 0xf3, 0x46, 0xe1, 0xcb, 0xb0, 0xcc, 0x2c, 0xf4, 0x12, 0xde, 0x80, - 0xb5, 0x4e, 0x57, 0xeb, 0xb7, 0x5b, 0x8a, 0xa6, 0x3c, 0x78, 0xa0, 0x34, 0x07, 0x7d, 0xf1, 0x3e, - 0x11, 0xb3, 0x07, 0xa9, 0x0d, 0x5e, 0xfb, 0x7d, 0x16, 0x36, 0x96, 0x58, 0x82, 0x1b, 0xe1, 0xcd, - 0x42, 0x5c, 0x76, 0xde, 0xbd, 0x88, 0xf5, 0x75, 0x56, 0x10, 0xf4, 0x88, 0x17, 0x84, 0x17, 0x91, - 0x1b, 0xc0, 0xbc, 0x64, 0x07, 0xe6, 0xd0, 0xa4, 0x5e, 0xf8, 0x9c, 0x23, 0xae, 0x1b, 0x6b, 0x33, - 0x5c, 0xbc, 0xe8, 0xfc, 0x1f, 0x60, 0xd7, 0xf1, 0xcd, 0xc0, 0x7c, 0x4c, 0x35, 0xd3, 0x8e, 0xde, - 0x7e, 0xd8, 0xf5, 0x23, 0xa7, 0xa2, 0xa8, 0xa7, 0x6d, 0x07, 0x31, 0xdb, 0xa6, 0x23, 0x32, 0xc7, - 0x66, 0xc9, 0x3c, 0xab, 0xa2, 0xa8, 0x27, 0x66, 0x5f, 0x83, 0x8a, 0xe1, 0x4c, 0x59, 0x4d, 0x26, - 0x78, 0xec, 0xec, 0x90, 0xd4, 0xb2, 0xc0, 0x62, 0x4a, 0x58, 0x6d, 0xcf, 0x1e, 0x9d, 0x2a, 0x6a, - 0x59, 0x60, 0x82, 0x72, 0x1d, 0xd6, 0xc8, 0x68, 0xe4, 0x31, 0xe5, 0x91, 0x22, 0x71, 0x7f, 0xa8, - 0xc6, 0x30, 0x27, 0x6e, 0x1d, 0x42, 0x31, 0xf2, 0x03, 0x3b, 0xaa, 0x99, 0x27, 0x34, 0x57, 0x5c, - 0x8a, 0x33, 0x3b, 0x25, 0xb5, 0x68, 0x47, 0x9d, 0xd7, 0xa0, 0x62, 0xfa, 0xda, 0xec, 0x0d, 0x3d, - 0xb3, 0x9d, 0xd9, 0x29, 0xaa, 0x65, 0xd3, 0x8f, 0xdf, 0x1f, 0x6b, 0x5f, 0x65, 0xa0, 0x9a, 0xfe, - 0x06, 0x80, 0x5b, 0x50, 0xb4, 0x1c, 0x9d, 0xf0, 0xd0, 0x12, 0x1f, 0xa0, 0x76, 0x9e, 0xf3, 0xd9, - 0xa0, 0x7e, 0x14, 0xf2, 0xd5, 0x58, 0x72, 0xeb, 0x6f, 0x12, 0x14, 0x23, 0x18, 0x5f, 0x82, 0x9c, - 0x4b, 0x82, 0x31, 0x57, 0x97, 0xdf, 0xcf, 0x20, 0x49, 0xe5, 0x6d, 0x86, 0xfb, 0x2e, 0xb1, 0x79, - 0x08, 0x84, 0x38, 0x6b, 0xb3, 0x75, 0xb5, 0x28, 0x31, 0xf8, 0xe5, 0xc4, 0x99, 0x4c, 0xa8, 0x1d, - 0xf8, 0xd1, 0xba, 0x86, 0x78, 0x33, 0x84, 0xf1, 0x3b, 0xb0, 0x1e, 0x78, 0xc4, 0xb4, 0x52, 0xdc, - 0x1c, 0xe7, 0xa2, 0xa8, 0x23, 0x26, 0xef, 0xc1, 0x95, 0x48, 0xaf, 0x41, 0x03, 0xa2, 0x8f, 0xa9, - 0x31, 0x13, 0x2a, 0xf0, 0x47, 0x88, 0xcb, 0x21, 0xa1, 0x15, 0xf6, 0x47, 0xb2, 0xb5, 0x6f, 0x24, - 0x58, 0x8f, 0xae, 0x53, 0x46, 0xec, 0xac, 0x63, 0x00, 0x62, 0xdb, 0x4e, 0x90, 0x74, 0xd7, 0x62, - 0x28, 0x2f, 0xc8, 0xd5, 0x1b, 0xb1, 0x90, 0x9a, 0x50, 0xb0, 0x35, 0x01, 0x98, 0xf5, 0x9c, 0xeb, - 0xb6, 0xab, 0x50, 0x0e, 0x3f, 0xf0, 0xf0, 0xaf, 0x84, 0xe2, 0x02, 0x0e, 0x02, 0x62, 0xf7, 0x2e, - 0xbc, 0x09, 0xf9, 0x53, 0x3a, 0x32, 0xed, 0xf0, 0xd9, 0x56, 0x34, 0xa2, 0x67, 0x92, 0x5c, 0xfc, - 0x4c, 0xb2, 0xff, 0x5b, 0x09, 0x36, 0x74, 0x67, 0x32, 0x6f, 0xef, 0x3e, 0x9a, 0x7b, 0x05, 0xf0, - 0x3f, 0x96, 0x3e, 0xff, 0x68, 0x64, 0x06, 0xe3, 0xe9, 0x69, 0x5d, 0x77, 0x26, 0xbb, 0x23, 0xc7, - 0x22, 0xf6, 0x68, 0xf6, 0x99, 0x93, 0xff, 0xd1, 0xdf, 0x1d, 0x51, 0xfb, 0xdd, 0x91, 0x93, 0xf8, - 0xe8, 0x79, 0x7f, 0xf6, 0xf7, 0xbf, 0x92, 0xf4, 0xa7, 0x4c, 0xf6, 0xa0, 0xb7, 0xff, 0xe7, 0xcc, - 0xd6, 0x81, 0x18, 0xae, 0x17, 0xb9, 0x47, 0xa5, 0x43, 0x8b, 0xea, 0x6c, 0xca, 0xff, 0x0b, 0x00, - 0x00, 0xff, 0xff, 0x1a, 0x28, 0x25, 0x79, 0x42, 0x1d, 0x00, 0x00, -} diff --git a/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go b/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go deleted file mode 100644 index a69b403ce15..00000000000 --- a/vendor/github.com/golang/protobuf/ptypes/empty/empty.pb.go +++ /dev/null @@ -1,79 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: google/protobuf/empty.proto - -package empty // import "github.com/golang/protobuf/ptypes/empty" - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -// A generic empty message that you can re-use to avoid defining duplicated -// empty messages in your APIs. A typical example is to use it as the request -// or the response type of an API method. For instance: -// -// service Foo { -// rpc Bar(google.protobuf.Empty) returns (google.protobuf.Empty); -// } -// -// The JSON representation for `Empty` is empty JSON object `{}`. -type Empty struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Empty) Reset() { *m = Empty{} } -func (m *Empty) String() string { return proto.CompactTextString(m) } -func (*Empty) ProtoMessage() {} -func (*Empty) Descriptor() ([]byte, []int) { - return fileDescriptor_empty_39e6d6db0632e5b2, []int{0} -} -func (*Empty) XXX_WellKnownType() string { return "Empty" } -func (m *Empty) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Empty.Unmarshal(m, b) -} -func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Empty.Marshal(b, m, deterministic) -} -func (dst *Empty) XXX_Merge(src proto.Message) { - xxx_messageInfo_Empty.Merge(dst, src) -} -func (m *Empty) XXX_Size() int { - return xxx_messageInfo_Empty.Size(m) -} -func (m *Empty) XXX_DiscardUnknown() { - xxx_messageInfo_Empty.DiscardUnknown(m) -} - -var xxx_messageInfo_Empty proto.InternalMessageInfo - -func init() { - proto.RegisterType((*Empty)(nil), "google.protobuf.Empty") -} - -func init() { proto.RegisterFile("google/protobuf/empty.proto", fileDescriptor_empty_39e6d6db0632e5b2) } - -var fileDescriptor_empty_39e6d6db0632e5b2 = []byte{ - // 148 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4e, 0xcf, 0xcf, 0x4f, - 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcd, 0x2d, 0x28, - 0xa9, 0xd4, 0x03, 0x73, 0x85, 0xf8, 0x21, 0x92, 0x7a, 0x30, 0x49, 0x25, 0x76, 0x2e, 0x56, 0x57, - 0x90, 0xbc, 0x53, 0x19, 0x97, 0x70, 0x72, 0x7e, 0xae, 0x1e, 0x9a, 0xbc, 0x13, 0x17, 0x58, 0x36, - 0x00, 0xc4, 0x0d, 0x60, 0x8c, 0x52, 0x4f, 0xcf, 0x2c, 0xc9, 0x28, 0x4d, 0xd2, 0x4b, 0xce, 0xcf, - 0xd5, 0x4f, 0xcf, 0xcf, 0x49, 0xcc, 0x4b, 0x47, 0x58, 0x53, 0x50, 0x52, 0x59, 0x90, 0x5a, 0x0c, - 0xb1, 0xed, 0x07, 0x23, 0xe3, 0x22, 0x26, 0x66, 0xf7, 0x00, 0xa7, 0x55, 0x4c, 0x72, 0xee, 0x10, - 0x13, 0x03, 0xa0, 0xea, 0xf4, 0xc2, 0x53, 0x73, 0x72, 0xbc, 0xf3, 0xf2, 0xcb, 0xf3, 0x42, 0x40, - 0xea, 0x93, 0xd8, 0xc0, 0x06, 0x18, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0x64, 0xd4, 0xb3, 0xa6, - 0xb7, 0x00, 0x00, 0x00, -} diff --git a/vendor/github.com/golang/snappy/AUTHORS b/vendor/github.com/golang/snappy/AUTHORS deleted file mode 100644 index bcfa19520af..00000000000 --- a/vendor/github.com/golang/snappy/AUTHORS +++ /dev/null @@ -1,15 +0,0 @@ -# This is the official list of Snappy-Go authors for copyright purposes. -# This file is distinct from the CONTRIBUTORS files. -# See the latter for an explanation. - -# Names should be added to this file as -# Name or Organization -# The email address is not required for organizations. - -# Please keep the list sorted. - -Damian Gryski -Google Inc. -Jan Mercl <0xjnml@gmail.com> -Rodolfo Carvalho -Sebastien Binet diff --git a/vendor/github.com/golang/snappy/CONTRIBUTORS b/vendor/github.com/golang/snappy/CONTRIBUTORS deleted file mode 100644 index 931ae31606f..00000000000 --- a/vendor/github.com/golang/snappy/CONTRIBUTORS +++ /dev/null @@ -1,37 +0,0 @@ -# This is the official list of people who can contribute -# (and typically have contributed) code to the Snappy-Go repository. -# The AUTHORS file lists the copyright holders; this file -# lists people. For example, Google employees are listed here -# but not in AUTHORS, because Google holds the copyright. -# -# The submission process automatically checks to make sure -# that people submitting code are listed in this file (by email address). -# -# Names should be added to this file only after verifying that -# the individual or the individual's organization has agreed to -# the appropriate Contributor License Agreement, found here: -# -# http://code.google.com/legal/individual-cla-v1.0.html -# http://code.google.com/legal/corporate-cla-v1.0.html -# -# The agreement for individuals can be filled out on the web. -# -# When adding J Random Contributor's name to this file, -# either J's name or J's organization's name should be -# added to the AUTHORS file, depending on whether the -# individual or corporate CLA was used. - -# Names should be added to this file like so: -# Name - -# Please keep the list sorted. - -Damian Gryski -Jan Mercl <0xjnml@gmail.com> -Kai Backman -Marc-Antoine Ruel -Nigel Tao -Rob Pike -Rodolfo Carvalho -Russ Cox -Sebastien Binet diff --git a/vendor/github.com/golang/snappy/LICENSE b/vendor/github.com/golang/snappy/LICENSE deleted file mode 100644 index 6050c10f4c8..00000000000 --- a/vendor/github.com/golang/snappy/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/golang/snappy/decode.go b/vendor/github.com/golang/snappy/decode.go deleted file mode 100644 index 72efb0353dd..00000000000 --- a/vendor/github.com/golang/snappy/decode.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2011 The Snappy-Go 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 snappy - -import ( - "encoding/binary" - "errors" - "io" -) - -var ( - // ErrCorrupt reports that the input is invalid. - ErrCorrupt = errors.New("snappy: corrupt input") - // ErrTooLarge reports that the uncompressed length is too large. - ErrTooLarge = errors.New("snappy: decoded block is too large") - // ErrUnsupported reports that the input isn't supported. - ErrUnsupported = errors.New("snappy: unsupported input") - - errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length") -) - -// DecodedLen returns the length of the decoded block. -func DecodedLen(src []byte) (int, error) { - v, _, err := decodedLen(src) - return v, err -} - -// decodedLen returns the length of the decoded block and the number of bytes -// that the length header occupied. -func decodedLen(src []byte) (blockLen, headerLen int, err error) { - v, n := binary.Uvarint(src) - if n <= 0 || v > 0xffffffff { - return 0, 0, ErrCorrupt - } - - const wordSize = 32 << (^uint(0) >> 32 & 1) - if wordSize == 32 && v > 0x7fffffff { - return 0, 0, ErrTooLarge - } - return int(v), n, nil -} - -const ( - decodeErrCodeCorrupt = 1 - decodeErrCodeUnsupportedLiteralLength = 2 -) - -// Decode returns the decoded form of src. The returned slice may be a sub- -// slice of dst if dst was large enough to hold the entire decoded block. -// Otherwise, a newly allocated slice will be returned. -// -// The dst and src must not overlap. It is valid to pass a nil dst. -func Decode(dst, src []byte) ([]byte, error) { - dLen, s, err := decodedLen(src) - if err != nil { - return nil, err - } - if dLen <= len(dst) { - dst = dst[:dLen] - } else { - dst = make([]byte, dLen) - } - switch decode(dst, src[s:]) { - case 0: - return dst, nil - case decodeErrCodeUnsupportedLiteralLength: - return nil, errUnsupportedLiteralLength - } - return nil, ErrCorrupt -} - -// NewReader returns a new Reader that decompresses from r, using the framing -// format described at -// https://github.com/google/snappy/blob/master/framing_format.txt -func NewReader(r io.Reader) *Reader { - return &Reader{ - r: r, - decoded: make([]byte, maxBlockSize), - buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize), - } -} - -// Reader is an io.Reader that can read Snappy-compressed bytes. -type Reader struct { - r io.Reader - err error - decoded []byte - buf []byte - // decoded[i:j] contains decoded bytes that have not yet been passed on. - i, j int - readHeader bool -} - -// Reset discards any buffered data, resets all state, and switches the Snappy -// reader to read from r. This permits reusing a Reader rather than allocating -// a new one. -func (r *Reader) Reset(reader io.Reader) { - r.r = reader - r.err = nil - r.i = 0 - r.j = 0 - r.readHeader = false -} - -func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) { - if _, r.err = io.ReadFull(r.r, p); r.err != nil { - if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { - r.err = ErrCorrupt - } - return false - } - return true -} - -// Read satisfies the io.Reader interface. -func (r *Reader) Read(p []byte) (int, error) { - if r.err != nil { - return 0, r.err - } - for { - if r.i < r.j { - n := copy(p, r.decoded[r.i:r.j]) - r.i += n - return n, nil - } - if !r.readFull(r.buf[:4], true) { - return 0, r.err - } - chunkType := r.buf[0] - if !r.readHeader { - if chunkType != chunkTypeStreamIdentifier { - r.err = ErrCorrupt - return 0, r.err - } - r.readHeader = true - } - chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 - if chunkLen > len(r.buf) { - r.err = ErrUnsupported - return 0, r.err - } - - // The chunk types are specified at - // https://github.com/google/snappy/blob/master/framing_format.txt - switch chunkType { - case chunkTypeCompressedData: - // Section 4.2. Compressed data (chunk type 0x00). - if chunkLen < checksumSize { - r.err = ErrCorrupt - return 0, r.err - } - buf := r.buf[:chunkLen] - if !r.readFull(buf, false) { - return 0, r.err - } - checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 - buf = buf[checksumSize:] - - n, err := DecodedLen(buf) - if err != nil { - r.err = err - return 0, r.err - } - if n > len(r.decoded) { - r.err = ErrCorrupt - return 0, r.err - } - if _, err := Decode(r.decoded, buf); err != nil { - r.err = err - return 0, r.err - } - if crc(r.decoded[:n]) != checksum { - r.err = ErrCorrupt - return 0, r.err - } - r.i, r.j = 0, n - continue - - case chunkTypeUncompressedData: - // Section 4.3. Uncompressed data (chunk type 0x01). - if chunkLen < checksumSize { - r.err = ErrCorrupt - return 0, r.err - } - buf := r.buf[:checksumSize] - if !r.readFull(buf, false) { - return 0, r.err - } - checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 - // Read directly into r.decoded instead of via r.buf. - n := chunkLen - checksumSize - if n > len(r.decoded) { - r.err = ErrCorrupt - return 0, r.err - } - if !r.readFull(r.decoded[:n], false) { - return 0, r.err - } - if crc(r.decoded[:n]) != checksum { - r.err = ErrCorrupt - return 0, r.err - } - r.i, r.j = 0, n - continue - - case chunkTypeStreamIdentifier: - // Section 4.1. Stream identifier (chunk type 0xff). - if chunkLen != len(magicBody) { - r.err = ErrCorrupt - return 0, r.err - } - if !r.readFull(r.buf[:len(magicBody)], false) { - return 0, r.err - } - for i := 0; i < len(magicBody); i++ { - if r.buf[i] != magicBody[i] { - r.err = ErrCorrupt - return 0, r.err - } - } - continue - } - - if chunkType <= 0x7f { - // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). - r.err = ErrUnsupported - return 0, r.err - } - // Section 4.4 Padding (chunk type 0xfe). - // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). - if !r.readFull(r.buf[:chunkLen], false) { - return 0, r.err - } - } -} diff --git a/vendor/github.com/golang/snappy/decode_amd64.go b/vendor/github.com/golang/snappy/decode_amd64.go deleted file mode 100644 index fcd192b849e..00000000000 --- a/vendor/github.com/golang/snappy/decode_amd64.go +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2016 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine -// +build gc -// +build !noasm - -package snappy - -// decode has the same semantics as in decode_other.go. -// -//go:noescape -func decode(dst, src []byte) int diff --git a/vendor/github.com/golang/snappy/decode_amd64.s b/vendor/github.com/golang/snappy/decode_amd64.s deleted file mode 100644 index e6179f65e35..00000000000 --- a/vendor/github.com/golang/snappy/decode_amd64.s +++ /dev/null @@ -1,490 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine -// +build gc -// +build !noasm - -#include "textflag.h" - -// The asm code generally follows the pure Go code in decode_other.go, except -// where marked with a "!!!". - -// func decode(dst, src []byte) int -// -// All local variables fit into registers. The non-zero stack size is only to -// spill registers and push args when issuing a CALL. The register allocation: -// - AX scratch -// - BX scratch -// - CX length or x -// - DX offset -// - SI &src[s] -// - DI &dst[d] -// + R8 dst_base -// + R9 dst_len -// + R10 dst_base + dst_len -// + R11 src_base -// + R12 src_len -// + R13 src_base + src_len -// - R14 used by doCopy -// - R15 used by doCopy -// -// The registers R8-R13 (marked with a "+") are set at the start of the -// function, and after a CALL returns, and are not otherwise modified. -// -// The d variable is implicitly DI - R8, and len(dst)-d is R10 - DI. -// The s variable is implicitly SI - R11, and len(src)-s is R13 - SI. -TEXT ·decode(SB), NOSPLIT, $48-56 - // Initialize SI, DI and R8-R13. - MOVQ dst_base+0(FP), R8 - MOVQ dst_len+8(FP), R9 - MOVQ R8, DI - MOVQ R8, R10 - ADDQ R9, R10 - MOVQ src_base+24(FP), R11 - MOVQ src_len+32(FP), R12 - MOVQ R11, SI - MOVQ R11, R13 - ADDQ R12, R13 - -loop: - // for s < len(src) - CMPQ SI, R13 - JEQ end - - // CX = uint32(src[s]) - // - // switch src[s] & 0x03 - MOVBLZX (SI), CX - MOVL CX, BX - ANDL $3, BX - CMPL BX, $1 - JAE tagCopy - - // ---------------------------------------- - // The code below handles literal tags. - - // case tagLiteral: - // x := uint32(src[s] >> 2) - // switch - SHRL $2, CX - CMPL CX, $60 - JAE tagLit60Plus - - // case x < 60: - // s++ - INCQ SI - -doLit: - // This is the end of the inner "switch", when we have a literal tag. - // - // We assume that CX == x and x fits in a uint32, where x is the variable - // used in the pure Go decode_other.go code. - - // length = int(x) + 1 - // - // Unlike the pure Go code, we don't need to check if length <= 0 because - // CX can hold 64 bits, so the increment cannot overflow. - INCQ CX - - // Prepare to check if copying length bytes will run past the end of dst or - // src. - // - // AX = len(dst) - d - // BX = len(src) - s - MOVQ R10, AX - SUBQ DI, AX - MOVQ R13, BX - SUBQ SI, BX - - // !!! Try a faster technique for short (16 or fewer bytes) copies. - // - // if length > 16 || len(dst)-d < 16 || len(src)-s < 16 { - // goto callMemmove // Fall back on calling runtime·memmove. - // } - // - // The C++ snappy code calls this TryFastAppend. It also checks len(src)-s - // against 21 instead of 16, because it cannot assume that all of its input - // is contiguous in memory and so it needs to leave enough source bytes to - // read the next tag without refilling buffers, but Go's Decode assumes - // contiguousness (the src argument is a []byte). - CMPQ CX, $16 - JGT callMemmove - CMPQ AX, $16 - JLT callMemmove - CMPQ BX, $16 - JLT callMemmove - - // !!! Implement the copy from src to dst as a 16-byte load and store. - // (Decode's documentation says that dst and src must not overlap.) - // - // This always copies 16 bytes, instead of only length bytes, but that's - // OK. If the input is a valid Snappy encoding then subsequent iterations - // will fix up the overrun. Otherwise, Decode returns a nil []byte (and a - // non-nil error), so the overrun will be ignored. - // - // Note that on amd64, it is legal and cheap to issue unaligned 8-byte or - // 16-byte loads and stores. This technique probably wouldn't be as - // effective on architectures that are fussier about alignment. - MOVOU 0(SI), X0 - MOVOU X0, 0(DI) - - // d += length - // s += length - ADDQ CX, DI - ADDQ CX, SI - JMP loop - -callMemmove: - // if length > len(dst)-d || length > len(src)-s { etc } - CMPQ CX, AX - JGT errCorrupt - CMPQ CX, BX - JGT errCorrupt - - // copy(dst[d:], src[s:s+length]) - // - // This means calling runtime·memmove(&dst[d], &src[s], length), so we push - // DI, SI and CX as arguments. Coincidentally, we also need to spill those - // three registers to the stack, to save local variables across the CALL. - MOVQ DI, 0(SP) - MOVQ SI, 8(SP) - MOVQ CX, 16(SP) - MOVQ DI, 24(SP) - MOVQ SI, 32(SP) - MOVQ CX, 40(SP) - CALL runtime·memmove(SB) - - // Restore local variables: unspill registers from the stack and - // re-calculate R8-R13. - MOVQ 24(SP), DI - MOVQ 32(SP), SI - MOVQ 40(SP), CX - MOVQ dst_base+0(FP), R8 - MOVQ dst_len+8(FP), R9 - MOVQ R8, R10 - ADDQ R9, R10 - MOVQ src_base+24(FP), R11 - MOVQ src_len+32(FP), R12 - MOVQ R11, R13 - ADDQ R12, R13 - - // d += length - // s += length - ADDQ CX, DI - ADDQ CX, SI - JMP loop - -tagLit60Plus: - // !!! This fragment does the - // - // s += x - 58; if uint(s) > uint(len(src)) { etc } - // - // checks. In the asm version, we code it once instead of once per switch case. - ADDQ CX, SI - SUBQ $58, SI - MOVQ SI, BX - SUBQ R11, BX - CMPQ BX, R12 - JA errCorrupt - - // case x == 60: - CMPL CX, $61 - JEQ tagLit61 - JA tagLit62Plus - - // x = uint32(src[s-1]) - MOVBLZX -1(SI), CX - JMP doLit - -tagLit61: - // case x == 61: - // x = uint32(src[s-2]) | uint32(src[s-1])<<8 - MOVWLZX -2(SI), CX - JMP doLit - -tagLit62Plus: - CMPL CX, $62 - JA tagLit63 - - // case x == 62: - // x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 - MOVWLZX -3(SI), CX - MOVBLZX -1(SI), BX - SHLL $16, BX - ORL BX, CX - JMP doLit - -tagLit63: - // case x == 63: - // x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 - MOVL -4(SI), CX - JMP doLit - -// The code above handles literal tags. -// ---------------------------------------- -// The code below handles copy tags. - -tagCopy4: - // case tagCopy4: - // s += 5 - ADDQ $5, SI - - // if uint(s) > uint(len(src)) { etc } - MOVQ SI, BX - SUBQ R11, BX - CMPQ BX, R12 - JA errCorrupt - - // length = 1 + int(src[s-5])>>2 - SHRQ $2, CX - INCQ CX - - // offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) - MOVLQZX -4(SI), DX - JMP doCopy - -tagCopy2: - // case tagCopy2: - // s += 3 - ADDQ $3, SI - - // if uint(s) > uint(len(src)) { etc } - MOVQ SI, BX - SUBQ R11, BX - CMPQ BX, R12 - JA errCorrupt - - // length = 1 + int(src[s-3])>>2 - SHRQ $2, CX - INCQ CX - - // offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) - MOVWQZX -2(SI), DX - JMP doCopy - -tagCopy: - // We have a copy tag. We assume that: - // - BX == src[s] & 0x03 - // - CX == src[s] - CMPQ BX, $2 - JEQ tagCopy2 - JA tagCopy4 - - // case tagCopy1: - // s += 2 - ADDQ $2, SI - - // if uint(s) > uint(len(src)) { etc } - MOVQ SI, BX - SUBQ R11, BX - CMPQ BX, R12 - JA errCorrupt - - // offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) - MOVQ CX, DX - ANDQ $0xe0, DX - SHLQ $3, DX - MOVBQZX -1(SI), BX - ORQ BX, DX - - // length = 4 + int(src[s-2])>>2&0x7 - SHRQ $2, CX - ANDQ $7, CX - ADDQ $4, CX - -doCopy: - // This is the end of the outer "switch", when we have a copy tag. - // - // We assume that: - // - CX == length && CX > 0 - // - DX == offset - - // if offset <= 0 { etc } - CMPQ DX, $0 - JLE errCorrupt - - // if d < offset { etc } - MOVQ DI, BX - SUBQ R8, BX - CMPQ BX, DX - JLT errCorrupt - - // if length > len(dst)-d { etc } - MOVQ R10, BX - SUBQ DI, BX - CMPQ CX, BX - JGT errCorrupt - - // forwardCopy(dst[d:d+length], dst[d-offset:]); d += length - // - // Set: - // - R14 = len(dst)-d - // - R15 = &dst[d-offset] - MOVQ R10, R14 - SUBQ DI, R14 - MOVQ DI, R15 - SUBQ DX, R15 - - // !!! Try a faster technique for short (16 or fewer bytes) forward copies. - // - // First, try using two 8-byte load/stores, similar to the doLit technique - // above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is - // still OK if offset >= 8. Note that this has to be two 8-byte load/stores - // and not one 16-byte load/store, and the first store has to be before the - // second load, due to the overlap if offset is in the range [8, 16). - // - // if length > 16 || offset < 8 || len(dst)-d < 16 { - // goto slowForwardCopy - // } - // copy 16 bytes - // d += length - CMPQ CX, $16 - JGT slowForwardCopy - CMPQ DX, $8 - JLT slowForwardCopy - CMPQ R14, $16 - JLT slowForwardCopy - MOVQ 0(R15), AX - MOVQ AX, 0(DI) - MOVQ 8(R15), BX - MOVQ BX, 8(DI) - ADDQ CX, DI - JMP loop - -slowForwardCopy: - // !!! If the forward copy is longer than 16 bytes, or if offset < 8, we - // can still try 8-byte load stores, provided we can overrun up to 10 extra - // bytes. As above, the overrun will be fixed up by subsequent iterations - // of the outermost loop. - // - // The C++ snappy code calls this technique IncrementalCopyFastPath. Its - // commentary says: - // - // ---- - // - // The main part of this loop is a simple copy of eight bytes at a time - // until we've copied (at least) the requested amount of bytes. However, - // if d and d-offset are less than eight bytes apart (indicating a - // repeating pattern of length < 8), we first need to expand the pattern in - // order to get the correct results. For instance, if the buffer looks like - // this, with the eight-byte and patterns marked as - // intervals: - // - // abxxxxxxxxxxxx - // [------] d-offset - // [------] d - // - // a single eight-byte copy from to will repeat the pattern - // once, after which we can move two bytes without moving : - // - // ababxxxxxxxxxx - // [------] d-offset - // [------] d - // - // and repeat the exercise until the two no longer overlap. - // - // This allows us to do very well in the special case of one single byte - // repeated many times, without taking a big hit for more general cases. - // - // The worst case of extra writing past the end of the match occurs when - // offset == 1 and length == 1; the last copy will read from byte positions - // [0..7] and write to [4..11], whereas it was only supposed to write to - // position 1. Thus, ten excess bytes. - // - // ---- - // - // That "10 byte overrun" worst case is confirmed by Go's - // TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy - // and finishSlowForwardCopy algorithm. - // - // if length > len(dst)-d-10 { - // goto verySlowForwardCopy - // } - SUBQ $10, R14 - CMPQ CX, R14 - JGT verySlowForwardCopy - -makeOffsetAtLeast8: - // !!! As above, expand the pattern so that offset >= 8 and we can use - // 8-byte load/stores. - // - // for offset < 8 { - // copy 8 bytes from dst[d-offset:] to dst[d:] - // length -= offset - // d += offset - // offset += offset - // // The two previous lines together means that d-offset, and therefore - // // R15, is unchanged. - // } - CMPQ DX, $8 - JGE fixUpSlowForwardCopy - MOVQ (R15), BX - MOVQ BX, (DI) - SUBQ DX, CX - ADDQ DX, DI - ADDQ DX, DX - JMP makeOffsetAtLeast8 - -fixUpSlowForwardCopy: - // !!! Add length (which might be negative now) to d (implied by DI being - // &dst[d]) so that d ends up at the right place when we jump back to the - // top of the loop. Before we do that, though, we save DI to AX so that, if - // length is positive, copying the remaining length bytes will write to the - // right place. - MOVQ DI, AX - ADDQ CX, DI - -finishSlowForwardCopy: - // !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative - // length means that we overrun, but as above, that will be fixed up by - // subsequent iterations of the outermost loop. - CMPQ CX, $0 - JLE loop - MOVQ (R15), BX - MOVQ BX, (AX) - ADDQ $8, R15 - ADDQ $8, AX - SUBQ $8, CX - JMP finishSlowForwardCopy - -verySlowForwardCopy: - // verySlowForwardCopy is a simple implementation of forward copy. In C - // parlance, this is a do/while loop instead of a while loop, since we know - // that length > 0. In Go syntax: - // - // for { - // dst[d] = dst[d - offset] - // d++ - // length-- - // if length == 0 { - // break - // } - // } - MOVB (R15), BX - MOVB BX, (DI) - INCQ R15 - INCQ DI - DECQ CX - JNZ verySlowForwardCopy - JMP loop - -// The code above handles copy tags. -// ---------------------------------------- - -end: - // This is the end of the "for s < len(src)". - // - // if d != len(dst) { etc } - CMPQ DI, R10 - JNE errCorrupt - - // return 0 - MOVQ $0, ret+48(FP) - RET - -errCorrupt: - // return decodeErrCodeCorrupt - MOVQ $1, ret+48(FP) - RET diff --git a/vendor/github.com/golang/snappy/decode_other.go b/vendor/github.com/golang/snappy/decode_other.go deleted file mode 100644 index 8c9f2049bc7..00000000000 --- a/vendor/github.com/golang/snappy/decode_other.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2016 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !amd64 appengine !gc noasm - -package snappy - -// decode writes the decoding of src to dst. It assumes that the varint-encoded -// length of the decompressed bytes has already been read, and that len(dst) -// equals that length. -// -// It returns 0 on success or a decodeErrCodeXxx error code on failure. -func decode(dst, src []byte) int { - var d, s, offset, length int - for s < len(src) { - switch src[s] & 0x03 { - case tagLiteral: - x := uint32(src[s] >> 2) - switch { - case x < 60: - s++ - case x == 60: - s += 2 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - x = uint32(src[s-1]) - case x == 61: - s += 3 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - x = uint32(src[s-2]) | uint32(src[s-1])<<8 - case x == 62: - s += 4 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 - case x == 63: - s += 5 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 - } - length = int(x) + 1 - if length <= 0 { - return decodeErrCodeUnsupportedLiteralLength - } - if length > len(dst)-d || length > len(src)-s { - return decodeErrCodeCorrupt - } - copy(dst[d:], src[s:s+length]) - d += length - s += length - continue - - case tagCopy1: - s += 2 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - length = 4 + int(src[s-2])>>2&0x7 - offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) - - case tagCopy2: - s += 3 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - length = 1 + int(src[s-3])>>2 - offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) - - case tagCopy4: - s += 5 - if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. - return decodeErrCodeCorrupt - } - length = 1 + int(src[s-5])>>2 - offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) - } - - if offset <= 0 || d < offset || length > len(dst)-d { - return decodeErrCodeCorrupt - } - // Copy from an earlier sub-slice of dst to a later sub-slice. Unlike - // the built-in copy function, this byte-by-byte copy always runs - // forwards, even if the slices overlap. Conceptually, this is: - // - // d += forwardCopy(dst[d:d+length], dst[d-offset:]) - for end := d + length; d != end; d++ { - dst[d] = dst[d-offset] - } - } - if d != len(dst) { - return decodeErrCodeCorrupt - } - return 0 -} diff --git a/vendor/github.com/golang/snappy/encode.go b/vendor/github.com/golang/snappy/encode.go deleted file mode 100644 index 8d393e904bb..00000000000 --- a/vendor/github.com/golang/snappy/encode.go +++ /dev/null @@ -1,285 +0,0 @@ -// Copyright 2011 The Snappy-Go 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 snappy - -import ( - "encoding/binary" - "errors" - "io" -) - -// Encode returns the encoded form of src. The returned slice may be a sub- -// slice of dst if dst was large enough to hold the entire encoded block. -// Otherwise, a newly allocated slice will be returned. -// -// The dst and src must not overlap. It is valid to pass a nil dst. -func Encode(dst, src []byte) []byte { - if n := MaxEncodedLen(len(src)); n < 0 { - panic(ErrTooLarge) - } else if len(dst) < n { - dst = make([]byte, n) - } - - // The block starts with the varint-encoded length of the decompressed bytes. - d := binary.PutUvarint(dst, uint64(len(src))) - - for len(src) > 0 { - p := src - src = nil - if len(p) > maxBlockSize { - p, src = p[:maxBlockSize], p[maxBlockSize:] - } - if len(p) < minNonLiteralBlockSize { - d += emitLiteral(dst[d:], p) - } else { - d += encodeBlock(dst[d:], p) - } - } - return dst[:d] -} - -// inputMargin is the minimum number of extra input bytes to keep, inside -// encodeBlock's inner loop. On some architectures, this margin lets us -// implement a fast path for emitLiteral, where the copy of short (<= 16 byte) -// literals can be implemented as a single load to and store from a 16-byte -// register. That literal's actual length can be as short as 1 byte, so this -// can copy up to 15 bytes too much, but that's OK as subsequent iterations of -// the encoding loop will fix up the copy overrun, and this inputMargin ensures -// that we don't overrun the dst and src buffers. -const inputMargin = 16 - 1 - -// minNonLiteralBlockSize is the minimum size of the input to encodeBlock that -// could be encoded with a copy tag. This is the minimum with respect to the -// algorithm used by encodeBlock, not a minimum enforced by the file format. -// -// The encoded output must start with at least a 1 byte literal, as there are -// no previous bytes to copy. A minimal (1 byte) copy after that, generated -// from an emitCopy call in encodeBlock's main loop, would require at least -// another inputMargin bytes, for the reason above: we want any emitLiteral -// calls inside encodeBlock's main loop to use the fast path if possible, which -// requires being able to overrun by inputMargin bytes. Thus, -// minNonLiteralBlockSize equals 1 + 1 + inputMargin. -// -// The C++ code doesn't use this exact threshold, but it could, as discussed at -// https://groups.google.com/d/topic/snappy-compression/oGbhsdIJSJ8/discussion -// The difference between Go (2+inputMargin) and C++ (inputMargin) is purely an -// optimization. It should not affect the encoded form. This is tested by -// TestSameEncodingAsCppShortCopies. -const minNonLiteralBlockSize = 1 + 1 + inputMargin - -// MaxEncodedLen returns the maximum length of a snappy block, given its -// uncompressed length. -// -// It will return a negative value if srcLen is too large to encode. -func MaxEncodedLen(srcLen int) int { - n := uint64(srcLen) - if n > 0xffffffff { - return -1 - } - // Compressed data can be defined as: - // compressed := item* literal* - // item := literal* copy - // - // The trailing literal sequence has a space blowup of at most 62/60 - // since a literal of length 60 needs one tag byte + one extra byte - // for length information. - // - // Item blowup is trickier to measure. Suppose the "copy" op copies - // 4 bytes of data. Because of a special check in the encoding code, - // we produce a 4-byte copy only if the offset is < 65536. Therefore - // the copy op takes 3 bytes to encode, and this type of item leads - // to at most the 62/60 blowup for representing literals. - // - // Suppose the "copy" op copies 5 bytes of data. If the offset is big - // enough, it will take 5 bytes to encode the copy op. Therefore the - // worst case here is a one-byte literal followed by a five-byte copy. - // That is, 6 bytes of input turn into 7 bytes of "compressed" data. - // - // This last factor dominates the blowup, so the final estimate is: - n = 32 + n + n/6 - if n > 0xffffffff { - return -1 - } - return int(n) -} - -var errClosed = errors.New("snappy: Writer is closed") - -// NewWriter returns a new Writer that compresses to w. -// -// The Writer returned does not buffer writes. There is no need to Flush or -// Close such a Writer. -// -// Deprecated: the Writer returned is not suitable for many small writes, only -// for few large writes. Use NewBufferedWriter instead, which is efficient -// regardless of the frequency and shape of the writes, and remember to Close -// that Writer when done. -func NewWriter(w io.Writer) *Writer { - return &Writer{ - w: w, - obuf: make([]byte, obufLen), - } -} - -// NewBufferedWriter returns a new Writer that compresses to w, using the -// framing format described at -// https://github.com/google/snappy/blob/master/framing_format.txt -// -// The Writer returned buffers writes. Users must call Close to guarantee all -// data has been forwarded to the underlying io.Writer. They may also call -// Flush zero or more times before calling Close. -func NewBufferedWriter(w io.Writer) *Writer { - return &Writer{ - w: w, - ibuf: make([]byte, 0, maxBlockSize), - obuf: make([]byte, obufLen), - } -} - -// Writer is an io.Writer that can write Snappy-compressed bytes. -type Writer struct { - w io.Writer - err error - - // ibuf is a buffer for the incoming (uncompressed) bytes. - // - // Its use is optional. For backwards compatibility, Writers created by the - // NewWriter function have ibuf == nil, do not buffer incoming bytes, and - // therefore do not need to be Flush'ed or Close'd. - ibuf []byte - - // obuf is a buffer for the outgoing (compressed) bytes. - obuf []byte - - // wroteStreamHeader is whether we have written the stream header. - wroteStreamHeader bool -} - -// Reset discards the writer's state and switches the Snappy writer to write to -// w. This permits reusing a Writer rather than allocating a new one. -func (w *Writer) Reset(writer io.Writer) { - w.w = writer - w.err = nil - if w.ibuf != nil { - w.ibuf = w.ibuf[:0] - } - w.wroteStreamHeader = false -} - -// Write satisfies the io.Writer interface. -func (w *Writer) Write(p []byte) (nRet int, errRet error) { - if w.ibuf == nil { - // Do not buffer incoming bytes. This does not perform or compress well - // if the caller of Writer.Write writes many small slices. This - // behavior is therefore deprecated, but still supported for backwards - // compatibility with code that doesn't explicitly Flush or Close. - return w.write(p) - } - - // The remainder of this method is based on bufio.Writer.Write from the - // standard library. - - for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err == nil { - var n int - if len(w.ibuf) == 0 { - // Large write, empty buffer. - // Write directly from p to avoid copy. - n, _ = w.write(p) - } else { - n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) - w.ibuf = w.ibuf[:len(w.ibuf)+n] - w.Flush() - } - nRet += n - p = p[n:] - } - if w.err != nil { - return nRet, w.err - } - n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) - w.ibuf = w.ibuf[:len(w.ibuf)+n] - nRet += n - return nRet, nil -} - -func (w *Writer) write(p []byte) (nRet int, errRet error) { - if w.err != nil { - return 0, w.err - } - for len(p) > 0 { - obufStart := len(magicChunk) - if !w.wroteStreamHeader { - w.wroteStreamHeader = true - copy(w.obuf, magicChunk) - obufStart = 0 - } - - var uncompressed []byte - if len(p) > maxBlockSize { - uncompressed, p = p[:maxBlockSize], p[maxBlockSize:] - } else { - uncompressed, p = p, nil - } - checksum := crc(uncompressed) - - // Compress the buffer, discarding the result if the improvement - // isn't at least 12.5%. - compressed := Encode(w.obuf[obufHeaderLen:], uncompressed) - chunkType := uint8(chunkTypeCompressedData) - chunkLen := 4 + len(compressed) - obufEnd := obufHeaderLen + len(compressed) - if len(compressed) >= len(uncompressed)-len(uncompressed)/8 { - chunkType = chunkTypeUncompressedData - chunkLen = 4 + len(uncompressed) - obufEnd = obufHeaderLen - } - - // Fill in the per-chunk header that comes before the body. - w.obuf[len(magicChunk)+0] = chunkType - w.obuf[len(magicChunk)+1] = uint8(chunkLen >> 0) - w.obuf[len(magicChunk)+2] = uint8(chunkLen >> 8) - w.obuf[len(magicChunk)+3] = uint8(chunkLen >> 16) - w.obuf[len(magicChunk)+4] = uint8(checksum >> 0) - w.obuf[len(magicChunk)+5] = uint8(checksum >> 8) - w.obuf[len(magicChunk)+6] = uint8(checksum >> 16) - w.obuf[len(magicChunk)+7] = uint8(checksum >> 24) - - if _, err := w.w.Write(w.obuf[obufStart:obufEnd]); err != nil { - w.err = err - return nRet, err - } - if chunkType == chunkTypeUncompressedData { - if _, err := w.w.Write(uncompressed); err != nil { - w.err = err - return nRet, err - } - } - nRet += len(uncompressed) - } - return nRet, nil -} - -// Flush flushes the Writer to its underlying io.Writer. -func (w *Writer) Flush() error { - if w.err != nil { - return w.err - } - if len(w.ibuf) == 0 { - return nil - } - w.write(w.ibuf) - w.ibuf = w.ibuf[:0] - return w.err -} - -// Close calls Flush and then closes the Writer. -func (w *Writer) Close() error { - w.Flush() - ret := w.err - if w.err == nil { - w.err = errClosed - } - return ret -} diff --git a/vendor/github.com/golang/snappy/encode_amd64.go b/vendor/github.com/golang/snappy/encode_amd64.go deleted file mode 100644 index 150d91bc8be..00000000000 --- a/vendor/github.com/golang/snappy/encode_amd64.go +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2016 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine -// +build gc -// +build !noasm - -package snappy - -// emitLiteral has the same semantics as in encode_other.go. -// -//go:noescape -func emitLiteral(dst, lit []byte) int - -// emitCopy has the same semantics as in encode_other.go. -// -//go:noescape -func emitCopy(dst []byte, offset, length int) int - -// extendMatch has the same semantics as in encode_other.go. -// -//go:noescape -func extendMatch(src []byte, i, j int) int - -// encodeBlock has the same semantics as in encode_other.go. -// -//go:noescape -func encodeBlock(dst, src []byte) (d int) diff --git a/vendor/github.com/golang/snappy/encode_amd64.s b/vendor/github.com/golang/snappy/encode_amd64.s deleted file mode 100644 index adfd979fe27..00000000000 --- a/vendor/github.com/golang/snappy/encode_amd64.s +++ /dev/null @@ -1,730 +0,0 @@ -// Copyright 2016 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine -// +build gc -// +build !noasm - -#include "textflag.h" - -// The XXX lines assemble on Go 1.4, 1.5 and 1.7, but not 1.6, due to a -// Go toolchain regression. See https://github.com/golang/go/issues/15426 and -// https://github.com/golang/snappy/issues/29 -// -// As a workaround, the package was built with a known good assembler, and -// those instructions were disassembled by "objdump -d" to yield the -// 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 -// style comments, in AT&T asm syntax. Note that rsp here is a physical -// register, not Go/asm's SP pseudo-register (see https://golang.org/doc/asm). -// The instructions were then encoded as "BYTE $0x.." sequences, which assemble -// fine on Go 1.6. - -// The asm code generally follows the pure Go code in encode_other.go, except -// where marked with a "!!!". - -// ---------------------------------------------------------------------------- - -// func emitLiteral(dst, lit []byte) int -// -// All local variables fit into registers. The register allocation: -// - AX len(lit) -// - BX n -// - DX return value -// - DI &dst[i] -// - R10 &lit[0] -// -// The 24 bytes of stack space is to call runtime·memmove. -// -// The unusual register allocation of local variables, such as R10 for the -// source pointer, matches the allocation used at the call site in encodeBlock, -// which makes it easier to manually inline this function. -TEXT ·emitLiteral(SB), NOSPLIT, $24-56 - MOVQ dst_base+0(FP), DI - MOVQ lit_base+24(FP), R10 - MOVQ lit_len+32(FP), AX - MOVQ AX, DX - MOVL AX, BX - SUBL $1, BX - - CMPL BX, $60 - JLT oneByte - CMPL BX, $256 - JLT twoBytes - -threeBytes: - MOVB $0xf4, 0(DI) - MOVW BX, 1(DI) - ADDQ $3, DI - ADDQ $3, DX - JMP memmove - -twoBytes: - MOVB $0xf0, 0(DI) - MOVB BX, 1(DI) - ADDQ $2, DI - ADDQ $2, DX - JMP memmove - -oneByte: - SHLB $2, BX - MOVB BX, 0(DI) - ADDQ $1, DI - ADDQ $1, DX - -memmove: - MOVQ DX, ret+48(FP) - - // copy(dst[i:], lit) - // - // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push - // DI, R10 and AX as arguments. - MOVQ DI, 0(SP) - MOVQ R10, 8(SP) - MOVQ AX, 16(SP) - CALL runtime·memmove(SB) - RET - -// ---------------------------------------------------------------------------- - -// func emitCopy(dst []byte, offset, length int) int -// -// All local variables fit into registers. The register allocation: -// - AX length -// - SI &dst[0] -// - DI &dst[i] -// - R11 offset -// -// The unusual register allocation of local variables, such as R11 for the -// offset, matches the allocation used at the call site in encodeBlock, which -// makes it easier to manually inline this function. -TEXT ·emitCopy(SB), NOSPLIT, $0-48 - MOVQ dst_base+0(FP), DI - MOVQ DI, SI - MOVQ offset+24(FP), R11 - MOVQ length+32(FP), AX - -loop0: - // for length >= 68 { etc } - CMPL AX, $68 - JLT step1 - - // Emit a length 64 copy, encoded as 3 bytes. - MOVB $0xfe, 0(DI) - MOVW R11, 1(DI) - ADDQ $3, DI - SUBL $64, AX - JMP loop0 - -step1: - // if length > 64 { etc } - CMPL AX, $64 - JLE step2 - - // Emit a length 60 copy, encoded as 3 bytes. - MOVB $0xee, 0(DI) - MOVW R11, 1(DI) - ADDQ $3, DI - SUBL $60, AX - -step2: - // if length >= 12 || offset >= 2048 { goto step3 } - CMPL AX, $12 - JGE step3 - CMPL R11, $2048 - JGE step3 - - // Emit the remaining copy, encoded as 2 bytes. - MOVB R11, 1(DI) - SHRL $8, R11 - SHLB $5, R11 - SUBB $4, AX - SHLB $2, AX - ORB AX, R11 - ORB $1, R11 - MOVB R11, 0(DI) - ADDQ $2, DI - - // Return the number of bytes written. - SUBQ SI, DI - MOVQ DI, ret+40(FP) - RET - -step3: - // Emit the remaining copy, encoded as 3 bytes. - SUBL $1, AX - SHLB $2, AX - ORB $2, AX - MOVB AX, 0(DI) - MOVW R11, 1(DI) - ADDQ $3, DI - - // Return the number of bytes written. - SUBQ SI, DI - MOVQ DI, ret+40(FP) - RET - -// ---------------------------------------------------------------------------- - -// func extendMatch(src []byte, i, j int) int -// -// All local variables fit into registers. The register allocation: -// - DX &src[0] -// - SI &src[j] -// - R13 &src[len(src) - 8] -// - R14 &src[len(src)] -// - R15 &src[i] -// -// The unusual register allocation of local variables, such as R15 for a source -// pointer, matches the allocation used at the call site in encodeBlock, which -// makes it easier to manually inline this function. -TEXT ·extendMatch(SB), NOSPLIT, $0-48 - MOVQ src_base+0(FP), DX - MOVQ src_len+8(FP), R14 - MOVQ i+24(FP), R15 - MOVQ j+32(FP), SI - ADDQ DX, R14 - ADDQ DX, R15 - ADDQ DX, SI - MOVQ R14, R13 - SUBQ $8, R13 - -cmp8: - // As long as we are 8 or more bytes before the end of src, we can load and - // compare 8 bytes at a time. If those 8 bytes are equal, repeat. - CMPQ SI, R13 - JA cmp1 - MOVQ (R15), AX - MOVQ (SI), BX - CMPQ AX, BX - JNE bsf - ADDQ $8, R15 - ADDQ $8, SI - JMP cmp8 - -bsf: - // If those 8 bytes were not equal, XOR the two 8 byte values, and return - // the index of the first byte that differs. The BSF instruction finds the - // least significant 1 bit, the amd64 architecture is little-endian, and - // the shift by 3 converts a bit index to a byte index. - XORQ AX, BX - BSFQ BX, BX - SHRQ $3, BX - ADDQ BX, SI - - // Convert from &src[ret] to ret. - SUBQ DX, SI - MOVQ SI, ret+40(FP) - RET - -cmp1: - // In src's tail, compare 1 byte at a time. - CMPQ SI, R14 - JAE extendMatchEnd - MOVB (R15), AX - MOVB (SI), BX - CMPB AX, BX - JNE extendMatchEnd - ADDQ $1, R15 - ADDQ $1, SI - JMP cmp1 - -extendMatchEnd: - // Convert from &src[ret] to ret. - SUBQ DX, SI - MOVQ SI, ret+40(FP) - RET - -// ---------------------------------------------------------------------------- - -// func encodeBlock(dst, src []byte) (d int) -// -// All local variables fit into registers, other than "var table". The register -// allocation: -// - AX . . -// - BX . . -// - CX 56 shift (note that amd64 shifts by non-immediates must use CX). -// - DX 64 &src[0], tableSize -// - SI 72 &src[s] -// - DI 80 &dst[d] -// - R9 88 sLimit -// - R10 . &src[nextEmit] -// - R11 96 prevHash, currHash, nextHash, offset -// - R12 104 &src[base], skip -// - R13 . &src[nextS], &src[len(src) - 8] -// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x -// - R15 112 candidate -// -// The second column (56, 64, etc) is the stack offset to spill the registers -// when calling other functions. We could pack this slightly tighter, but it's -// simpler to have a dedicated spill map independent of the function called. -// -// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An -// extra 56 bytes, to call other functions, and an extra 64 bytes, to spill -// local variables (registers) during calls gives 32768 + 56 + 64 = 32888. -TEXT ·encodeBlock(SB), 0, $32888-56 - MOVQ dst_base+0(FP), DI - MOVQ src_base+24(FP), SI - MOVQ src_len+32(FP), R14 - - // shift, tableSize := uint32(32-8), 1<<8 - MOVQ $24, CX - MOVQ $256, DX - -calcShift: - // for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { - // shift-- - // } - CMPQ DX, $16384 - JGE varTable - CMPQ DX, R14 - JGE varTable - SUBQ $1, CX - SHLQ $1, DX - JMP calcShift - -varTable: - // var table [maxTableSize]uint16 - // - // In the asm code, unlike the Go code, we can zero-initialize only the - // first tableSize elements. Each uint16 element is 2 bytes and each MOVOU - // writes 16 bytes, so we can do only tableSize/8 writes instead of the - // 2048 writes that would zero-initialize all of table's 32768 bytes. - SHRQ $3, DX - LEAQ table-32768(SP), BX - PXOR X0, X0 - -memclr: - MOVOU X0, 0(BX) - ADDQ $16, BX - SUBQ $1, DX - JNZ memclr - - // !!! DX = &src[0] - MOVQ SI, DX - - // sLimit := len(src) - inputMargin - MOVQ R14, R9 - SUBQ $15, R9 - - // !!! Pre-emptively spill CX, DX and R9 to the stack. Their values don't - // change for the rest of the function. - MOVQ CX, 56(SP) - MOVQ DX, 64(SP) - MOVQ R9, 88(SP) - - // nextEmit := 0 - MOVQ DX, R10 - - // s := 1 - ADDQ $1, SI - - // nextHash := hash(load32(src, s), shift) - MOVL 0(SI), R11 - IMULL $0x1e35a7bd, R11 - SHRL CX, R11 - -outer: - // for { etc } - - // skip := 32 - MOVQ $32, R12 - - // nextS := s - MOVQ SI, R13 - - // candidate := 0 - MOVQ $0, R15 - -inner0: - // for { etc } - - // s := nextS - MOVQ R13, SI - - // bytesBetweenHashLookups := skip >> 5 - MOVQ R12, R14 - SHRQ $5, R14 - - // nextS = s + bytesBetweenHashLookups - ADDQ R14, R13 - - // skip += bytesBetweenHashLookups - ADDQ R14, R12 - - // if nextS > sLimit { goto emitRemainder } - MOVQ R13, AX - SUBQ DX, AX - CMPQ AX, R9 - JA emitRemainder - - // candidate = int(table[nextHash]) - // XXX: MOVWQZX table-32768(SP)(R11*2), R15 - // XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 - BYTE $0x4e - BYTE $0x0f - BYTE $0xb7 - BYTE $0x7c - BYTE $0x5c - BYTE $0x78 - - // table[nextHash] = uint16(s) - MOVQ SI, AX - SUBQ DX, AX - - // XXX: MOVW AX, table-32768(SP)(R11*2) - // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) - BYTE $0x66 - BYTE $0x42 - BYTE $0x89 - BYTE $0x44 - BYTE $0x5c - BYTE $0x78 - - // nextHash = hash(load32(src, nextS), shift) - MOVL 0(R13), R11 - IMULL $0x1e35a7bd, R11 - SHRL CX, R11 - - // if load32(src, s) != load32(src, candidate) { continue } break - MOVL 0(SI), AX - MOVL (DX)(R15*1), BX - CMPL AX, BX - JNE inner0 - -fourByteMatch: - // As per the encode_other.go code: - // - // A 4-byte match has been found. We'll later see etc. - - // !!! Jump to a fast path for short (<= 16 byte) literals. See the comment - // on inputMargin in encode.go. - MOVQ SI, AX - SUBQ R10, AX - CMPQ AX, $16 - JLE emitLiteralFastPath - - // ---------------------------------------- - // Begin inline of the emitLiteral call. - // - // d += emitLiteral(dst[d:], src[nextEmit:s]) - - MOVL AX, BX - SUBL $1, BX - - CMPL BX, $60 - JLT inlineEmitLiteralOneByte - CMPL BX, $256 - JLT inlineEmitLiteralTwoBytes - -inlineEmitLiteralThreeBytes: - MOVB $0xf4, 0(DI) - MOVW BX, 1(DI) - ADDQ $3, DI - JMP inlineEmitLiteralMemmove - -inlineEmitLiteralTwoBytes: - MOVB $0xf0, 0(DI) - MOVB BX, 1(DI) - ADDQ $2, DI - JMP inlineEmitLiteralMemmove - -inlineEmitLiteralOneByte: - SHLB $2, BX - MOVB BX, 0(DI) - ADDQ $1, DI - -inlineEmitLiteralMemmove: - // Spill local variables (registers) onto the stack; call; unspill. - // - // copy(dst[i:], lit) - // - // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push - // DI, R10 and AX as arguments. - MOVQ DI, 0(SP) - MOVQ R10, 8(SP) - MOVQ AX, 16(SP) - ADDQ AX, DI // Finish the "d +=" part of "d += emitLiteral(etc)". - MOVQ SI, 72(SP) - MOVQ DI, 80(SP) - MOVQ R15, 112(SP) - CALL runtime·memmove(SB) - MOVQ 56(SP), CX - MOVQ 64(SP), DX - MOVQ 72(SP), SI - MOVQ 80(SP), DI - MOVQ 88(SP), R9 - MOVQ 112(SP), R15 - JMP inner1 - -inlineEmitLiteralEnd: - // End inline of the emitLiteral call. - // ---------------------------------------- - -emitLiteralFastPath: - // !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2". - MOVB AX, BX - SUBB $1, BX - SHLB $2, BX - MOVB BX, (DI) - ADDQ $1, DI - - // !!! Implement the copy from lit to dst as a 16-byte load and store. - // (Encode's documentation says that dst and src must not overlap.) - // - // This always copies 16 bytes, instead of only len(lit) bytes, but that's - // OK. Subsequent iterations will fix up the overrun. - // - // Note that on amd64, it is legal and cheap to issue unaligned 8-byte or - // 16-byte loads and stores. This technique probably wouldn't be as - // effective on architectures that are fussier about alignment. - MOVOU 0(R10), X0 - MOVOU X0, 0(DI) - ADDQ AX, DI - -inner1: - // for { etc } - - // base := s - MOVQ SI, R12 - - // !!! offset := base - candidate - MOVQ R12, R11 - SUBQ R15, R11 - SUBQ DX, R11 - - // ---------------------------------------- - // Begin inline of the extendMatch call. - // - // s = extendMatch(src, candidate+4, s+4) - - // !!! R14 = &src[len(src)] - MOVQ src_len+32(FP), R14 - ADDQ DX, R14 - - // !!! R13 = &src[len(src) - 8] - MOVQ R14, R13 - SUBQ $8, R13 - - // !!! R15 = &src[candidate + 4] - ADDQ $4, R15 - ADDQ DX, R15 - - // !!! s += 4 - ADDQ $4, SI - -inlineExtendMatchCmp8: - // As long as we are 8 or more bytes before the end of src, we can load and - // compare 8 bytes at a time. If those 8 bytes are equal, repeat. - CMPQ SI, R13 - JA inlineExtendMatchCmp1 - MOVQ (R15), AX - MOVQ (SI), BX - CMPQ AX, BX - JNE inlineExtendMatchBSF - ADDQ $8, R15 - ADDQ $8, SI - JMP inlineExtendMatchCmp8 - -inlineExtendMatchBSF: - // If those 8 bytes were not equal, XOR the two 8 byte values, and return - // the index of the first byte that differs. The BSF instruction finds the - // least significant 1 bit, the amd64 architecture is little-endian, and - // the shift by 3 converts a bit index to a byte index. - XORQ AX, BX - BSFQ BX, BX - SHRQ $3, BX - ADDQ BX, SI - JMP inlineExtendMatchEnd - -inlineExtendMatchCmp1: - // In src's tail, compare 1 byte at a time. - CMPQ SI, R14 - JAE inlineExtendMatchEnd - MOVB (R15), AX - MOVB (SI), BX - CMPB AX, BX - JNE inlineExtendMatchEnd - ADDQ $1, R15 - ADDQ $1, SI - JMP inlineExtendMatchCmp1 - -inlineExtendMatchEnd: - // End inline of the extendMatch call. - // ---------------------------------------- - - // ---------------------------------------- - // Begin inline of the emitCopy call. - // - // d += emitCopy(dst[d:], base-candidate, s-base) - - // !!! length := s - base - MOVQ SI, AX - SUBQ R12, AX - -inlineEmitCopyLoop0: - // for length >= 68 { etc } - CMPL AX, $68 - JLT inlineEmitCopyStep1 - - // Emit a length 64 copy, encoded as 3 bytes. - MOVB $0xfe, 0(DI) - MOVW R11, 1(DI) - ADDQ $3, DI - SUBL $64, AX - JMP inlineEmitCopyLoop0 - -inlineEmitCopyStep1: - // if length > 64 { etc } - CMPL AX, $64 - JLE inlineEmitCopyStep2 - - // Emit a length 60 copy, encoded as 3 bytes. - MOVB $0xee, 0(DI) - MOVW R11, 1(DI) - ADDQ $3, DI - SUBL $60, AX - -inlineEmitCopyStep2: - // if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 } - CMPL AX, $12 - JGE inlineEmitCopyStep3 - CMPL R11, $2048 - JGE inlineEmitCopyStep3 - - // Emit the remaining copy, encoded as 2 bytes. - MOVB R11, 1(DI) - SHRL $8, R11 - SHLB $5, R11 - SUBB $4, AX - SHLB $2, AX - ORB AX, R11 - ORB $1, R11 - MOVB R11, 0(DI) - ADDQ $2, DI - JMP inlineEmitCopyEnd - -inlineEmitCopyStep3: - // Emit the remaining copy, encoded as 3 bytes. - SUBL $1, AX - SHLB $2, AX - ORB $2, AX - MOVB AX, 0(DI) - MOVW R11, 1(DI) - ADDQ $3, DI - -inlineEmitCopyEnd: - // End inline of the emitCopy call. - // ---------------------------------------- - - // nextEmit = s - MOVQ SI, R10 - - // if s >= sLimit { goto emitRemainder } - MOVQ SI, AX - SUBQ DX, AX - CMPQ AX, R9 - JAE emitRemainder - - // As per the encode_other.go code: - // - // We could immediately etc. - - // x := load64(src, s-1) - MOVQ -1(SI), R14 - - // prevHash := hash(uint32(x>>0), shift) - MOVL R14, R11 - IMULL $0x1e35a7bd, R11 - SHRL CX, R11 - - // table[prevHash] = uint16(s-1) - MOVQ SI, AX - SUBQ DX, AX - SUBQ $1, AX - - // XXX: MOVW AX, table-32768(SP)(R11*2) - // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) - BYTE $0x66 - BYTE $0x42 - BYTE $0x89 - BYTE $0x44 - BYTE $0x5c - BYTE $0x78 - - // currHash := hash(uint32(x>>8), shift) - SHRQ $8, R14 - MOVL R14, R11 - IMULL $0x1e35a7bd, R11 - SHRL CX, R11 - - // candidate = int(table[currHash]) - // XXX: MOVWQZX table-32768(SP)(R11*2), R15 - // XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 - BYTE $0x4e - BYTE $0x0f - BYTE $0xb7 - BYTE $0x7c - BYTE $0x5c - BYTE $0x78 - - // table[currHash] = uint16(s) - ADDQ $1, AX - - // XXX: MOVW AX, table-32768(SP)(R11*2) - // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) - BYTE $0x66 - BYTE $0x42 - BYTE $0x89 - BYTE $0x44 - BYTE $0x5c - BYTE $0x78 - - // if uint32(x>>8) == load32(src, candidate) { continue } - MOVL (DX)(R15*1), BX - CMPL R14, BX - JEQ inner1 - - // nextHash = hash(uint32(x>>16), shift) - SHRQ $8, R14 - MOVL R14, R11 - IMULL $0x1e35a7bd, R11 - SHRL CX, R11 - - // s++ - ADDQ $1, SI - - // break out of the inner1 for loop, i.e. continue the outer loop. - JMP outer - -emitRemainder: - // if nextEmit < len(src) { etc } - MOVQ src_len+32(FP), AX - ADDQ DX, AX - CMPQ R10, AX - JEQ encodeBlockEnd - - // d += emitLiteral(dst[d:], src[nextEmit:]) - // - // Push args. - MOVQ DI, 0(SP) - MOVQ $0, 8(SP) // Unnecessary, as the callee ignores it, but conservative. - MOVQ $0, 16(SP) // Unnecessary, as the callee ignores it, but conservative. - MOVQ R10, 24(SP) - SUBQ R10, AX - MOVQ AX, 32(SP) - MOVQ AX, 40(SP) // Unnecessary, as the callee ignores it, but conservative. - - // Spill local variables (registers) onto the stack; call; unspill. - MOVQ DI, 80(SP) - CALL ·emitLiteral(SB) - MOVQ 80(SP), DI - - // Finish the "d +=" part of "d += emitLiteral(etc)". - ADDQ 48(SP), DI - -encodeBlockEnd: - MOVQ dst_base+0(FP), AX - SUBQ AX, DI - MOVQ DI, d+48(FP) - RET diff --git a/vendor/github.com/golang/snappy/encode_other.go b/vendor/github.com/golang/snappy/encode_other.go deleted file mode 100644 index dbcae905e6e..00000000000 --- a/vendor/github.com/golang/snappy/encode_other.go +++ /dev/null @@ -1,238 +0,0 @@ -// Copyright 2016 The Snappy-Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !amd64 appengine !gc noasm - -package snappy - -func load32(b []byte, i int) uint32 { - b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line. - return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 -} - -func load64(b []byte, i int) uint64 { - b = b[i : i+8 : len(b)] // Help the compiler eliminate bounds checks on the next line. - return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | - uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 -} - -// emitLiteral writes a literal chunk and returns the number of bytes written. -// -// It assumes that: -// dst is long enough to hold the encoded bytes -// 1 <= len(lit) && len(lit) <= 65536 -func emitLiteral(dst, lit []byte) int { - i, n := 0, uint(len(lit)-1) - switch { - case n < 60: - dst[0] = uint8(n)<<2 | tagLiteral - i = 1 - case n < 1<<8: - dst[0] = 60<<2 | tagLiteral - dst[1] = uint8(n) - i = 2 - default: - dst[0] = 61<<2 | tagLiteral - dst[1] = uint8(n) - dst[2] = uint8(n >> 8) - i = 3 - } - return i + copy(dst[i:], lit) -} - -// emitCopy writes a copy chunk and returns the number of bytes written. -// -// It assumes that: -// dst is long enough to hold the encoded bytes -// 1 <= offset && offset <= 65535 -// 4 <= length && length <= 65535 -func emitCopy(dst []byte, offset, length int) int { - i := 0 - // The maximum length for a single tagCopy1 or tagCopy2 op is 64 bytes. The - // threshold for this loop is a little higher (at 68 = 64 + 4), and the - // length emitted down below is is a little lower (at 60 = 64 - 4), because - // it's shorter to encode a length 67 copy as a length 60 tagCopy2 followed - // by a length 7 tagCopy1 (which encodes as 3+2 bytes) than to encode it as - // a length 64 tagCopy2 followed by a length 3 tagCopy2 (which encodes as - // 3+3 bytes). The magic 4 in the 64±4 is because the minimum length for a - // tagCopy1 op is 4 bytes, which is why a length 3 copy has to be an - // encodes-as-3-bytes tagCopy2 instead of an encodes-as-2-bytes tagCopy1. - for length >= 68 { - // Emit a length 64 copy, encoded as 3 bytes. - dst[i+0] = 63<<2 | tagCopy2 - dst[i+1] = uint8(offset) - dst[i+2] = uint8(offset >> 8) - i += 3 - length -= 64 - } - if length > 64 { - // Emit a length 60 copy, encoded as 3 bytes. - dst[i+0] = 59<<2 | tagCopy2 - dst[i+1] = uint8(offset) - dst[i+2] = uint8(offset >> 8) - i += 3 - length -= 60 - } - if length >= 12 || offset >= 2048 { - // Emit the remaining copy, encoded as 3 bytes. - dst[i+0] = uint8(length-1)<<2 | tagCopy2 - dst[i+1] = uint8(offset) - dst[i+2] = uint8(offset >> 8) - return i + 3 - } - // Emit the remaining copy, encoded as 2 bytes. - dst[i+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 - dst[i+1] = uint8(offset) - return i + 2 -} - -// extendMatch returns the largest k such that k <= len(src) and that -// src[i:i+k-j] and src[j:k] have the same contents. -// -// It assumes that: -// 0 <= i && i < j && j <= len(src) -func extendMatch(src []byte, i, j int) int { - for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 { - } - return j -} - -func hash(u, shift uint32) uint32 { - return (u * 0x1e35a7bd) >> shift -} - -// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It -// assumes that the varint-encoded length of the decompressed bytes has already -// been written. -// -// It also assumes that: -// len(dst) >= MaxEncodedLen(len(src)) && -// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize -func encodeBlock(dst, src []byte) (d int) { - // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. - // The table element type is uint16, as s < sLimit and sLimit < len(src) - // and len(src) <= maxBlockSize and maxBlockSize == 65536. - const ( - maxTableSize = 1 << 14 - // tableMask is redundant, but helps the compiler eliminate bounds - // checks. - tableMask = maxTableSize - 1 - ) - shift := uint32(32 - 8) - for tableSize := 1 << 8; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { - shift-- - } - // In Go, all array elements are zero-initialized, so there is no advantage - // to a smaller tableSize per se. However, it matches the C++ algorithm, - // and in the asm versions of this code, we can get away with zeroing only - // the first tableSize elements. - var table [maxTableSize]uint16 - - // sLimit is when to stop looking for offset/length copies. The inputMargin - // lets us use a fast path for emitLiteral in the main loop, while we are - // looking for copies. - sLimit := len(src) - inputMargin - - // nextEmit is where in src the next emitLiteral should start from. - nextEmit := 0 - - // The encoded form must start with a literal, as there are no previous - // bytes to copy, so we start looking for hash matches at s == 1. - s := 1 - nextHash := hash(load32(src, s), shift) - - for { - // Copied from the C++ snappy implementation: - // - // Heuristic match skipping: If 32 bytes are scanned with no matches - // found, start looking only at every other byte. If 32 more bytes are - // scanned (or skipped), look at every third byte, etc.. When a match - // is found, immediately go back to looking at every byte. This is a - // small loss (~5% performance, ~0.1% density) for compressible data - // due to more bookkeeping, but for non-compressible data (such as - // JPEG) it's a huge win since the compressor quickly "realizes" the - // data is incompressible and doesn't bother looking for matches - // everywhere. - // - // The "skip" variable keeps track of how many bytes there are since - // the last match; dividing it by 32 (ie. right-shifting by five) gives - // the number of bytes to move ahead for each iteration. - skip := 32 - - nextS := s - candidate := 0 - for { - s = nextS - bytesBetweenHashLookups := skip >> 5 - nextS = s + bytesBetweenHashLookups - skip += bytesBetweenHashLookups - if nextS > sLimit { - goto emitRemainder - } - candidate = int(table[nextHash&tableMask]) - table[nextHash&tableMask] = uint16(s) - nextHash = hash(load32(src, nextS), shift) - if load32(src, s) == load32(src, candidate) { - break - } - } - - // A 4-byte match has been found. We'll later see if more than 4 bytes - // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit - // them as literal bytes. - d += emitLiteral(dst[d:], src[nextEmit:s]) - - // Call emitCopy, and then see if another emitCopy could be our next - // move. Repeat until we find no match for the input immediately after - // what was consumed by the last emitCopy call. - // - // If we exit this loop normally then we need to call emitLiteral next, - // though we don't yet know how big the literal will be. We handle that - // by proceeding to the next iteration of the main loop. We also can - // exit this loop via goto if we get close to exhausting the input. - for { - // Invariant: we have a 4-byte match at s, and no need to emit any - // literal bytes prior to s. - base := s - - // Extend the 4-byte match as long as possible. - // - // This is an inlined version of: - // s = extendMatch(src, candidate+4, s+4) - s += 4 - for i := candidate + 4; s < len(src) && src[i] == src[s]; i, s = i+1, s+1 { - } - - d += emitCopy(dst[d:], base-candidate, s-base) - nextEmit = s - if s >= sLimit { - goto emitRemainder - } - - // We could immediately start working at s now, but to improve - // compression we first update the hash table at s-1 and at s. If - // another emitCopy is not our next move, also calculate nextHash - // at s+1. At least on GOARCH=amd64, these three hash calculations - // are faster as one load64 call (with some shifts) instead of - // three load32 calls. - x := load64(src, s-1) - prevHash := hash(uint32(x>>0), shift) - table[prevHash&tableMask] = uint16(s - 1) - currHash := hash(uint32(x>>8), shift) - candidate = int(table[currHash&tableMask]) - table[currHash&tableMask] = uint16(s) - if uint32(x>>8) != load32(src, candidate) { - nextHash = hash(uint32(x>>16), shift) - s++ - break - } - } - } - -emitRemainder: - if nextEmit < len(src) { - d += emitLiteral(dst[d:], src[nextEmit:]) - } - return d -} diff --git a/vendor/github.com/golang/snappy/snappy.go b/vendor/github.com/golang/snappy/snappy.go deleted file mode 100644 index ece692ea461..00000000000 --- a/vendor/github.com/golang/snappy/snappy.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2011 The Snappy-Go 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 snappy implements the Snappy compression format. It aims for very -// high speeds and reasonable compression. -// -// There are actually two Snappy formats: block and stream. They are related, -// but different: trying to decompress block-compressed data as a Snappy stream -// will fail, and vice versa. The block format is the Decode and Encode -// functions and the stream format is the Reader and Writer types. -// -// The block format, the more common case, is used when the complete size (the -// number of bytes) of the original data is known upfront, at the time -// compression starts. The stream format, also known as the framing format, is -// for when that isn't always true. -// -// The canonical, C++ implementation is at https://github.com/google/snappy and -// it only implements the block format. -package snappy // import "github.com/golang/snappy" - -import ( - "hash/crc32" -) - -/* -Each encoded block begins with the varint-encoded length of the decoded data, -followed by a sequence of chunks. Chunks begin and end on byte boundaries. The -first byte of each chunk is broken into its 2 least and 6 most significant bits -called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. -Zero means a literal tag. All other values mean a copy tag. - -For literal tags: - - If m < 60, the next 1 + m bytes are literal bytes. - - Otherwise, let n be the little-endian unsigned integer denoted by the next - m - 59 bytes. The next 1 + n bytes after that are literal bytes. - -For copy tags, length bytes are copied from offset bytes ago, in the style of -Lempel-Ziv compression algorithms. In particular: - - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). - The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 - of the offset. The next byte is bits 0-7 of the offset. - - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). - The length is 1 + m. The offset is the little-endian unsigned integer - denoted by the next 2 bytes. - - For l == 3, this tag is a legacy format that is no longer issued by most - encoders. Nonetheless, the offset ranges in [0, 1<<32) and the length in - [1, 65). The length is 1 + m. The offset is the little-endian unsigned - integer denoted by the next 4 bytes. -*/ -const ( - tagLiteral = 0x00 - tagCopy1 = 0x01 - tagCopy2 = 0x02 - tagCopy4 = 0x03 -) - -const ( - checksumSize = 4 - chunkHeaderSize = 4 - magicChunk = "\xff\x06\x00\x00" + magicBody - magicBody = "sNaPpY" - - // maxBlockSize is the maximum size of the input to encodeBlock. It is not - // part of the wire format per se, but some parts of the encoder assume - // that an offset fits into a uint16. - // - // Also, for the framing format (Writer type instead of Encode function), - // https://github.com/google/snappy/blob/master/framing_format.txt says - // that "the uncompressed data in a chunk must be no longer than 65536 - // bytes". - maxBlockSize = 65536 - - // maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is - // hard coded to be a const instead of a variable, so that obufLen can also - // be a const. Their equivalence is confirmed by - // TestMaxEncodedLenOfMaxBlockSize. - maxEncodedLenOfMaxBlockSize = 76490 - - obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize - obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize -) - -const ( - chunkTypeCompressedData = 0x00 - chunkTypeUncompressedData = 0x01 - chunkTypePadding = 0xfe - chunkTypeStreamIdentifier = 0xff -) - -var crcTable = crc32.MakeTable(crc32.Castagnoli) - -// crc implements the checksum specified in section 3 of -// https://github.com/google/snappy/blob/master/framing_format.txt -func crc(b []byte) uint32 { - c := crc32.Update(0, crcTable, b) - return uint32(c>>15|c<<17) + 0xa282ead8 -} diff --git a/vendor/github.com/google/go-github/AUTHORS b/vendor/github.com/google/go-github/AUTHORS deleted file mode 100644 index 548ff1587dd..00000000000 --- a/vendor/github.com/google/go-github/AUTHORS +++ /dev/null @@ -1,171 +0,0 @@ -# This is the official list of go-github authors for copyright purposes. -# -# This does not necessarily list everyone who has contributed code, since in -# some cases, their employer may be the copyright holder. To see the full list -# of contributors, see the revision history in source control or -# https://github.com/google/go-github/graphs/contributors. -# -# Authors who wish to be recognized in this file should add themselves (or -# their employer, as appropriate). - -178inaba -Abhinav Gupta -Ahmed Hagy -Ainsley Chong -Akeda Bagus -Alec Thomas -Aleks Clark -Alex Bramley -Alexander Harkness -Allen Sun -Amey Sakhadeo -Andreas Garnæs -Andrew Ryabchun -Andy Hume -Andy Lindeman -Anshuman Bhartiya -Antoine Pelisse -Anubha Kushwaha -Aravind -Arıl Bozoluk -Austin Dizzy -Beshr Kayali -Beyang Liu -Billy Lynch -Björn Häuser -Brad Harris -Bradley Falzon -Brian Egizi -Bryan Boreham -Cami Diez -Carlos Alexandro Becker -chandresh-pancholi -Charlie Yan -Chris King -Chris Roche -Chris Schaefer -Christoph Sassenberg -Colin Misare -Craig Peterson -Cristian Maglie -Daehyeok Mun -Daniel Leavitt -Dave Du Cros -Dave Henderson -David Deng -Dennis Webb -Diego Lapiduz -Dmitri Shuralyov -dmnlk -Don Petersen -Doug Turner -Drew Fradette -Eli Uriegas -Elliott Beach -erwinvaneyk -Fabrice -Filippo Valsorda -Florian Forster -Francesc Gil -Francis -Fredrik Jönsson -Garrett Squire -Georgy Buranov -Gnahz -Google Inc. -griffin_stewie -Guz Alexander -Hanno Hecker -Hari haran -haya14busa -Huy Tr -huydx -i2bskn -Isao Jonas -isqua -Jameel Haffejee -Jan Kosecki -Jeremy Morris -Jihoon Chung -Jimmi Dyson -Joe Tsai -John Barton -John Engelman -jpbelanger-mtl -Juan Basso -Julien Rostand -Justin Abrahms -jzhoucliqr -Katrina Owen -Keita Urashima -Kevin Burke -Konrad Malawski -Kookheon Kwon -Krzysztof Kowalczyk -Kshitij Saraogi -kyokomi -Lucas Alcantara -Luke Evers -Luke Kysow -Luke Roberts -Luke Young -Maksim Zhylinski -Martin-Louis Bright -Mat Geist -Matt Brender -Matt Landis -Maxime Bury -Michael Tiller -Michał Glapa -Nathan VanBenschoten -Neil O'Toole -Nick Miyake -Nick Spragg -Nikhita Raghunath -Noah Zoschke -ns-cweber -Ondřej Kupka -Panagiotis Moustafellos -Parker Moore -Pavel Shtanko -Petr Shevtsov -Pierre Carrier -Piotr Zurek -Quinn Slack -Rackspace US, Inc. -RaviTeja Pothana -rc1140 -Red Hat, Inc. -Rob Figueiredo -Ronak Jain -Ruben Vereecken -Ryan Lower -Sahil Dua -saisi -Sam Minnée -Sander van Harmelen -Sarasa Kisaragi -Sean Wang -Sebastian Mandrean -Sebastian Mæland Pedersen -Sevki -Shawn Catanzarite -Shawn Smith -sona-tar -SoundCloud, Ltd. -Stian Eikeland -Thomas Bruyelle -Timothée Peignier -Trey Tacon -ttacon -Varadarajan Aravamudhan -Victor Castell -Victor Vrantchan -Vlad Ungureanu -Will Maier -William Bailey -Yann Malet -Yannick Utard -Yicheng Qin -Yumikiyo Osanai -Zach Latta diff --git a/vendor/github.com/google/go-github/LICENSE b/vendor/github.com/google/go-github/LICENSE deleted file mode 100644 index 53d5374a711..00000000000 --- a/vendor/github.com/google/go-github/LICENSE +++ /dev/null @@ -1,341 +0,0 @@ -Copyright (c) 2013 The go-github AUTHORS. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - ----------- - -Some documentation is taken from the GitHub Developer site -, which is available under the following Creative -Commons Attribution 3.0 License. This applies only to the go-github source -code and would not apply to any compiled binaries. - -THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS OF THIS CREATIVE -COMMONS PUBLIC LICENSE ("CCPL" OR "LICENSE"). THE WORK IS PROTECTED BY -COPYRIGHT AND/OR OTHER APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS -AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS PROHIBITED. - -BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU ACCEPT AND AGREE -TO BE BOUND BY THE TERMS OF THIS LICENSE. TO THE EXTENT THIS LICENSE MAY -BE CONSIDERED TO BE A CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS -CONTAINED HERE IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND -CONDITIONS. - -1. Definitions - - a. "Adaptation" means a work based upon the Work, or upon the Work and - other pre-existing works, such as a translation, adaptation, - derivative work, arrangement of music or other alterations of a - literary or artistic work, or phonogram or performance and includes - cinematographic adaptations or any other form in which the Work may be - recast, transformed, or adapted including in any form recognizably - derived from the original, except that a work that constitutes a - Collection will not be considered an Adaptation for the purpose of - this License. For the avoidance of doubt, where the Work is a musical - work, performance or phonogram, the synchronization of the Work in - timed-relation with a moving image ("synching") will be considered an - Adaptation for the purpose of this License. - b. "Collection" means a collection of literary or artistic works, such as - encyclopedias and anthologies, or performances, phonograms or - broadcasts, or other works or subject matter other than works listed - in Section 1(f) below, which, by reason of the selection and - arrangement of their contents, constitute intellectual creations, in - which the Work is included in its entirety in unmodified form along - with one or more other contributions, each constituting separate and - independent works in themselves, which together are assembled into a - collective whole. A work that constitutes a Collection will not be - considered an Adaptation (as defined above) for the purposes of this - License. - c. "Distribute" means to make available to the public the original and - copies of the Work or Adaptation, as appropriate, through sale or - other transfer of ownership. - d. "Licensor" means the individual, individuals, entity or entities that - offer(s) the Work under the terms of this License. - e. "Original Author" means, in the case of a literary or artistic work, - the individual, individuals, entity or entities who created the Work - or if no individual or entity can be identified, the publisher; and in - addition (i) in the case of a performance the actors, singers, - musicians, dancers, and other persons who act, sing, deliver, declaim, - play in, interpret or otherwise perform literary or artistic works or - expressions of folklore; (ii) in the case of a phonogram the producer - being the person or legal entity who first fixes the sounds of a - performance or other sounds; and, (iii) in the case of broadcasts, the - organization that transmits the broadcast. - f. "Work" means the literary and/or artistic work offered under the terms - of this License including without limitation any production in the - literary, scientific and artistic domain, whatever may be the mode or - form of its expression including digital form, such as a book, - pamphlet and other writing; a lecture, address, sermon or other work - of the same nature; a dramatic or dramatico-musical work; a - choreographic work or entertainment in dumb show; a musical - composition with or without words; a cinematographic work to which are - assimilated works expressed by a process analogous to cinematography; - a work of drawing, painting, architecture, sculpture, engraving or - lithography; a photographic work to which are assimilated works - expressed by a process analogous to photography; a work of applied - art; an illustration, map, plan, sketch or three-dimensional work - relative to geography, topography, architecture or science; a - performance; a broadcast; a phonogram; a compilation of data to the - extent it is protected as a copyrightable work; or a work performed by - a variety or circus performer to the extent it is not otherwise - considered a literary or artistic work. - g. "You" means an individual or entity exercising rights under this - License who has not previously violated the terms of this License with - respect to the Work, or who has received express permission from the - Licensor to exercise rights under this License despite a previous - violation. - h. "Publicly Perform" means to perform public recitations of the Work and - to communicate to the public those public recitations, by any means or - process, including by wire or wireless means or public digital - performances; to make available to the public Works in such a way that - members of the public may access these Works from a place and at a - place individually chosen by them; to perform the Work to the public - by any means or process and the communication to the public of the - performances of the Work, including by public digital performance; to - broadcast and rebroadcast the Work by any means including signs, - sounds or images. - i. "Reproduce" means to make copies of the Work by any means including - without limitation by sound or visual recordings and the right of - fixation and reproducing fixations of the Work, including storage of a - protected performance or phonogram in digital form or other electronic - medium. - -2. Fair Dealing Rights. Nothing in this License is intended to reduce, -limit, or restrict any uses free from copyright or rights arising from -limitations or exceptions that are provided for in connection with the -copyright protection under copyright law or other applicable laws. - -3. License Grant. Subject to the terms and conditions of this License, -Licensor hereby grants You a worldwide, royalty-free, non-exclusive, -perpetual (for the duration of the applicable copyright) license to -exercise the rights in the Work as stated below: - - a. to Reproduce the Work, to incorporate the Work into one or more - Collections, and to Reproduce the Work as incorporated in the - Collections; - b. to create and Reproduce Adaptations provided that any such Adaptation, - including any translation in any medium, takes reasonable steps to - clearly label, demarcate or otherwise identify that changes were made - to the original Work. For example, a translation could be marked "The - original work was translated from English to Spanish," or a - modification could indicate "The original work has been modified."; - c. to Distribute and Publicly Perform the Work including as incorporated - in Collections; and, - d. to Distribute and Publicly Perform Adaptations. - e. For the avoidance of doubt: - - i. Non-waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme cannot be waived, the Licensor - reserves the exclusive right to collect such royalties for any - exercise by You of the rights granted under this License; - ii. Waivable Compulsory License Schemes. In those jurisdictions in - which the right to collect royalties through any statutory or - compulsory licensing scheme can be waived, the Licensor waives the - exclusive right to collect such royalties for any exercise by You - of the rights granted under this License; and, - iii. Voluntary License Schemes. The Licensor waives the right to - collect royalties, whether individually or, in the event that the - Licensor is a member of a collecting society that administers - voluntary licensing schemes, via that society, from any exercise - by You of the rights granted under this License. - -The above rights may be exercised in all media and formats whether now -known or hereafter devised. The above rights include the right to make -such modifications as are technically necessary to exercise the rights in -other media and formats. Subject to Section 8(f), all rights not expressly -granted by Licensor are hereby reserved. - -4. Restrictions. The license granted in Section 3 above is expressly made -subject to and limited by the following restrictions: - - a. You may Distribute or Publicly Perform the Work only under the terms - of this License. You must include a copy of, or the Uniform Resource - Identifier (URI) for, this License with every copy of the Work You - Distribute or Publicly Perform. You may not offer or impose any terms - on the Work that restrict the terms of this License or the ability of - the recipient of the Work to exercise the rights granted to that - recipient under the terms of the License. You may not sublicense the - Work. You must keep intact all notices that refer to this License and - to the disclaimer of warranties with every copy of the Work You - Distribute or Publicly Perform. When You Distribute or Publicly - Perform the Work, You may not impose any effective technological - measures on the Work that restrict the ability of a recipient of the - Work from You to exercise the rights granted to that recipient under - the terms of the License. This Section 4(a) applies to the Work as - incorporated in a Collection, but this does not require the Collection - apart from the Work itself to be made subject to the terms of this - License. If You create a Collection, upon notice from any Licensor You - must, to the extent practicable, remove from the Collection any credit - as required by Section 4(b), as requested. If You create an - Adaptation, upon notice from any Licensor You must, to the extent - practicable, remove from the Adaptation any credit as required by - Section 4(b), as requested. - b. If You Distribute, or Publicly Perform the Work or any Adaptations or - Collections, You must, unless a request has been made pursuant to - Section 4(a), keep intact all copyright notices for the Work and - provide, reasonable to the medium or means You are utilizing: (i) the - name of the Original Author (or pseudonym, if applicable) if supplied, - and/or if the Original Author and/or Licensor designate another party - or parties (e.g., a sponsor institute, publishing entity, journal) for - attribution ("Attribution Parties") in Licensor's copyright notice, - terms of service or by other reasonable means, the name of such party - or parties; (ii) the title of the Work if supplied; (iii) to the - extent reasonably practicable, the URI, if any, that Licensor - specifies to be associated with the Work, unless such URI does not - refer to the copyright notice or licensing information for the Work; - and (iv) , consistent with Section 3(b), in the case of an Adaptation, - a credit identifying the use of the Work in the Adaptation (e.g., - "French translation of the Work by Original Author," or "Screenplay - based on original Work by Original Author"). The credit required by - this Section 4 (b) may be implemented in any reasonable manner; - provided, however, that in the case of a Adaptation or Collection, at - a minimum such credit will appear, if a credit for all contributing - authors of the Adaptation or Collection appears, then as part of these - credits and in a manner at least as prominent as the credits for the - other contributing authors. For the avoidance of doubt, You may only - use the credit required by this Section for the purpose of attribution - in the manner set out above and, by exercising Your rights under this - License, You may not implicitly or explicitly assert or imply any - connection with, sponsorship or endorsement by the Original Author, - Licensor and/or Attribution Parties, as appropriate, of You or Your - use of the Work, without the separate, express prior written - permission of the Original Author, Licensor and/or Attribution - Parties. - c. Except as otherwise agreed in writing by the Licensor or as may be - otherwise permitted by applicable law, if You Reproduce, Distribute or - Publicly Perform the Work either by itself or as part of any - Adaptations or Collections, You must not distort, mutilate, modify or - take other derogatory action in relation to the Work which would be - prejudicial to the Original Author's honor or reputation. Licensor - agrees that in those jurisdictions (e.g. Japan), in which any exercise - of the right granted in Section 3(b) of this License (the right to - make Adaptations) would be deemed to be a distortion, mutilation, - modification or other derogatory action prejudicial to the Original - Author's honor and reputation, the Licensor will waive or not assert, - as appropriate, this Section, to the fullest extent permitted by the - applicable national law, to enable You to reasonably exercise Your - right under Section 3(b) of this License (right to make Adaptations) - but not otherwise. - -5. Representations, Warranties and Disclaimer - -UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN WRITING, LICENSOR -OFFERS THE WORK AS-IS AND MAKES NO REPRESENTATIONS OR WARRANTIES OF ANY -KIND CONCERNING THE WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, -INCLUDING, WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY, -FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE ABSENCE OF -LATENT OR OTHER DEFECTS, ACCURACY, OR THE PRESENCE OF ABSENCE OF ERRORS, -WHETHER OR NOT DISCOVERABLE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION -OF IMPLIED WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU. - -6. Limitation on Liability. EXCEPT TO THE EXTENT REQUIRED BY APPLICABLE -LAW, IN NO EVENT WILL LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR -ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY DAMAGES -ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK, EVEN IF LICENSOR HAS -BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. - -7. Termination - - a. This License and the rights granted hereunder will terminate - automatically upon any breach by You of the terms of this License. - Individuals or entities who have received Adaptations or Collections - from You under this License, however, will not have their licenses - terminated provided such individuals or entities remain in full - compliance with those licenses. Sections 1, 2, 5, 6, 7, and 8 will - survive any termination of this License. - b. Subject to the above terms and conditions, the license granted here is - perpetual (for the duration of the applicable copyright in the Work). - Notwithstanding the above, Licensor reserves the right to release the - Work under different license terms or to stop distributing the Work at - any time; provided, however that any such election will not serve to - withdraw this License (or any other license that has been, or is - required to be, granted under the terms of this License), and this - License will continue in full force and effect unless terminated as - stated above. - -8. Miscellaneous - - a. Each time You Distribute or Publicly Perform the Work or a Collection, - the Licensor offers to the recipient a license to the Work on the same - terms and conditions as the license granted to You under this License. - b. Each time You Distribute or Publicly Perform an Adaptation, Licensor - offers to the recipient a license to the original Work on the same - terms and conditions as the license granted to You under this License. - c. If any provision of this License is invalid or unenforceable under - applicable law, it shall not affect the validity or enforceability of - the remainder of the terms of this License, and without further action - by the parties to this agreement, such provision shall be reformed to - the minimum extent necessary to make such provision valid and - enforceable. - d. No term or provision of this License shall be deemed waived and no - breach consented to unless such waiver or consent shall be in writing - and signed by the party to be charged with such waiver or consent. - e. This License constitutes the entire agreement between the parties with - respect to the Work licensed here. There are no understandings, - agreements or representations with respect to the Work not specified - here. Licensor shall not be bound by any additional provisions that - may appear in any communication from You. This License may not be - modified without the mutual written agreement of the Licensor and You. - f. The rights granted under, and the subject matter referenced, in this - License were drafted utilizing the terminology of the Berne Convention - for the Protection of Literary and Artistic Works (as amended on - September 28, 1979), the Rome Convention of 1961, the WIPO Copyright - Treaty of 1996, the WIPO Performances and Phonograms Treaty of 1996 - and the Universal Copyright Convention (as revised on July 24, 1971). - These rights and subject matter take effect in the relevant - jurisdiction in which the License terms are sought to be enforced - according to the corresponding provisions of the implementation of - those treaty provisions in the applicable national law. If the - standard suite of rights granted under applicable copyright law - includes additional rights not granted under this License, such - additional rights are deemed to be included in the License; this - License is not intended to restrict the license of any rights under - applicable law. - - -Creative Commons Notice - - Creative Commons is not a party to this License, and makes no warranty - whatsoever in connection with the Work. Creative Commons will not be - liable to You or any party on any legal theory for any damages - whatsoever, including without limitation any general, special, - incidental or consequential damages arising in connection to this - license. Notwithstanding the foregoing two (2) sentences, if Creative - Commons has expressly identified itself as the Licensor hereunder, it - shall have all rights and obligations of Licensor. - - Except for the limited purpose of indicating to the public that the - Work is licensed under the CCPL, Creative Commons does not authorize - the use by either party of the trademark "Creative Commons" or any - related trademark or logo of Creative Commons without the prior - written consent of Creative Commons. Any permitted use will be in - compliance with Creative Commons' then-current trademark usage - guidelines, as may be published on its website or otherwise made - available upon request from time to time. For the avoidance of doubt, - this trademark restriction does not form part of this License. - - Creative Commons may be contacted at http://creativecommons.org/. diff --git a/vendor/github.com/google/go-github/github/activity.go b/vendor/github.com/google/go-github/github/activity.go deleted file mode 100644 index d6c992c7f50..00000000000 --- a/vendor/github.com/google/go-github/github/activity.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import "context" - -// ActivityService handles communication with the activity related -// methods of the GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/activity/ -type ActivityService service - -// FeedLink represents a link to a related resource. -type FeedLink struct { - HRef *string `json:"href,omitempty"` - Type *string `json:"type,omitempty"` -} - -// Feeds represents timeline resources in Atom format. -type Feeds struct { - TimelineURL *string `json:"timeline_url,omitempty"` - UserURL *string `json:"user_url,omitempty"` - CurrentUserPublicURL *string `json:"current_user_public_url,omitempty"` - CurrentUserURL *string `json:"current_user_url,omitempty"` - CurrentUserActorURL *string `json:"current_user_actor_url,omitempty"` - CurrentUserOrganizationURL *string `json:"current_user_organization_url,omitempty"` - CurrentUserOrganizationURLs []string `json:"current_user_organization_urls,omitempty"` - Links *struct { - Timeline *FeedLink `json:"timeline,omitempty"` - User *FeedLink `json:"user,omitempty"` - CurrentUserPublic *FeedLink `json:"current_user_public,omitempty"` - CurrentUser *FeedLink `json:"current_user,omitempty"` - CurrentUserActor *FeedLink `json:"current_user_actor,omitempty"` - CurrentUserOrganization *FeedLink `json:"current_user_organization,omitempty"` - CurrentUserOrganizations []FeedLink `json:"current_user_organizations,omitempty"` - } `json:"_links,omitempty"` -} - -// ListFeeds lists all the feeds available to the authenticated user. -// -// GitHub provides several timeline resources in Atom format: -// Timeline: The GitHub global public timeline -// User: The public timeline for any user, using URI template -// Current user public: The public timeline for the authenticated user -// Current user: The private timeline for the authenticated user -// Current user actor: The private timeline for activity created by the -// authenticated user -// Current user organizations: The private timeline for the organizations -// the authenticated user is a member of. -// -// Note: Private feeds are only returned when authenticating via Basic Auth -// since current feed URIs use the older, non revocable auth tokens. -func (s *ActivityService) ListFeeds(ctx context.Context) (*Feeds, *Response, error) { - req, err := s.client.NewRequest("GET", "feeds", nil) - if err != nil { - return nil, nil, err - } - - f := &Feeds{} - resp, err := s.client.Do(ctx, req, f) - if err != nil { - return nil, resp, err - } - - return f, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/activity_events.go b/vendor/github.com/google/go-github/github/activity_events.go deleted file mode 100644 index f337fcd2b05..00000000000 --- a/vendor/github.com/google/go-github/github/activity_events.go +++ /dev/null @@ -1,324 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "encoding/json" - "fmt" - "time" -) - -// Event represents a GitHub event. -type Event struct { - Type *string `json:"type,omitempty"` - Public *bool `json:"public,omitempty"` - RawPayload *json.RawMessage `json:"payload,omitempty"` - Repo *Repository `json:"repo,omitempty"` - Actor *User `json:"actor,omitempty"` - Org *Organization `json:"org,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - ID *string `json:"id,omitempty"` -} - -func (e Event) String() string { - return Stringify(e) -} - -// ParsePayload parses the event payload. For recognized event types, -// a value of the corresponding struct type will be returned. -func (e *Event) ParsePayload() (payload interface{}, err error) { - switch *e.Type { - case "CommitCommentEvent": - payload = &CommitCommentEvent{} - case "CreateEvent": - payload = &CreateEvent{} - case "DeleteEvent": - payload = &DeleteEvent{} - case "DeploymentEvent": - payload = &DeploymentEvent{} - case "DeploymentStatusEvent": - payload = &DeploymentStatusEvent{} - case "ForkEvent": - payload = &ForkEvent{} - case "GollumEvent": - payload = &GollumEvent{} - case "InstallationEvent": - payload = &InstallationEvent{} - case "InstallationRepositoriesEvent": - payload = &InstallationRepositoriesEvent{} - case "IssueCommentEvent": - payload = &IssueCommentEvent{} - case "IssuesEvent": - payload = &IssuesEvent{} - case "LabelEvent": - payload = &LabelEvent{} - case "MarketplacePurchaseEvent": - payload = &MarketplacePurchaseEvent{} - case "MemberEvent": - payload = &MemberEvent{} - case "MembershipEvent": - payload = &MembershipEvent{} - case "MilestoneEvent": - payload = &MilestoneEvent{} - case "OrganizationEvent": - payload = &OrganizationEvent{} - case "OrgBlockEvent": - payload = &OrgBlockEvent{} - case "PageBuildEvent": - payload = &PageBuildEvent{} - case "PingEvent": - payload = &PingEvent{} - case "ProjectEvent": - payload = &ProjectEvent{} - case "ProjectCardEvent": - payload = &ProjectCardEvent{} - case "ProjectColumnEvent": - payload = &ProjectColumnEvent{} - case "PublicEvent": - payload = &PublicEvent{} - case "PullRequestEvent": - payload = &PullRequestEvent{} - case "PullRequestReviewEvent": - payload = &PullRequestReviewEvent{} - case "PullRequestReviewCommentEvent": - payload = &PullRequestReviewCommentEvent{} - case "PushEvent": - payload = &PushEvent{} - case "ReleaseEvent": - payload = &ReleaseEvent{} - case "RepositoryEvent": - payload = &RepositoryEvent{} - case "StatusEvent": - payload = &StatusEvent{} - case "TeamEvent": - payload = &TeamEvent{} - case "TeamAddEvent": - payload = &TeamAddEvent{} - case "WatchEvent": - payload = &WatchEvent{} - } - err = json.Unmarshal(*e.RawPayload, &payload) - return payload, err -} - -// Payload returns the parsed event payload. For recognized event types, -// a value of the corresponding struct type will be returned. -// -// Deprecated: Use ParsePayload instead, which returns an error -// rather than panics if JSON unmarshaling raw payload fails. -func (e *Event) Payload() (payload interface{}) { - var err error - payload, err = e.ParsePayload() - if err != nil { - panic(err) - } - return payload -} - -// ListEvents drinks from the firehose of all public events across GitHub. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events -func (s *ActivityService) ListEvents(ctx context.Context, opt *ListOptions) ([]*Event, *Response, error) { - u, err := addOptions("events", opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var events []*Event - resp, err := s.client.Do(ctx, req, &events) - if err != nil { - return nil, resp, err - } - - return events, resp, nil -} - -// ListRepositoryEvents lists events for a repository. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-repository-events -func (s *ActivityService) ListRepositoryEvents(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Event, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/events", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var events []*Event - resp, err := s.client.Do(ctx, req, &events) - if err != nil { - return nil, resp, err - } - - return events, resp, nil -} - -// ListIssueEventsForRepository lists issue events for a repository. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-issue-events-for-a-repository -func (s *ActivityService) ListIssueEventsForRepository(ctx context.Context, owner, repo string, opt *ListOptions) ([]*IssueEvent, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var events []*IssueEvent - resp, err := s.client.Do(ctx, req, &events) - if err != nil { - return nil, resp, err - } - - return events, resp, nil -} - -// ListEventsForRepoNetwork lists public events for a network of repositories. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-a-network-of-repositories -func (s *ActivityService) ListEventsForRepoNetwork(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Event, *Response, error) { - u := fmt.Sprintf("networks/%v/%v/events", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var events []*Event - resp, err := s.client.Do(ctx, req, &events) - if err != nil { - return nil, resp, err - } - - return events, resp, nil -} - -// ListEventsForOrganization lists public events for an organization. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-public-events-for-an-organization -func (s *ActivityService) ListEventsForOrganization(ctx context.Context, org string, opt *ListOptions) ([]*Event, *Response, error) { - u := fmt.Sprintf("orgs/%v/events", org) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var events []*Event - resp, err := s.client.Do(ctx, req, &events) - if err != nil { - return nil, resp, err - } - - return events, resp, nil -} - -// ListEventsPerformedByUser lists the events performed by a user. If publicOnly is -// true, only public events will be returned. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-performed-by-a-user -func (s *ActivityService) ListEventsPerformedByUser(ctx context.Context, user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) { - var u string - if publicOnly { - u = fmt.Sprintf("users/%v/events/public", user) - } else { - u = fmt.Sprintf("users/%v/events", user) - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var events []*Event - resp, err := s.client.Do(ctx, req, &events) - if err != nil { - return nil, resp, err - } - - return events, resp, nil -} - -// ListEventsReceivedByUser lists the events received by a user. If publicOnly is -// true, only public events will be returned. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-that-a-user-has-received -func (s *ActivityService) ListEventsReceivedByUser(ctx context.Context, user string, publicOnly bool, opt *ListOptions) ([]*Event, *Response, error) { - var u string - if publicOnly { - u = fmt.Sprintf("users/%v/received_events/public", user) - } else { - u = fmt.Sprintf("users/%v/received_events", user) - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var events []*Event - resp, err := s.client.Do(ctx, req, &events) - if err != nil { - return nil, resp, err - } - - return events, resp, nil -} - -// ListUserEventsForOrganization provides the user’s organization dashboard. You -// must be authenticated as the user to view this. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/#list-events-for-an-organization -func (s *ActivityService) ListUserEventsForOrganization(ctx context.Context, org, user string, opt *ListOptions) ([]*Event, *Response, error) { - u := fmt.Sprintf("users/%v/events/orgs/%v", user, org) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var events []*Event - resp, err := s.client.Do(ctx, req, &events) - if err != nil { - return nil, resp, err - } - - return events, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/activity_notifications.go b/vendor/github.com/google/go-github/github/activity_notifications.go deleted file mode 100644 index 45c8b2aeced..00000000000 --- a/vendor/github.com/google/go-github/github/activity_notifications.go +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2014 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// Notification identifies a GitHub notification for a user. -type Notification struct { - ID *string `json:"id,omitempty"` - Repository *Repository `json:"repository,omitempty"` - Subject *NotificationSubject `json:"subject,omitempty"` - - // Reason identifies the event that triggered the notification. - // - // GitHub API docs: https://developer.github.com/v3/activity/notifications/#notification-reasons - Reason *string `json:"reason,omitempty"` - - Unread *bool `json:"unread,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - LastReadAt *time.Time `json:"last_read_at,omitempty"` - URL *string `json:"url,omitempty"` -} - -// NotificationSubject identifies the subject of a notification. -type NotificationSubject struct { - Title *string `json:"title,omitempty"` - URL *string `json:"url,omitempty"` - LatestCommentURL *string `json:"latest_comment_url,omitempty"` - Type *string `json:"type,omitempty"` -} - -// NotificationListOptions specifies the optional parameters to the -// ActivityService.ListNotifications method. -type NotificationListOptions struct { - All bool `url:"all,omitempty"` - Participating bool `url:"participating,omitempty"` - Since time.Time `url:"since,omitempty"` - Before time.Time `url:"before,omitempty"` - - ListOptions -} - -// ListNotifications lists all notifications for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications -func (s *ActivityService) ListNotifications(ctx context.Context, opt *NotificationListOptions) ([]*Notification, *Response, error) { - u := fmt.Sprintf("notifications") - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var notifications []*Notification - resp, err := s.client.Do(ctx, req, ¬ifications) - if err != nil { - return nil, resp, err - } - - return notifications, resp, nil -} - -// ListRepositoryNotifications lists all notifications in a given repository -// for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#list-your-notifications-in-a-repository -func (s *ActivityService) ListRepositoryNotifications(ctx context.Context, owner, repo string, opt *NotificationListOptions) ([]*Notification, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var notifications []*Notification - resp, err := s.client.Do(ctx, req, ¬ifications) - if err != nil { - return nil, resp, err - } - - return notifications, resp, nil -} - -type markReadOptions struct { - LastReadAt time.Time `json:"last_read_at,omitempty"` -} - -// MarkNotificationsRead marks all notifications up to lastRead as read. -// -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-as-read -func (s *ActivityService) MarkNotificationsRead(ctx context.Context, lastRead time.Time) (*Response, error) { - opts := &markReadOptions{ - LastReadAt: lastRead, - } - req, err := s.client.NewRequest("PUT", "notifications", opts) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// MarkRepositoryNotificationsRead marks all notifications up to lastRead in -// the specified repository as read. -// -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-notifications-as-read-in-a-repository -func (s *ActivityService) MarkRepositoryNotificationsRead(ctx context.Context, owner, repo string, lastRead time.Time) (*Response, error) { - opts := &markReadOptions{ - LastReadAt: lastRead, - } - u := fmt.Sprintf("repos/%v/%v/notifications", owner, repo) - req, err := s.client.NewRequest("PUT", u, opts) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// GetThread gets the specified notification thread. -// -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#view-a-single-thread -func (s *ActivityService) GetThread(ctx context.Context, id string) (*Notification, *Response, error) { - u := fmt.Sprintf("notifications/threads/%v", id) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - notification := new(Notification) - resp, err := s.client.Do(ctx, req, notification) - if err != nil { - return nil, resp, err - } - - return notification, resp, nil -} - -// MarkThreadRead marks the specified thread as read. -// -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#mark-a-thread-as-read -func (s *ActivityService) MarkThreadRead(ctx context.Context, id string) (*Response, error) { - u := fmt.Sprintf("notifications/threads/%v", id) - - req, err := s.client.NewRequest("PATCH", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// GetThreadSubscription checks to see if the authenticated user is subscribed -// to a thread. -// -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#get-a-thread-subscription -func (s *ActivityService) GetThreadSubscription(ctx context.Context, id string) (*Subscription, *Response, error) { - u := fmt.Sprintf("notifications/threads/%v/subscription", id) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - sub := new(Subscription) - resp, err := s.client.Do(ctx, req, sub) - if err != nil { - return nil, resp, err - } - - return sub, resp, nil -} - -// SetThreadSubscription sets the subscription for the specified thread for the -// authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#set-a-thread-subscription -func (s *ActivityService) SetThreadSubscription(ctx context.Context, id string, subscription *Subscription) (*Subscription, *Response, error) { - u := fmt.Sprintf("notifications/threads/%v/subscription", id) - - req, err := s.client.NewRequest("PUT", u, subscription) - if err != nil { - return nil, nil, err - } - - sub := new(Subscription) - resp, err := s.client.Do(ctx, req, sub) - if err != nil { - return nil, resp, err - } - - return sub, resp, nil -} - -// DeleteThreadSubscription deletes the subscription for the specified thread -// for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/activity/notifications/#delete-a-thread-subscription -func (s *ActivityService) DeleteThreadSubscription(ctx context.Context, id string) (*Response, error) { - u := fmt.Sprintf("notifications/threads/%v/subscription", id) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/activity_star.go b/vendor/github.com/google/go-github/github/activity_star.go deleted file mode 100644 index d5b067127c6..00000000000 --- a/vendor/github.com/google/go-github/github/activity_star.go +++ /dev/null @@ -1,135 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// StarredRepository is returned by ListStarred. -type StarredRepository struct { - StarredAt *Timestamp `json:"starred_at,omitempty"` - Repository *Repository `json:"repo,omitempty"` -} - -// Stargazer represents a user that has starred a repository. -type Stargazer struct { - StarredAt *Timestamp `json:"starred_at,omitempty"` - User *User `json:"user,omitempty"` -} - -// ListStargazers lists people who have starred the specified repo. -// -// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-stargazers -func (s *ActivityService) ListStargazers(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Stargazer, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/stargazers", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeStarringPreview) - - var stargazers []*Stargazer - resp, err := s.client.Do(ctx, req, &stargazers) - if err != nil { - return nil, resp, err - } - - return stargazers, resp, nil -} - -// ActivityListStarredOptions specifies the optional parameters to the -// ActivityService.ListStarred method. -type ActivityListStarredOptions struct { - // How to sort the repository list. Possible values are: created, updated, - // pushed, full_name. Default is "full_name". - Sort string `url:"sort,omitempty"` - - // Direction in which to sort repositories. Possible values are: asc, desc. - // Default is "asc" when sort is "full_name", otherwise default is "desc". - Direction string `url:"direction,omitempty"` - - ListOptions -} - -// ListStarred lists all the repos starred by a user. Passing the empty string -// will list the starred repositories for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/activity/starring/#list-repositories-being-starred -func (s *ActivityService) ListStarred(ctx context.Context, user string, opt *ActivityListStarredOptions) ([]*StarredRepository, *Response, error) { - var u string - if user != "" { - u = fmt.Sprintf("users/%v/starred", user) - } else { - u = "user/starred" - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeStarringPreview) - - var repos []*StarredRepository - resp, err := s.client.Do(ctx, req, &repos) - if err != nil { - return nil, resp, err - } - - return repos, resp, nil -} - -// IsStarred checks if a repository is starred by authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/activity/starring/#check-if-you-are-starring-a-repository -func (s *ActivityService) IsStarred(ctx context.Context, owner, repo string) (bool, *Response, error) { - u := fmt.Sprintf("user/starred/%v/%v", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return false, nil, err - } - resp, err := s.client.Do(ctx, req, nil) - starred, err := parseBoolResponse(err) - return starred, resp, err -} - -// Star a repository as the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/activity/starring/#star-a-repository -func (s *ActivityService) Star(ctx context.Context, owner, repo string) (*Response, error) { - u := fmt.Sprintf("user/starred/%v/%v", owner, repo) - req, err := s.client.NewRequest("PUT", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} - -// Unstar a repository as the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/activity/starring/#unstar-a-repository -func (s *ActivityService) Unstar(ctx context.Context, owner, repo string) (*Response, error) { - u := fmt.Sprintf("user/starred/%v/%v", owner, repo) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/activity_watching.go b/vendor/github.com/google/go-github/github/activity_watching.go deleted file mode 100644 index c749ca86e7c..00000000000 --- a/vendor/github.com/google/go-github/github/activity_watching.go +++ /dev/null @@ -1,146 +0,0 @@ -// Copyright 2014 The go-github 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 github - -import ( - "context" - "fmt" -) - -// Subscription identifies a repository or thread subscription. -type Subscription struct { - Subscribed *bool `json:"subscribed,omitempty"` - Ignored *bool `json:"ignored,omitempty"` - Reason *string `json:"reason,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - URL *string `json:"url,omitempty"` - - // only populated for repository subscriptions - RepositoryURL *string `json:"repository_url,omitempty"` - - // only populated for thread subscriptions - ThreadURL *string `json:"thread_url,omitempty"` -} - -// ListWatchers lists watchers of a particular repo. -// -// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-watchers -func (s *ActivityService) ListWatchers(ctx context.Context, owner, repo string, opt *ListOptions) ([]*User, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/subscribers", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var watchers []*User - resp, err := s.client.Do(ctx, req, &watchers) - if err != nil { - return nil, resp, err - } - - return watchers, resp, nil -} - -// ListWatched lists the repositories the specified user is watching. Passing -// the empty string will fetch watched repos for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/activity/watching/#list-repositories-being-watched -func (s *ActivityService) ListWatched(ctx context.Context, user string, opt *ListOptions) ([]*Repository, *Response, error) { - var u string - if user != "" { - u = fmt.Sprintf("users/%v/subscriptions", user) - } else { - u = "user/subscriptions" - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var watched []*Repository - resp, err := s.client.Do(ctx, req, &watched) - if err != nil { - return nil, resp, err - } - - return watched, resp, nil -} - -// GetRepositorySubscription returns the subscription for the specified -// repository for the authenticated user. If the authenticated user is not -// watching the repository, a nil Subscription is returned. -// -// GitHub API docs: https://developer.github.com/v3/activity/watching/#get-a-repository-subscription -func (s *ActivityService) GetRepositorySubscription(ctx context.Context, owner, repo string) (*Subscription, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - sub := new(Subscription) - resp, err := s.client.Do(ctx, req, sub) - if err != nil { - // if it's just a 404, don't return that as an error - _, err = parseBoolResponse(err) - return nil, resp, err - } - - return sub, resp, nil -} - -// SetRepositorySubscription sets the subscription for the specified repository -// for the authenticated user. -// -// To watch a repository, set subscription.Subscribed to true. -// To ignore notifications made within a repository, set subscription.Ignored to true. -// To stop watching a repository, use DeleteRepositorySubscription. -// -// GitHub API docs: https://developer.github.com/v3/activity/watching/#set-a-repository-subscription -func (s *ActivityService) SetRepositorySubscription(ctx context.Context, owner, repo string, subscription *Subscription) (*Subscription, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) - - req, err := s.client.NewRequest("PUT", u, subscription) - if err != nil { - return nil, nil, err - } - - sub := new(Subscription) - resp, err := s.client.Do(ctx, req, sub) - if err != nil { - return nil, resp, err - } - - return sub, resp, nil -} - -// DeleteRepositorySubscription deletes the subscription for the specified -// repository for the authenticated user. -// -// This is used to stop watching a repository. To control whether or not to -// receive notifications from a repository, use SetRepositorySubscription. -// -// GitHub API docs: https://developer.github.com/v3/activity/watching/#delete-a-repository-subscription -func (s *ActivityService) DeleteRepositorySubscription(ctx context.Context, owner, repo string) (*Response, error) { - u := fmt.Sprintf("repos/%s/%s/subscription", owner, repo) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/admin.go b/vendor/github.com/google/go-github/github/admin.go deleted file mode 100644 index 2d96733a1c7..00000000000 --- a/vendor/github.com/google/go-github/github/admin.go +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2016 The go-github 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 github - -import ( - "context" - "fmt" -) - -// AdminService handles communication with the admin related methods of the -// GitHub API. These API routes are normally only accessible for GitHub -// Enterprise installations. -// -// GitHub API docs: https://developer.github.com/v3/enterprise/ -type AdminService service - -// TeamLDAPMapping represents the mapping between a GitHub team and an LDAP group. -type TeamLDAPMapping struct { - ID *int64 `json:"id,omitempty"` - LDAPDN *string `json:"ldap_dn,omitempty"` - URL *string `json:"url,omitempty"` - Name *string `json:"name,omitempty"` - Slug *string `json:"slug,omitempty"` - Description *string `json:"description,omitempty"` - Privacy *string `json:"privacy,omitempty"` - Permission *string `json:"permission,omitempty"` - - MembersURL *string `json:"members_url,omitempty"` - RepositoriesURL *string `json:"repositories_url,omitempty"` -} - -func (m TeamLDAPMapping) String() string { - return Stringify(m) -} - -// UserLDAPMapping represents the mapping between a GitHub user and an LDAP user. -type UserLDAPMapping struct { - ID *int64 `json:"id,omitempty"` - LDAPDN *string `json:"ldap_dn,omitempty"` - Login *string `json:"login,omitempty"` - AvatarURL *string `json:"avatar_url,omitempty"` - GravatarID *string `json:"gravatar_id,omitempty"` - Type *string `json:"type,omitempty"` - SiteAdmin *bool `json:"site_admin,omitempty"` - - URL *string `json:"url,omitempty"` - EventsURL *string `json:"events_url,omitempty"` - FollowingURL *string `json:"following_url,omitempty"` - FollowersURL *string `json:"followers_url,omitempty"` - GistsURL *string `json:"gists_url,omitempty"` - OrganizationsURL *string `json:"organizations_url,omitempty"` - ReceivedEventsURL *string `json:"received_events_url,omitempty"` - ReposURL *string `json:"repos_url,omitempty"` - StarredURL *string `json:"starred_url,omitempty"` - SubscriptionsURL *string `json:"subscriptions_url,omitempty"` -} - -func (m UserLDAPMapping) String() string { - return Stringify(m) -} - -// UpdateUserLDAPMapping updates the mapping between a GitHub user and an LDAP user. -// -// GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-user -func (s *AdminService) UpdateUserLDAPMapping(ctx context.Context, user string, mapping *UserLDAPMapping) (*UserLDAPMapping, *Response, error) { - u := fmt.Sprintf("admin/ldap/users/%v/mapping", user) - req, err := s.client.NewRequest("PATCH", u, mapping) - if err != nil { - return nil, nil, err - } - - m := new(UserLDAPMapping) - resp, err := s.client.Do(ctx, req, m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// UpdateTeamLDAPMapping updates the mapping between a GitHub team and an LDAP group. -// -// GitHub API docs: https://developer.github.com/v3/enterprise/ldap/#update-ldap-mapping-for-a-team -func (s *AdminService) UpdateTeamLDAPMapping(ctx context.Context, team int64, mapping *TeamLDAPMapping) (*TeamLDAPMapping, *Response, error) { - u := fmt.Sprintf("admin/ldap/teams/%v/mapping", team) - req, err := s.client.NewRequest("PATCH", u, mapping) - if err != nil { - return nil, nil, err - } - - m := new(TeamLDAPMapping) - resp, err := s.client.Do(ctx, req, m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/admin_stats.go b/vendor/github.com/google/go-github/github/admin_stats.go deleted file mode 100644 index 1550d250ef0..00000000000 --- a/vendor/github.com/google/go-github/github/admin_stats.go +++ /dev/null @@ -1,171 +0,0 @@ -// Copyright 2017 The go-github 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 github - -import ( - "context" - "fmt" -) - -// AdminStats represents a variety of stats of a Github Enterprise -// installation. -type AdminStats struct { - Issues *IssueStats `json:"issues,omitempty"` - Hooks *HookStats `json:"hooks,omitempty"` - Milestones *MilestoneStats `json:"milestones,omitempty"` - Orgs *OrgStats `json:"orgs,omitempty"` - Comments *CommentStats `json:"comments,omitempty"` - Pages *PageStats `json:"pages,omitempty"` - Users *UserStats `json:"users,omitempty"` - Gists *GistStats `json:"gists,omitempty"` - Pulls *PullStats `json:"pulls,omitempty"` - Repos *RepoStats `json:"repos,omitempty"` -} - -func (s AdminStats) String() string { - return Stringify(s) -} - -// IssueStats represents the number of total, open and closed issues. -type IssueStats struct { - TotalIssues *int `json:"total_issues,omitempty"` - OpenIssues *int `json:"open_issues,omitempty"` - ClosedIssues *int `json:"closed_issues,omitempty"` -} - -func (s IssueStats) String() string { - return Stringify(s) -} - -// HookStats represents the number of total, active and inactive hooks. -type HookStats struct { - TotalHooks *int `json:"total_hooks,omitempty"` - ActiveHooks *int `json:"active_hooks,omitempty"` - InactiveHooks *int `json:"inactive_hooks,omitempty"` -} - -func (s HookStats) String() string { - return Stringify(s) -} - -// MilestoneStats represents the number of total, open and close milestones. -type MilestoneStats struct { - TotalMilestones *int `json:"total_milestones,omitempty"` - OpenMilestones *int `json:"open_milestones,omitempty"` - ClosedMilestones *int `json:"closed_milestones,omitempty"` -} - -func (s MilestoneStats) String() string { - return Stringify(s) -} - -// OrgStats represents the number of total, disabled organizations and the team -// and team member count. -type OrgStats struct { - TotalOrgs *int `json:"total_orgs,omitempty"` - DisabledOrgs *int `json:"disabled_orgs,omitempty"` - TotalTeams *int `json:"total_teams,omitempty"` - TotalTeamMembers *int `json:"total_team_members,omitempty"` -} - -func (s OrgStats) String() string { - return Stringify(s) -} - -// CommentStats represents the number of total comments on commits, gists, issues -// and pull requests. -type CommentStats struct { - TotalCommitComments *int `json:"total_commit_comments,omitempty"` - TotalGistComments *int `json:"total_gist_comments,omitempty"` - TotalIssueComments *int `json:"total_issue_comments,omitempty"` - TotalPullRequestComments *int `json:"total_pull_request_comments,omitempty"` -} - -func (s CommentStats) String() string { - return Stringify(s) -} - -// PageStats represents the total number of github pages. -type PageStats struct { - TotalPages *int `json:"total_pages,omitempty"` -} - -func (s PageStats) String() string { - return Stringify(s) -} - -// UserStats represents the number of total, admin and suspended users. -type UserStats struct { - TotalUsers *int `json:"total_users,omitempty"` - AdminUsers *int `json:"admin_users,omitempty"` - SuspendedUsers *int `json:"suspended_users,omitempty"` -} - -func (s UserStats) String() string { - return Stringify(s) -} - -//GistStats represents the number of total, private and public gists. -type GistStats struct { - TotalGists *int `json:"total_gists,omitempty"` - PrivateGists *int `json:"private_gists,omitempty"` - PublicGists *int `json:"public_gists,omitempty"` -} - -func (s GistStats) String() string { - return Stringify(s) -} - -// PullStats represents the number of total, merged, mergable and unmergeable -// pull-requests. -type PullStats struct { - TotalPulls *int `json:"total_pulls,omitempty"` - MergedPulls *int `json:"merged_pulls,omitempty"` - MergablePulls *int `json:"mergeable_pulls,omitempty"` - UnmergablePulls *int `json:"unmergeable_pulls,omitempty"` -} - -func (s PullStats) String() string { - return Stringify(s) -} - -// RepoStats represents the number of total, root, fork, organization repositories -// together with the total number of pushes and wikis. -type RepoStats struct { - TotalRepos *int `json:"total_repos,omitempty"` - RootRepos *int `json:"root_repos,omitempty"` - ForkRepos *int `json:"fork_repos,omitempty"` - OrgRepos *int `json:"org_repos,omitempty"` - TotalPushes *int `json:"total_pushes,omitempty"` - TotalWikis *int `json:"total_wikis,omitempty"` -} - -func (s RepoStats) String() string { - return Stringify(s) -} - -// GetAdminStats returns a variety of metrics about a Github Enterprise -// installation. -// -// Please note that this is only available to site administrators, -// otherwise it will error with a 404 not found (instead of 401 or 403). -// -// GitHub API docs: https://developer.github.com/v3/enterprise-admin/admin_stats/ -func (s *AdminService) GetAdminStats(ctx context.Context) (*AdminStats, *Response, error) { - u := fmt.Sprintf("enterprise/stats/all") - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - m := new(AdminStats) - resp, err := s.client.Do(ctx, req, m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/apps.go b/vendor/github.com/google/go-github/github/apps.go deleted file mode 100644 index 740642e62f6..00000000000 --- a/vendor/github.com/google/go-github/github/apps.go +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright 2016 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// AppsService provides access to the installation related functions -// in the GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/apps/ -type AppsService service - -// App represents a GitHub App. -type App struct { - ID *int64 `json:"id,omitempty"` - Owner *User `json:"owner,omitempty"` - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - ExternalURL *string `json:"external_url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` -} - -// InstallationToken represents an installation token. -type InstallationToken struct { - Token *string `json:"token,omitempty"` - ExpiresAt *time.Time `json:"expires_at,omitempty"` -} - -// Get a single GitHub App. Passing the empty string will get -// the authenticated GitHub App. -// -// Note: appSlug is just the URL-friendly name of your GitHub App. -// You can find this on the settings page for your GitHub App -// (e.g., https://github.com/settings/apps/:app_slug). -// -// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-github-app -func (s *AppsService) Get(ctx context.Context, appSlug string) (*App, *Response, error) { - var u string - if appSlug != "" { - u = fmt.Sprintf("apps/%v", appSlug) - } else { - u = "app" - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) - - app := new(App) - resp, err := s.client.Do(ctx, req, app) - if err != nil { - return nil, resp, err - } - - return app, resp, nil -} - -// ListInstallations lists the installations that the current GitHub App has. -// -// GitHub API docs: https://developer.github.com/v3/apps/#find-installations -func (s *AppsService) ListInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) { - u, err := addOptions("app/installations", opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) - - var i []*Installation - resp, err := s.client.Do(ctx, req, &i) - if err != nil { - return nil, resp, err - } - - return i, resp, nil -} - -// GetInstallation returns the specified installation. -// -// GitHub API docs: https://developer.github.com/v3/apps/#get-a-single-installation -func (s *AppsService) GetInstallation(ctx context.Context, id int64) (*Installation, *Response, error) { - u := fmt.Sprintf("app/installations/%v", id) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) - - i := new(Installation) - resp, err := s.client.Do(ctx, req, i) - if err != nil { - return nil, resp, err - } - - return i, resp, nil -} - -// ListUserInstallations lists installations that are accessible to the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/apps/#list-installations-for-user -func (s *AppsService) ListUserInstallations(ctx context.Context, opt *ListOptions) ([]*Installation, *Response, error) { - u, err := addOptions("user/installations", opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) - - var i struct { - Installations []*Installation `json:"installations"` - } - resp, err := s.client.Do(ctx, req, &i) - if err != nil { - return nil, resp, err - } - - return i.Installations, resp, nil -} - -// CreateInstallationToken creates a new installation token. -// -// GitHub API docs: https://developer.github.com/v3/apps/#create-a-new-installation-token -func (s *AppsService) CreateInstallationToken(ctx context.Context, id int64) (*InstallationToken, *Response, error) { - u := fmt.Sprintf("installations/%v/access_tokens", id) - - req, err := s.client.NewRequest("POST", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) - - t := new(InstallationToken) - resp, err := s.client.Do(ctx, req, t) - if err != nil { - return nil, resp, err - } - - return t, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/apps_installation.go b/vendor/github.com/google/go-github/github/apps_installation.go deleted file mode 100644 index af85cb87f19..00000000000 --- a/vendor/github.com/google/go-github/github/apps_installation.go +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2016 The go-github 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 github - -import ( - "context" - "fmt" -) - -// Installation represents a GitHub Apps installation. -type Installation struct { - ID *int64 `json:"id,omitempty"` - Account *User `json:"account,omitempty"` - AccessTokensURL *string `json:"access_tokens_url,omitempty"` - RepositoriesURL *string `json:"repositories_url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` -} - -func (i Installation) String() string { - return Stringify(i) -} - -// ListRepos lists the repositories that are accessible to the authenticated installation. -// -// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-repositories -func (s *AppsService) ListRepos(ctx context.Context, opt *ListOptions) ([]*Repository, *Response, error) { - u, err := addOptions("installation/repositories", opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) - - var r struct { - Repositories []*Repository `json:"repositories"` - } - resp, err := s.client.Do(ctx, req, &r) - if err != nil { - return nil, resp, err - } - - return r.Repositories, resp, nil -} - -// ListUserRepos lists repositories that are accessible -// to the authenticated user for an installation. -// -// GitHub API docs: https://developer.github.com/v3/apps/installations/#list-repositories-accessible-to-the-user-for-an-installation -func (s *AppsService) ListUserRepos(ctx context.Context, id int64, opt *ListOptions) ([]*Repository, *Response, error) { - u := fmt.Sprintf("user/installations/%v/repositories", id) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeIntegrationPreview) - - var r struct { - Repositories []*Repository `json:"repositories"` - } - resp, err := s.client.Do(ctx, req, &r) - if err != nil { - return nil, resp, err - } - - return r.Repositories, resp, nil -} - -// AddRepository adds a single repository to an installation. -// -// GitHub API docs: https://developer.github.com/v3/apps/installations/#add-repository-to-installation -func (s *AppsService) AddRepository(ctx context.Context, instID, repoID int64) (*Repository, *Response, error) { - u := fmt.Sprintf("apps/installations/%v/repositories/%v", instID, repoID) - req, err := s.client.NewRequest("PUT", u, nil) - if err != nil { - return nil, nil, err - } - - r := new(Repository) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, nil -} - -// RemoveRepository removes a single repository from an installation. -// -// GitHub docs: https://developer.github.com/v3/apps/installations/#remove-repository-from-installation -func (s *AppsService) RemoveRepository(ctx context.Context, instID, repoID int64) (*Response, error) { - u := fmt.Sprintf("apps/installations/%v/repositories/%v", instID, repoID) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/apps_marketplace.go b/vendor/github.com/google/go-github/github/apps_marketplace.go deleted file mode 100644 index 089cdbf7ee1..00000000000 --- a/vendor/github.com/google/go-github/github/apps_marketplace.go +++ /dev/null @@ -1,180 +0,0 @@ -// Copyright 2017 The go-github 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 github - -import ( - "context" - "fmt" -) - -// MarketplaceService handles communication with the marketplace related -// methods of the GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/apps/marketplace/ -type MarketplaceService struct { - client *Client - // Stubbed controls whether endpoints that return stubbed data are used - // instead of production endpoints. Stubbed data is fake data that's useful - // for testing your GitHub Apps. Stubbed data is hard-coded and will not - // change based on actual subscriptions. - // - // GitHub API docs: https://developer.github.com/v3/apps/marketplace/ - Stubbed bool -} - -// MarketplacePlan represents a GitHub Apps Marketplace Listing Plan. -type MarketplacePlan struct { - URL *string `json:"url,omitempty"` - AccountsURL *string `json:"accounts_url,omitempty"` - ID *int64 `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - MonthlyPriceInCents *int `json:"monthly_price_in_cents,omitempty"` - YearlyPriceInCents *int `json:"yearly_price_in_cents,omitempty"` - PriceModel *string `json:"price_model,omitempty"` - UnitName *string `json:"unit_name,omitempty"` - Bullets *[]string `json:"bullets,omitempty"` -} - -// MarketplacePurchase represents a GitHub Apps Marketplace Purchase. -type MarketplacePurchase struct { - BillingCycle *string `json:"billing_cycle,omitempty"` - NextBillingDate *string `json:"next_billing_date,omitempty"` - UnitCount *int `json:"unit_count,omitempty"` - Plan *MarketplacePlan `json:"plan,omitempty"` - Account *MarketplacePlanAccount `json:"account,omitempty"` -} - -// MarketplacePlanAccount represents a GitHub Account (user or organization) on a specific plan. -type MarketplacePlanAccount struct { - URL *string `json:"url,omitempty"` - Type *string `json:"type,omitempty"` - ID *int64 `json:"id,omitempty"` - Login *string `json:"login,omitempty"` - Email *string `json:"email,omitempty"` - OrganizationBillingEmail *string `json:"organization_billing_email,omitempty"` - MarketplacePurchase *MarketplacePurchase `json:"marketplace_purchase,omitempty"` -} - -// ListPlans lists all plans for your Marketplace listing. -// -// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-plans-for-your-marketplace-listing -func (s *MarketplaceService) ListPlans(ctx context.Context, opt *ListOptions) ([]*MarketplacePlan, *Response, error) { - uri := s.marketplaceURI("plans") - u, err := addOptions(uri, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeMarketplacePreview) - - var plans []*MarketplacePlan - resp, err := s.client.Do(ctx, req, &plans) - if err != nil { - return nil, resp, err - } - - return plans, resp, nil -} - -// ListPlanAccountsForPlan lists all GitHub accounts (user or organization) on a specific plan. -// -// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#list-all-github-accounts-user-or-organization-on-a-specific-plan -func (s *MarketplaceService) ListPlanAccountsForPlan(ctx context.Context, planID int64, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) { - uri := s.marketplaceURI(fmt.Sprintf("plans/%v/accounts", planID)) - u, err := addOptions(uri, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeMarketplacePreview) - - var accounts []*MarketplacePlanAccount - resp, err := s.client.Do(ctx, req, &accounts) - if err != nil { - return nil, resp, err - } - - return accounts, resp, nil -} - -// ListPlanAccountsForAccount lists all GitHub accounts (user or organization) associated with an account. -// -// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#check-if-a-github-account-is-associated-with-any-marketplace-listing -func (s *MarketplaceService) ListPlanAccountsForAccount(ctx context.Context, accountID int64, opt *ListOptions) ([]*MarketplacePlanAccount, *Response, error) { - uri := s.marketplaceURI(fmt.Sprintf("accounts/%v", accountID)) - u, err := addOptions(uri, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeMarketplacePreview) - - var accounts []*MarketplacePlanAccount - resp, err := s.client.Do(ctx, req, &accounts) - if err != nil { - return nil, resp, err - } - - return accounts, resp, nil -} - -// ListMarketplacePurchasesForUser lists all GitHub marketplace purchases made by a user. -// -// GitHub API docs: https://developer.github.com/v3/apps/marketplace/#get-a-users-marketplace-purchases -func (s *MarketplaceService) ListMarketplacePurchasesForUser(ctx context.Context, opt *ListOptions) ([]*MarketplacePurchase, *Response, error) { - uri := "user/marketplace_purchases" - if s.Stubbed { - uri = "user/marketplace_purchases/stubbed" - } - - u, err := addOptions(uri, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeMarketplacePreview) - - var purchases []*MarketplacePurchase - resp, err := s.client.Do(ctx, req, &purchases) - if err != nil { - return nil, resp, err - } - - return purchases, resp, nil -} - -func (s *MarketplaceService) marketplaceURI(endpoint string) string { - url := "marketplace_listing" - if s.Stubbed { - url = "marketplace_listing/stubbed" - } - return url + "/" + endpoint -} diff --git a/vendor/github.com/google/go-github/github/authorizations.go b/vendor/github.com/google/go-github/github/authorizations.go deleted file mode 100644 index 190205b02ac..00000000000 --- a/vendor/github.com/google/go-github/github/authorizations.go +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright 2015 The go-github 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 github - -import ( - "context" - "fmt" -) - -// Scope models a GitHub authorization scope. -// -// GitHub API docs: https://developer.github.com/v3/oauth/#scopes -type Scope string - -// This is the set of scopes for GitHub API V3 -const ( - ScopeNone Scope = "(no scope)" // REVISIT: is this actually returned, or just a documentation artifact? - ScopeUser Scope = "user" - ScopeUserEmail Scope = "user:email" - ScopeUserFollow Scope = "user:follow" - ScopePublicRepo Scope = "public_repo" - ScopeRepo Scope = "repo" - ScopeRepoDeployment Scope = "repo_deployment" - ScopeRepoStatus Scope = "repo:status" - ScopeDeleteRepo Scope = "delete_repo" - ScopeNotifications Scope = "notifications" - ScopeGist Scope = "gist" - ScopeReadRepoHook Scope = "read:repo_hook" - ScopeWriteRepoHook Scope = "write:repo_hook" - ScopeAdminRepoHook Scope = "admin:repo_hook" - ScopeAdminOrgHook Scope = "admin:org_hook" - ScopeReadOrg Scope = "read:org" - ScopeWriteOrg Scope = "write:org" - ScopeAdminOrg Scope = "admin:org" - ScopeReadPublicKey Scope = "read:public_key" - ScopeWritePublicKey Scope = "write:public_key" - ScopeAdminPublicKey Scope = "admin:public_key" - ScopeReadGPGKey Scope = "read:gpg_key" - ScopeWriteGPGKey Scope = "write:gpg_key" - ScopeAdminGPGKey Scope = "admin:gpg_key" -) - -// AuthorizationsService handles communication with the authorization related -// methods of the GitHub API. -// -// This service requires HTTP Basic Authentication; it cannot be accessed using -// an OAuth token. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/ -type AuthorizationsService service - -// Authorization represents an individual GitHub authorization. -type Authorization struct { - ID *int64 `json:"id,omitempty"` - URL *string `json:"url,omitempty"` - Scopes []Scope `json:"scopes,omitempty"` - Token *string `json:"token,omitempty"` - TokenLastEight *string `json:"token_last_eight,omitempty"` - HashedToken *string `json:"hashed_token,omitempty"` - App *AuthorizationApp `json:"app,omitempty"` - Note *string `json:"note,omitempty"` - NoteURL *string `json:"note_url,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - Fingerprint *string `json:"fingerprint,omitempty"` - - // User is only populated by the Check and Reset methods. - User *User `json:"user,omitempty"` -} - -func (a Authorization) String() string { - return Stringify(a) -} - -// AuthorizationApp represents an individual GitHub app (in the context of authorization). -type AuthorizationApp struct { - URL *string `json:"url,omitempty"` - Name *string `json:"name,omitempty"` - ClientID *string `json:"client_id,omitempty"` -} - -func (a AuthorizationApp) String() string { - return Stringify(a) -} - -// Grant represents an OAuth application that has been granted access to an account. -type Grant struct { - ID *int64 `json:"id,omitempty"` - URL *string `json:"url,omitempty"` - App *AuthorizationApp `json:"app,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - Scopes []string `json:"scopes,omitempty"` -} - -func (g Grant) String() string { - return Stringify(g) -} - -// AuthorizationRequest represents a request to create an authorization. -type AuthorizationRequest struct { - Scopes []Scope `json:"scopes,omitempty"` - Note *string `json:"note,omitempty"` - NoteURL *string `json:"note_url,omitempty"` - ClientID *string `json:"client_id,omitempty"` - ClientSecret *string `json:"client_secret,omitempty"` - Fingerprint *string `json:"fingerprint,omitempty"` -} - -func (a AuthorizationRequest) String() string { - return Stringify(a) -} - -// AuthorizationUpdateRequest represents a request to update an authorization. -// -// Note that for any one update, you must only provide one of the "scopes" -// fields. That is, you may provide only one of "Scopes", or "AddScopes", or -// "RemoveScopes". -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization -type AuthorizationUpdateRequest struct { - Scopes []string `json:"scopes,omitempty"` - AddScopes []string `json:"add_scopes,omitempty"` - RemoveScopes []string `json:"remove_scopes,omitempty"` - Note *string `json:"note,omitempty"` - NoteURL *string `json:"note_url,omitempty"` - Fingerprint *string `json:"fingerprint,omitempty"` -} - -func (a AuthorizationUpdateRequest) String() string { - return Stringify(a) -} - -// List the authorizations for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-authorizations -func (s *AuthorizationsService) List(ctx context.Context, opt *ListOptions) ([]*Authorization, *Response, error) { - u := "authorizations" - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var auths []*Authorization - resp, err := s.client.Do(ctx, req, &auths) - if err != nil { - return nil, resp, err - } - return auths, resp, nil -} - -// Get a single authorization. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-authorization -func (s *AuthorizationsService) Get(ctx context.Context, id int64) (*Authorization, *Response, error) { - u := fmt.Sprintf("authorizations/%d", id) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) - if err != nil { - return nil, resp, err - } - return a, resp, nil -} - -// Create a new authorization for the specified OAuth application. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#create-a-new-authorization -func (s *AuthorizationsService) Create(ctx context.Context, auth *AuthorizationRequest) (*Authorization, *Response, error) { - u := "authorizations" - - req, err := s.client.NewRequest("POST", u, auth) - if err != nil { - return nil, nil, err - } - - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) - if err != nil { - return nil, resp, err - } - return a, resp, nil -} - -// GetOrCreateForApp creates a new authorization for the specified OAuth -// application, only if an authorization for that application doesn’t already -// exist for the user. -// -// If a new token is created, the HTTP status code will be "201 Created", and -// the returned Authorization.Token field will be populated. If an existing -// token is returned, the status code will be "200 OK" and the -// Authorization.Token field will be empty. -// -// clientID is the OAuth Client ID with which to create the token. -// -// GitHub API docs: -// https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app -// https://developer.github.com/v3/oauth_authorizations/#get-or-create-an-authorization-for-a-specific-app-and-fingerprint -func (s *AuthorizationsService) GetOrCreateForApp(ctx context.Context, clientID string, auth *AuthorizationRequest) (*Authorization, *Response, error) { - var u string - if auth.Fingerprint == nil || *auth.Fingerprint == "" { - u = fmt.Sprintf("authorizations/clients/%v", clientID) - } else { - u = fmt.Sprintf("authorizations/clients/%v/%v", clientID, *auth.Fingerprint) - } - - req, err := s.client.NewRequest("PUT", u, auth) - if err != nil { - return nil, nil, err - } - - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) - if err != nil { - return nil, resp, err - } - - return a, resp, nil -} - -// Edit a single authorization. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#update-an-existing-authorization -func (s *AuthorizationsService) Edit(ctx context.Context, id int64, auth *AuthorizationUpdateRequest) (*Authorization, *Response, error) { - u := fmt.Sprintf("authorizations/%d", id) - - req, err := s.client.NewRequest("PATCH", u, auth) - if err != nil { - return nil, nil, err - } - - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) - if err != nil { - return nil, resp, err - } - - return a, resp, nil -} - -// Delete a single authorization. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-an-authorization -func (s *AuthorizationsService) Delete(ctx context.Context, id int64) (*Response, error) { - u := fmt.Sprintf("authorizations/%d", id) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// Check if an OAuth token is valid for a specific app. -// -// Note that this operation requires the use of BasicAuth, but where the -// username is the OAuth application clientID, and the password is its -// clientSecret. Invalid tokens will return a 404 Not Found. -// -// The returned Authorization.User field will be populated. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#check-an-authorization -func (s *AuthorizationsService) Check(ctx context.Context, clientID string, token string) (*Authorization, *Response, error) { - u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) - if err != nil { - return nil, resp, err - } - - return a, resp, nil -} - -// Reset is used to reset a valid OAuth token without end user involvement. -// Applications must save the "token" property in the response, because changes -// take effect immediately. -// -// Note that this operation requires the use of BasicAuth, but where the -// username is the OAuth application clientID, and the password is its -// clientSecret. Invalid tokens will return a 404 Not Found. -// -// The returned Authorization.User field will be populated. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#reset-an-authorization -func (s *AuthorizationsService) Reset(ctx context.Context, clientID string, token string) (*Authorization, *Response, error) { - u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) - - req, err := s.client.NewRequest("POST", u, nil) - if err != nil { - return nil, nil, err - } - - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) - if err != nil { - return nil, resp, err - } - - return a, resp, nil -} - -// Revoke an authorization for an application. -// -// Note that this operation requires the use of BasicAuth, but where the -// username is the OAuth application clientID, and the password is its -// clientSecret. Invalid tokens will return a 404 Not Found. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#revoke-an-authorization-for-an-application -func (s *AuthorizationsService) Revoke(ctx context.Context, clientID string, token string) (*Response, error) { - u := fmt.Sprintf("applications/%v/tokens/%v", clientID, token) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// ListGrants lists the set of OAuth applications that have been granted -// access to a user's account. This will return one entry for each application -// that has been granted access to the account, regardless of the number of -// tokens an application has generated for the user. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#list-your-grants -func (s *AuthorizationsService) ListGrants(ctx context.Context, opt *ListOptions) ([]*Grant, *Response, error) { - u, err := addOptions("applications/grants", opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - grants := []*Grant{} - resp, err := s.client.Do(ctx, req, &grants) - if err != nil { - return nil, resp, err - } - - return grants, resp, nil -} - -// GetGrant gets a single OAuth application grant. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#get-a-single-grant -func (s *AuthorizationsService) GetGrant(ctx context.Context, id int64) (*Grant, *Response, error) { - u := fmt.Sprintf("applications/grants/%d", id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - grant := new(Grant) - resp, err := s.client.Do(ctx, req, grant) - if err != nil { - return nil, resp, err - } - - return grant, resp, nil -} - -// DeleteGrant deletes an OAuth application grant. Deleting an application's -// grant will also delete all OAuth tokens associated with the application for -// the user. -// -// GitHub API docs: https://developer.github.com/v3/oauth_authorizations/#delete-a-grant -func (s *AuthorizationsService) DeleteGrant(ctx context.Context, id int64) (*Response, error) { - u := fmt.Sprintf("applications/grants/%d", id) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// CreateImpersonation creates an impersonation OAuth token. -// -// This requires admin permissions. With the returned Authorization.Token -// you can e.g. create or delete a user's public SSH key. NOTE: creating a -// new token automatically revokes an existing one. -// -// GitHub API docs: https://developer.github.com/enterprise/2.5/v3/users/administration/#create-an-impersonation-oauth-token -func (s *AuthorizationsService) CreateImpersonation(ctx context.Context, username string, authReq *AuthorizationRequest) (*Authorization, *Response, error) { - u := fmt.Sprintf("admin/users/%v/authorizations", username) - req, err := s.client.NewRequest("POST", u, authReq) - if err != nil { - return nil, nil, err - } - - a := new(Authorization) - resp, err := s.client.Do(ctx, req, a) - if err != nil { - return nil, resp, err - } - return a, resp, nil -} - -// DeleteImpersonation deletes an impersonation OAuth token. -// -// NOTE: there can be only one at a time. -// -// GitHub API docs: https://developer.github.com/enterprise/2.5/v3/users/administration/#delete-an-impersonation-oauth-token -func (s *AuthorizationsService) DeleteImpersonation(ctx context.Context, username string) (*Response, error) { - u := fmt.Sprintf("admin/users/%v/authorizations", username) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/doc.go b/vendor/github.com/google/go-github/github/doc.go deleted file mode 100644 index 4ba03cb3ca4..00000000000 --- a/vendor/github.com/google/go-github/github/doc.go +++ /dev/null @@ -1,191 +0,0 @@ -// Copyright 2013 The go-github 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 github provides a client for using the GitHub API. - -Usage: - - import "github.com/google/go-github/github" - -Construct a new GitHub client, then use the various services on the client to -access different parts of the GitHub API. For example: - - client := github.NewClient(nil) - - // list all organizations for user "willnorris" - orgs, _, err := client.Organizations.List(ctx, "willnorris", nil) - -Some API methods have optional parameters that can be passed. For example: - - client := github.NewClient(nil) - - // list public repositories for org "github" - opt := &github.RepositoryListByOrgOptions{Type: "public"} - repos, _, err := client.Repositories.ListByOrg(ctx, "github", opt) - -The services of a client divide the API into logical chunks and correspond to -the structure of the GitHub API documentation at -https://developer.github.com/v3/. - -Authentication - -The go-github library does not directly handle authentication. Instead, when -creating a new client, pass an http.Client that can handle authentication for -you. The easiest and recommended way to do this is using the golang.org/x/oauth2 -library, but you can always use any other library that provides an http.Client. -If you have an OAuth2 access token (for example, a personal API token), you can -use it with the oauth2 library using: - - import "golang.org/x/oauth2" - - func main() { - ctx := context.Background() - ts := oauth2.StaticTokenSource( - &oauth2.Token{AccessToken: "... your access token ..."}, - ) - tc := oauth2.NewClient(ctx, ts) - - client := github.NewClient(tc) - - // list all repositories for the authenticated user - repos, _, err := client.Repositories.List(ctx, "", nil) - } - -Note that when using an authenticated Client, all calls made by the client will -include the specified OAuth token. Therefore, authenticated clients should -almost never be shared between different users. - -See the oauth2 docs for complete instructions on using that library. - -For API methods that require HTTP Basic Authentication, use the -BasicAuthTransport. - -GitHub Apps authentication can be provided by the -https://github.com/bradleyfalzon/ghinstallation package. - - import "github.com/bradleyfalzon/ghinstallation" - - func main() { - // Wrap the shared transport for use with the integration ID 1 authenticating with installation ID 99. - itr, err := ghinstallation.NewKeyFromFile(http.DefaultTransport, 1, 99, "2016-10-19.private-key.pem") - if err != nil { - // Handle error. - } - - // Use installation transport with client - client := github.NewClient(&http.Client{Transport: itr}) - - // Use client... - } - -Rate Limiting - -GitHub imposes a rate limit on all API clients. Unauthenticated clients are -limited to 60 requests per hour, while authenticated clients can make up to -5,000 requests per hour. The Search API has a custom rate limit. Unauthenticated -clients are limited to 10 requests per minute, while authenticated clients -can make up to 30 requests per minute. To receive the higher rate limit when -making calls that are not issued on behalf of a user, -use UnauthenticatedRateLimitedTransport. - -The returned Response.Rate value contains the rate limit information -from the most recent API call. If a recent enough response isn't -available, you can use RateLimits to fetch the most up-to-date rate -limit data for the client. - -To detect an API rate limit error, you can check if its type is *github.RateLimitError: - - repos, _, err := client.Repositories.List(ctx, "", nil) - if _, ok := err.(*github.RateLimitError); ok { - log.Println("hit rate limit") - } - -Learn more about GitHub rate limiting at -https://developer.github.com/v3/#rate-limiting. - -Accepted Status - -Some endpoints may return a 202 Accepted status code, meaning that the -information required is not yet ready and was scheduled to be gathered on -the GitHub side. Methods known to behave like this are documented specifying -this behavior. - -To detect this condition of error, you can check if its type is -*github.AcceptedError: - - stats, _, err := client.Repositories.ListContributorsStats(ctx, org, repo) - if _, ok := err.(*github.AcceptedError); ok { - log.Println("scheduled on GitHub side") - } - -Conditional Requests - -The GitHub API has good support for conditional requests which will help -prevent you from burning through your rate limit, as well as help speed up your -application. go-github does not handle conditional requests directly, but is -instead designed to work with a caching http.Transport. We recommend using -https://github.com/gregjones/httpcache for that. - -Learn more about GitHub conditional requests at -https://developer.github.com/v3/#conditional-requests. - -Creating and Updating Resources - -All structs for GitHub resources use pointer values for all non-repeated fields. -This allows distinguishing between unset fields and those set to a zero-value. -Helper functions have been provided to easily create these pointers for string, -bool, and int values. For example: - - // create a new private repository named "foo" - repo := &github.Repository{ - Name: github.String("foo"), - Private: github.Bool(true), - } - client.Repositories.Create(ctx, "", repo) - -Users who have worked with protocol buffers should find this pattern familiar. - -Pagination - -All requests for resource collections (repos, pull requests, issues, etc.) -support pagination. Pagination options are described in the -github.ListOptions struct and passed to the list methods directly or as an -embedded type of a more specific list options struct (for example -github.PullRequestListOptions). Pages information is available via the -github.Response struct. - - client := github.NewClient(nil) - - opt := &github.RepositoryListByOrgOptions{ - ListOptions: github.ListOptions{PerPage: 10}, - } - // get all pages of results - var allRepos []*github.Repository - for { - repos, resp, err := client.Repositories.ListByOrg(ctx, "github", opt) - if err != nil { - return err - } - allRepos = append(allRepos, repos...) - if resp.NextPage == 0 { - break - } - opt.Page = resp.NextPage - } - -Google App Engine - -Go on App Engine Classic (which as of this writing uses Go 1.6) can not use -the "context" import and still relies on "golang.org/x/net/context". -As a result, if you wish to continue to use "go-github" on App Engine Classic, -you will need to rewrite all the "context" imports using the following command: - - gofmt -w -r '"context" -> "golang.org/x/net/context"' *.go - -See "with_appengine.go" for more details. - -*/ -package github diff --git a/vendor/github.com/google/go-github/github/event_types.go b/vendor/github.com/google/go-github/github/event_types.go deleted file mode 100644 index 046ba51395c..00000000000 --- a/vendor/github.com/google/go-github/github/event_types.go +++ /dev/null @@ -1,748 +0,0 @@ -// Copyright 2016 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// These event types are shared between the Events API and used as Webhook payloads. - -package github - -// CommitCommentEvent is triggered when a commit comment is created. -// The Webhook event name is "commit_comment". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#commitcommentevent -type CommitCommentEvent struct { - Comment *RepositoryComment `json:"comment,omitempty"` - - // The following fields are only populated by Webhook events. - Action *string `json:"action,omitempty"` - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// CreateEvent represents a created repository, branch, or tag. -// The Webhook event name is "create". -// -// Note: webhooks will not receive this event for created repositories. -// Additionally, webhooks will not receive this event for tags if more -// than three tags are pushed at once. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#createevent -type CreateEvent struct { - Ref *string `json:"ref,omitempty"` - // RefType is the object that was created. Possible values are: "repository", "branch", "tag". - RefType *string `json:"ref_type,omitempty"` - MasterBranch *string `json:"master_branch,omitempty"` - Description *string `json:"description,omitempty"` - - // The following fields are only populated by Webhook events. - PusherType *string `json:"pusher_type,omitempty"` - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// DeleteEvent represents a deleted branch or tag. -// The Webhook event name is "delete". -// -// Note: webhooks will not receive this event for tags if more than three tags -// are deleted at once. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deleteevent -type DeleteEvent struct { - Ref *string `json:"ref,omitempty"` - // RefType is the object that was deleted. Possible values are: "branch", "tag". - RefType *string `json:"ref_type,omitempty"` - - // The following fields are only populated by Webhook events. - PusherType *string `json:"pusher_type,omitempty"` - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// DeploymentEvent represents a deployment. -// The Webhook event name is "deployment". -// -// Events of this type are not visible in timelines, they are only used to trigger hooks. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploymentevent -type DeploymentEvent struct { - Deployment *Deployment `json:"deployment,omitempty"` - Repo *Repository `json:"repository,omitempty"` - - // The following fields are only populated by Webhook events. - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// DeploymentStatusEvent represents a deployment status. -// The Webhook event name is "deployment_status". -// -// Events of this type are not visible in timelines, they are only used to trigger hooks. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#deploymentstatusevent -type DeploymentStatusEvent struct { - Deployment *Deployment `json:"deployment,omitempty"` - DeploymentStatus *DeploymentStatus `json:"deployment_status,omitempty"` - Repo *Repository `json:"repository,omitempty"` - - // The following fields are only populated by Webhook events. - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// ForkEvent is triggered when a user forks a repository. -// The Webhook event name is "fork". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#forkevent -type ForkEvent struct { - // Forkee is the created repository. - Forkee *Repository `json:"forkee,omitempty"` - - // The following fields are only populated by Webhook events. - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// Page represents a single Wiki page. -type Page struct { - PageName *string `json:"page_name,omitempty"` - Title *string `json:"title,omitempty"` - Summary *string `json:"summary,omitempty"` - Action *string `json:"action,omitempty"` - SHA *string `json:"sha,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` -} - -// GollumEvent is triggered when a Wiki page is created or updated. -// The Webhook event name is "gollum". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#gollumevent -type GollumEvent struct { - Pages []*Page `json:"pages,omitempty"` - - // The following fields are only populated by Webhook events. - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// EditChange represents the changes when an issue, pull request, or comment has -// been edited. -type EditChange struct { - Title *struct { - From *string `json:"from,omitempty"` - } `json:"title,omitempty"` - Body *struct { - From *string `json:"from,omitempty"` - } `json:"body,omitempty"` -} - -// ProjectChange represents the changes when a project has been edited. -type ProjectChange struct { - Name *struct { - From *string `json:"from,omitempty"` - } `json:"name,omitempty"` - Body *struct { - From *string `json:"from,omitempty"` - } `json:"body,omitempty"` -} - -// ProjectCardChange represents the changes when a project card has been edited. -type ProjectCardChange struct { - Note *struct { - From *string `json:"from,omitempty"` - } `json:"note,omitempty"` -} - -// ProjectColumnChange represents the changes when a project column has been edited. -type ProjectColumnChange struct { - Name *struct { - From *string `json:"from,omitempty"` - } `json:"name,omitempty"` -} - -// TeamChange represents the changes when a team has been edited. -type TeamChange struct { - Description *struct { - From *string `json:"from,omitempty"` - } `json:"description,omitempty"` - Name *struct { - From *string `json:"from,omitempty"` - } `json:"name,omitempty"` - Privacy *struct { - From *string `json:"from,omitempty"` - } `json:"privacy,omitempty"` - Repository *struct { - Permissions *struct { - From *struct { - Admin *bool `json:"admin,omitempty"` - Pull *bool `json:"pull,omitempty"` - Push *bool `json:"push,omitempty"` - } `json:"from,omitempty"` - } `json:"permissions,omitempty"` - } `json:"repository,omitempty"` -} - -// InstallationEvent is triggered when a GitHub App has been installed or uninstalled. -// The Webhook event name is "installation". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#installationevent -type InstallationEvent struct { - // The action that was performed. Can be either "created" or "deleted". - Action *string `json:"action,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// InstallationRepositoriesEvent is triggered when a repository is added or -// removed from an installation. The Webhook event name is "installation_repositories". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#installationrepositoriesevent -type InstallationRepositoriesEvent struct { - // The action that was performed. Can be either "added" or "removed". - Action *string `json:"action,omitempty"` - RepositoriesAdded []*Repository `json:"repositories_added,omitempty"` - RepositoriesRemoved []*Repository `json:"repositories_removed,omitempty"` - RepositorySelection *string `json:"repository_selection,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// IssueCommentEvent is triggered when an issue comment is created on an issue -// or pull request. -// The Webhook event name is "issue_comment". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#issuecommentevent -type IssueCommentEvent struct { - // Action is the action that was performed on the comment. - // Possible values are: "created", "edited", "deleted". - Action *string `json:"action,omitempty"` - Issue *Issue `json:"issue,omitempty"` - Comment *IssueComment `json:"comment,omitempty"` - - // The following fields are only populated by Webhook events. - Changes *EditChange `json:"changes,omitempty"` - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// IssuesEvent is triggered when an issue is assigned, unassigned, labeled, -// unlabeled, opened, closed, or reopened. -// The Webhook event name is "issues". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#issuesevent -type IssuesEvent struct { - // Action is the action that was performed. Possible values are: "assigned", - // "unassigned", "labeled", "unlabeled", "opened", "closed", "reopened", "edited". - Action *string `json:"action,omitempty"` - Issue *Issue `json:"issue,omitempty"` - Assignee *User `json:"assignee,omitempty"` - Label *Label `json:"label,omitempty"` - - // The following fields are only populated by Webhook events. - Changes *EditChange `json:"changes,omitempty"` - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// LabelEvent is triggered when a repository's label is created, edited, or deleted. -// The Webhook event name is "label" -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#labelevent -type LabelEvent struct { - // Action is the action that was performed. Possible values are: - // "created", "edited", "deleted" - Action *string `json:"action,omitempty"` - Label *Label `json:"label,omitempty"` - - // The following fields are only populated by Webhook events. - Changes *EditChange `json:"changes,omitempty"` - Repo *Repository `json:"repository,omitempty"` - Org *Organization `json:"organization,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// MarketplacePurchaseEvent is triggered when a user purchases, cancels, or changes -// their GitHub Marketplace plan. -// Webhook event name "marketplace_purchase". -// -// Github API docs: https://developer.github.com/v3/activity/events/types/#marketplacepurchaseevent -type MarketplacePurchaseEvent struct { - // Action is the action that was performed. Possible values are: - // "purchased", "cancelled", "changed". - Action *string `json:"action,omitempty"` - - // The following fields are only populated by Webhook events. - EffectiveDate *Timestamp `json:"effective_date,omitempty"` - MarketplacePurchase *MarketplacePurchase `json:"marketplace_purchase,omitempty"` - PreviousMarketplacePurchase *MarketplacePurchase `json:"previous_marketplace_purchase,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// MemberEvent is triggered when a user is added as a collaborator to a repository. -// The Webhook event name is "member". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#memberevent -type MemberEvent struct { - // Action is the action that was performed. Possible value is: "added". - Action *string `json:"action,omitempty"` - Member *User `json:"member,omitempty"` - - // The following fields are only populated by Webhook events. - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// MembershipEvent is triggered when a user is added or removed from a team. -// The Webhook event name is "membership". -// -// Events of this type are not visible in timelines, they are only used to -// trigger organization webhooks. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#membershipevent -type MembershipEvent struct { - // Action is the action that was performed. Possible values are: "added", "removed". - Action *string `json:"action,omitempty"` - // Scope is the scope of the membership. Possible value is: "team". - Scope *string `json:"scope,omitempty"` - Member *User `json:"member,omitempty"` - Team *Team `json:"team,omitempty"` - - // The following fields are only populated by Webhook events. - Org *Organization `json:"organization,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// MilestoneEvent is triggered when a milestone is created, closed, opened, edited, or deleted. -// The Webhook event name is "milestone". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#milestoneevent -type MilestoneEvent struct { - // Action is the action that was performed. Possible values are: - // "created", "closed", "opened", "edited", "deleted" - Action *string `json:"action,omitempty"` - Milestone *Milestone `json:"milestone,omitempty"` - - // The following fields are only populated by Webhook events. - Changes *EditChange `json:"changes,omitempty"` - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Org *Organization `json:"organization,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// OrganizationEvent is triggered when a user is added, removed, or invited to an organization. -// Events of this type are not visible in timelines. These events are only used to trigger organization hooks. -// Webhook event name is "organization". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#organizationevent -type OrganizationEvent struct { - // Action is the action that was performed. - // Can be one of "member_added", "member_removed", or "member_invited". - Action *string `json:"action,omitempty"` - - // Invitaion is the invitation for the user or email if the action is "member_invited". - Invitation *Invitation `json:"invitation,omitempty"` - - // Membership is the membership between the user and the organization. - // Not present when the action is "member_invited". - Membership *Membership `json:"membership,omitempty"` - - Organization *Organization `json:"organization,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// OrgBlockEvent is triggered when an organization blocks or unblocks a user. -// The Webhook event name is "org_block". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#orgblockevent -type OrgBlockEvent struct { - // Action is the action that was performed. - // Can be "blocked" or "unblocked". - Action *string `json:"action,omitempty"` - BlockedUser *User `json:"blocked_user,omitempty"` - Organization *Organization `json:"organization,omitempty"` - Sender *User `json:"sender,omitempty"` - - // The following fields are only populated by Webhook events. - Installation *Installation `json:"installation,omitempty"` -} - -// PageBuildEvent represents an attempted build of a GitHub Pages site, whether -// successful or not. -// The Webhook event name is "page_build". -// -// This event is triggered on push to a GitHub Pages enabled branch (gh-pages -// for project pages, master for user and organization pages). -// -// Events of this type are not visible in timelines, they are only used to trigger hooks. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pagebuildevent -type PageBuildEvent struct { - Build *PagesBuild `json:"build,omitempty"` - - // The following fields are only populated by Webhook events. - ID *int64 `json:"id,omitempty"` - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// PingEvent is triggered when a Webhook is added to GitHub. -// -// GitHub API docs: https://developer.github.com/webhooks/#ping-event -type PingEvent struct { - // Random string of GitHub zen. - Zen *string `json:"zen,omitempty"` - // The ID of the webhook that triggered the ping. - HookID *int64 `json:"hook_id,omitempty"` - // The webhook configuration. - Hook *Hook `json:"hook,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// ProjectEvent is triggered when project is created, modified or deleted. -// The webhook event name is "project". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectevent -type ProjectEvent struct { - Action *string `json:"action,omitempty"` - Changes *ProjectChange `json:"changes,omitempty"` - Project *Project `json:"project,omitempty"` - - // The following fields are only populated by Webhook events. - Repo *Repository `json:"repository,omitempty"` - Org *Organization `json:"organization,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// ProjectCardEvent is triggered when a project card is created, updated, moved, converted to an issue, or deleted. -// The webhook event name is "project_card". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectcardevent -type ProjectCardEvent struct { - Action *string `json:"action,omitempty"` - Changes *ProjectCardChange `json:"changes,omitempty"` - AfterID *int64 `json:"after_id,omitempty"` - ProjectCard *ProjectCard `json:"project_card,omitempty"` - - // The following fields are only populated by Webhook events. - Repo *Repository `json:"repository,omitempty"` - Org *Organization `json:"organization,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// ProjectColumnEvent is triggered when a project column is created, updated, moved, or deleted. -// The webhook event name is "project_column". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#projectcolumnevent -type ProjectColumnEvent struct { - Action *string `json:"action,omitempty"` - Changes *ProjectColumnChange `json:"changes,omitempty"` - AfterID *int64 `json:"after_id,omitempty"` - ProjectColumn *ProjectColumn `json:"project_column,omitempty"` - - // The following fields are only populated by Webhook events. - Repo *Repository `json:"repository,omitempty"` - Org *Organization `json:"organization,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// PublicEvent is triggered when a private repository is open sourced. -// According to GitHub: "Without a doubt: the best GitHub event." -// The Webhook event name is "public". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#publicevent -type PublicEvent struct { - // The following fields are only populated by Webhook events. - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// PullRequestEvent is triggered when a pull request is assigned, unassigned, -// labeled, unlabeled, opened, closed, reopened, or synchronized. -// The Webhook event name is "pull_request". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestevent -type PullRequestEvent struct { - // Action is the action that was performed. Possible values are: - // "assigned", "unassigned", "review_requested", "review_request_removed", "labeled", "unlabeled", - // "opened", "closed", "reopened", "synchronize", "edited". - // If the action is "closed" and the merged key is false, - // the pull request was closed with unmerged commits. If the action is "closed" - // and the merged key is true, the pull request was merged. - Action *string `json:"action,omitempty"` - Number *int `json:"number,omitempty"` - PullRequest *PullRequest `json:"pull_request,omitempty"` - - // The following fields are only populated by Webhook events. - Changes *EditChange `json:"changes,omitempty"` - RequestedReviewers []*User `json:"requested_reviewers,omitempty"` // Populated in "review_requested", "review_request_removed" event deliveries. - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// PullRequestReviewEvent is triggered when a review is submitted on a pull -// request. -// The Webhook event name is "pull_request_review". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestreviewevent -type PullRequestReviewEvent struct { - // Action is always "submitted". - Action *string `json:"action,omitempty"` - Review *PullRequestReview `json:"review,omitempty"` - PullRequest *PullRequest `json:"pull_request,omitempty"` - - // The following fields are only populated by Webhook events. - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` - - // The following field is only present when the webhook is triggered on - // a repository belonging to an organization. - Organization *Organization `json:"organization,omitempty"` -} - -// PullRequestReviewCommentEvent is triggered when a comment is created on a -// portion of the unified diff of a pull request. -// The Webhook event name is "pull_request_review_comment". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pullrequestreviewcommentevent -type PullRequestReviewCommentEvent struct { - // Action is the action that was performed on the comment. - // Possible values are: "created", "edited", "deleted". - Action *string `json:"action,omitempty"` - PullRequest *PullRequest `json:"pull_request,omitempty"` - Comment *PullRequestComment `json:"comment,omitempty"` - - // The following fields are only populated by Webhook events. - Changes *EditChange `json:"changes,omitempty"` - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// PushEvent represents a git push to a GitHub repository. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#pushevent -type PushEvent struct { - PushID *int64 `json:"push_id,omitempty"` - Head *string `json:"head,omitempty"` - Ref *string `json:"ref,omitempty"` - Size *int `json:"size,omitempty"` - Commits []PushEventCommit `json:"commits,omitempty"` - Before *string `json:"before,omitempty"` - DistinctSize *int `json:"distinct_size,omitempty"` - - // The following fields are only populated by Webhook events. - After *string `json:"after,omitempty"` - Created *bool `json:"created,omitempty"` - Deleted *bool `json:"deleted,omitempty"` - Forced *bool `json:"forced,omitempty"` - BaseRef *string `json:"base_ref,omitempty"` - Compare *string `json:"compare,omitempty"` - Repo *PushEventRepository `json:"repository,omitempty"` - HeadCommit *PushEventCommit `json:"head_commit,omitempty"` - Pusher *User `json:"pusher,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -func (p PushEvent) String() string { - return Stringify(p) -} - -// PushEventCommit represents a git commit in a GitHub PushEvent. -type PushEventCommit struct { - Message *string `json:"message,omitempty"` - Author *CommitAuthor `json:"author,omitempty"` - URL *string `json:"url,omitempty"` - Distinct *bool `json:"distinct,omitempty"` - - // The following fields are only populated by Events API. - SHA *string `json:"sha,omitempty"` - - // The following fields are only populated by Webhook events. - ID *string `json:"id,omitempty"` - TreeID *string `json:"tree_id,omitempty"` - Timestamp *Timestamp `json:"timestamp,omitempty"` - Committer *CommitAuthor `json:"committer,omitempty"` - Added []string `json:"added,omitempty"` - Removed []string `json:"removed,omitempty"` - Modified []string `json:"modified,omitempty"` -} - -func (p PushEventCommit) String() string { - return Stringify(p) -} - -// PushEventRepository represents the repo object in a PushEvent payload. -type PushEventRepository struct { - ID *int64 `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - FullName *string `json:"full_name,omitempty"` - Owner *PushEventRepoOwner `json:"owner,omitempty"` - Private *bool `json:"private,omitempty"` - Description *string `json:"description,omitempty"` - Fork *bool `json:"fork,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - PushedAt *Timestamp `json:"pushed_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - Homepage *string `json:"homepage,omitempty"` - Size *int `json:"size,omitempty"` - StargazersCount *int `json:"stargazers_count,omitempty"` - WatchersCount *int `json:"watchers_count,omitempty"` - Language *string `json:"language,omitempty"` - HasIssues *bool `json:"has_issues,omitempty"` - HasDownloads *bool `json:"has_downloads,omitempty"` - HasWiki *bool `json:"has_wiki,omitempty"` - HasPages *bool `json:"has_pages,omitempty"` - ForksCount *int `json:"forks_count,omitempty"` - OpenIssuesCount *int `json:"open_issues_count,omitempty"` - DefaultBranch *string `json:"default_branch,omitempty"` - MasterBranch *string `json:"master_branch,omitempty"` - Organization *string `json:"organization,omitempty"` - URL *string `json:"url,omitempty"` - ArchiveURL *string `json:"archive_url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - StatusesURL *string `json:"statuses_url,omitempty"` - GitURL *string `json:"git_url,omitempty"` - SSHURL *string `json:"ssh_url,omitempty"` - CloneURL *string `json:"clone_url,omitempty"` - SVNURL *string `json:"svn_url,omitempty"` -} - -// PushEventRepoOwner is a basic representation of user/org in a PushEvent payload. -type PushEventRepoOwner struct { - Name *string `json:"name,omitempty"` - Email *string `json:"email,omitempty"` -} - -// ReleaseEvent is triggered when a release is published. -// The Webhook event name is "release". -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#releaseevent -type ReleaseEvent struct { - // Action is the action that was performed. Possible value is: "published". - Action *string `json:"action,omitempty"` - Release *RepositoryRelease `json:"release,omitempty"` - - // The following fields are only populated by Webhook events. - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// RepositoryEvent is triggered when a repository is created. -// The Webhook event name is "repository". -// -// Events of this type are not visible in timelines, they are only used to -// trigger organization webhooks. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#repositoryevent -type RepositoryEvent struct { - // Action is the action that was performed. Possible values are: "created", "deleted", - // "publicized", "privatized". - Action *string `json:"action,omitempty"` - Repo *Repository `json:"repository,omitempty"` - - // The following fields are only populated by Webhook events. - Org *Organization `json:"organization,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// StatusEvent is triggered when the status of a Git commit changes. -// The Webhook event name is "status". -// -// Events of this type are not visible in timelines, they are only used to -// trigger hooks. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#statusevent -type StatusEvent struct { - SHA *string `json:"sha,omitempty"` - // State is the new state. Possible values are: "pending", "success", "failure", "error". - State *string `json:"state,omitempty"` - Description *string `json:"description,omitempty"` - TargetURL *string `json:"target_url,omitempty"` - Branches []*Branch `json:"branches,omitempty"` - - // The following fields are only populated by Webhook events. - ID *int64 `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - Context *string `json:"context,omitempty"` - Commit *RepositoryCommit `json:"commit,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// TeamEvent is triggered when an organization's team is created, modified or deleted. -// The Webhook event name is "team". -// -// Events of this type are not visible in timelines. These events are only used -// to trigger hooks. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#teamevent -type TeamEvent struct { - Action *string `json:"action,omitempty"` - Team *Team `json:"team,omitempty"` - Changes *TeamChange `json:"changes,omitempty"` - Repo *Repository `json:"repository,omitempty"` - - // The following fields are only populated by Webhook events. - Org *Organization `json:"organization,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// TeamAddEvent is triggered when a repository is added to a team. -// The Webhook event name is "team_add". -// -// Events of this type are not visible in timelines. These events are only used -// to trigger hooks. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#teamaddevent -type TeamAddEvent struct { - Team *Team `json:"team,omitempty"` - Repo *Repository `json:"repository,omitempty"` - - // The following fields are only populated by Webhook events. - Org *Organization `json:"organization,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} - -// WatchEvent is related to starring a repository, not watching. See this API -// blog post for an explanation: https://developer.github.com/changes/2012-09-05-watcher-api/ -// -// The event’s actor is the user who starred a repository, and the event’s -// repository is the repository that was starred. -// -// GitHub API docs: https://developer.github.com/v3/activity/events/types/#watchevent -type WatchEvent struct { - // Action is the action that was performed. Possible value is: "started". - Action *string `json:"action,omitempty"` - - // The following fields are only populated by Webhook events. - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` - Installation *Installation `json:"installation,omitempty"` -} diff --git a/vendor/github.com/google/go-github/github/gen-accessors.go b/vendor/github.com/google/go-github/github/gen-accessors.go deleted file mode 100644 index fe92206fcf8..00000000000 --- a/vendor/github.com/google/go-github/github/gen-accessors.go +++ /dev/null @@ -1,332 +0,0 @@ -// Copyright 2017 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build ignore - -// gen-accessors generates accessor methods for structs with pointer fields. -// -// It is meant to be used by the go-github authors in conjunction with the -// go generate tool before sending a commit to GitHub. -package main - -import ( - "bytes" - "flag" - "fmt" - "go/ast" - "go/format" - "go/parser" - "go/token" - "io/ioutil" - "log" - "os" - "sort" - "strings" - "text/template" -) - -const ( - fileSuffix = "-accessors.go" -) - -var ( - verbose = flag.Bool("v", false, "Print verbose log messages") - - sourceTmpl = template.Must(template.New("source").Parse(source)) - - // blacklistStructMethod lists "struct.method" combos to skip. - blacklistStructMethod = map[string]bool{ - "RepositoryContent.GetContent": true, - "Client.GetBaseURL": true, - "Client.GetUploadURL": true, - "ErrorResponse.GetResponse": true, - "RateLimitError.GetResponse": true, - "AbuseRateLimitError.GetResponse": true, - } - // blacklistStruct lists structs to skip. - blacklistStruct = map[string]bool{ - "Client": true, - } -) - -func logf(fmt string, args ...interface{}) { - if *verbose { - log.Printf(fmt, args...) - } -} - -func main() { - flag.Parse() - fset := token.NewFileSet() - - pkgs, err := parser.ParseDir(fset, ".", sourceFilter, 0) - if err != nil { - log.Fatal(err) - return - } - - for pkgName, pkg := range pkgs { - t := &templateData{ - filename: pkgName + fileSuffix, - Year: 2017, - Package: pkgName, - Imports: map[string]string{}, - } - for filename, f := range pkg.Files { - logf("Processing %v...", filename) - if err := t.processAST(f); err != nil { - log.Fatal(err) - } - } - if err := t.dump(); err != nil { - log.Fatal(err) - } - } - logf("Done.") -} - -func (t *templateData) processAST(f *ast.File) error { - for _, decl := range f.Decls { - gd, ok := decl.(*ast.GenDecl) - if !ok { - continue - } - for _, spec := range gd.Specs { - ts, ok := spec.(*ast.TypeSpec) - if !ok { - continue - } - // Skip unexported identifiers. - if !ts.Name.IsExported() { - logf("Struct %v is unexported; skipping.", ts.Name) - continue - } - // Check if the struct is blacklisted. - if blacklistStruct[ts.Name.Name] { - logf("Struct %v is blacklisted; skipping.", ts.Name) - continue - } - st, ok := ts.Type.(*ast.StructType) - if !ok { - continue - } - for _, field := range st.Fields.List { - se, ok := field.Type.(*ast.StarExpr) - if len(field.Names) == 0 || !ok { - continue - } - - fieldName := field.Names[0] - // Skip unexported identifiers. - if !fieldName.IsExported() { - logf("Field %v is unexported; skipping.", fieldName) - continue - } - // Check if "struct.method" is blacklisted. - if key := fmt.Sprintf("%v.Get%v", ts.Name, fieldName); blacklistStructMethod[key] { - logf("Method %v is blacklisted; skipping.", key) - continue - } - - switch x := se.X.(type) { - case *ast.ArrayType: - t.addArrayType(x, ts.Name.String(), fieldName.String()) - case *ast.Ident: - t.addIdent(x, ts.Name.String(), fieldName.String()) - case *ast.MapType: - t.addMapType(x, ts.Name.String(), fieldName.String()) - case *ast.SelectorExpr: - t.addSelectorExpr(x, ts.Name.String(), fieldName.String()) - default: - logf("processAST: type %q, field %q, unknown %T: %+v", ts.Name, fieldName, x, x) - } - } - } - } - return nil -} - -func sourceFilter(fi os.FileInfo) bool { - return !strings.HasSuffix(fi.Name(), "_test.go") && !strings.HasSuffix(fi.Name(), fileSuffix) -} - -func (t *templateData) dump() error { - if len(t.Getters) == 0 { - logf("No getters for %v; skipping.", t.filename) - return nil - } - - // Sort getters by ReceiverType.FieldName. - sort.Sort(byName(t.Getters)) - - var buf bytes.Buffer - if err := sourceTmpl.Execute(&buf, t); err != nil { - return err - } - clean, err := format.Source(buf.Bytes()) - if err != nil { - return err - } - - logf("Writing %v...", t.filename) - return ioutil.WriteFile(t.filename, clean, 0644) -} - -func newGetter(receiverType, fieldName, fieldType, zeroValue string, namedStruct bool) *getter { - return &getter{ - sortVal: strings.ToLower(receiverType) + "." + strings.ToLower(fieldName), - ReceiverVar: strings.ToLower(receiverType[:1]), - ReceiverType: receiverType, - FieldName: fieldName, - FieldType: fieldType, - ZeroValue: zeroValue, - NamedStruct: namedStruct, - } -} - -func (t *templateData) addArrayType(x *ast.ArrayType, receiverType, fieldName string) { - var eltType string - switch elt := x.Elt.(type) { - case *ast.Ident: - eltType = elt.String() - default: - logf("addArrayType: type %q, field %q: unknown elt type: %T %+v; skipping.", receiverType, fieldName, elt, elt) - return - } - - t.Getters = append(t.Getters, newGetter(receiverType, fieldName, "[]"+eltType, "nil", false)) -} - -func (t *templateData) addIdent(x *ast.Ident, receiverType, fieldName string) { - var zeroValue string - var namedStruct = false - switch x.String() { - case "int", "int64": - zeroValue = "0" - case "string": - zeroValue = `""` - case "bool": - zeroValue = "false" - case "Timestamp": - zeroValue = "Timestamp{}" - default: - zeroValue = "nil" - namedStruct = true - } - - t.Getters = append(t.Getters, newGetter(receiverType, fieldName, x.String(), zeroValue, namedStruct)) -} - -func (t *templateData) addMapType(x *ast.MapType, receiverType, fieldName string) { - var keyType string - switch key := x.Key.(type) { - case *ast.Ident: - keyType = key.String() - default: - logf("addMapType: type %q, field %q: unknown key type: %T %+v; skipping.", receiverType, fieldName, key, key) - return - } - - var valueType string - switch value := x.Value.(type) { - case *ast.Ident: - valueType = value.String() - default: - logf("addMapType: type %q, field %q: unknown value type: %T %+v; skipping.", receiverType, fieldName, value, value) - return - } - - fieldType := fmt.Sprintf("map[%v]%v", keyType, valueType) - zeroValue := fmt.Sprintf("map[%v]%v{}", keyType, valueType) - t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false)) -} - -func (t *templateData) addSelectorExpr(x *ast.SelectorExpr, receiverType, fieldName string) { - if strings.ToLower(fieldName[:1]) == fieldName[:1] { // Non-exported field. - return - } - - var xX string - if xx, ok := x.X.(*ast.Ident); ok { - xX = xx.String() - } - - switch xX { - case "time", "json": - if xX == "json" { - t.Imports["encoding/json"] = "encoding/json" - } else { - t.Imports[xX] = xX - } - fieldType := fmt.Sprintf("%v.%v", xX, x.Sel.Name) - zeroValue := fmt.Sprintf("%v.%v{}", xX, x.Sel.Name) - if xX == "time" && x.Sel.Name == "Duration" { - zeroValue = "0" - } - t.Getters = append(t.Getters, newGetter(receiverType, fieldName, fieldType, zeroValue, false)) - default: - logf("addSelectorExpr: xX %q, type %q, field %q: unknown x=%+v; skipping.", xX, receiverType, fieldName, x) - } -} - -type templateData struct { - filename string - Year int - Package string - Imports map[string]string - Getters []*getter -} - -type getter struct { - sortVal string // Lower-case version of "ReceiverType.FieldName". - ReceiverVar string // The one-letter variable name to match the ReceiverType. - ReceiverType string - FieldName string - FieldType string - ZeroValue string - NamedStruct bool // Getter for named struct. -} - -type byName []*getter - -func (b byName) Len() int { return len(b) } -func (b byName) Less(i, j int) bool { return b[i].sortVal < b[j].sortVal } -func (b byName) Swap(i, j int) { b[i], b[j] = b[j], b[i] } - -const source = `// Copyright {{.Year}} The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Code generated by gen-accessors; DO NOT EDIT. - -package {{.Package}} -{{with .Imports}} -import ( - {{- range . -}} - "{{.}}" - {{end -}} -) -{{end}} -{{range .Getters}} -{{if .NamedStruct}} -// Get{{.FieldName}} returns the {{.FieldName}} field. -func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() *{{.FieldType}} { - if {{.ReceiverVar}} == nil { - return {{.ZeroValue}} - } - return {{.ReceiverVar}}.{{.FieldName}} -} -{{else}} -// Get{{.FieldName}} returns the {{.FieldName}} field if it's non-nil, zero value otherwise. -func ({{.ReceiverVar}} *{{.ReceiverType}}) Get{{.FieldName}}() {{.FieldType}} { - if {{.ReceiverVar}} == nil || {{.ReceiverVar}}.{{.FieldName}} == nil { - return {{.ZeroValue}} - } - return *{{.ReceiverVar}}.{{.FieldName}} -} -{{end}} -{{end}} -` diff --git a/vendor/github.com/google/go-github/github/gists.go b/vendor/github.com/google/go-github/github/gists.go deleted file mode 100644 index 9108b642449..00000000000 --- a/vendor/github.com/google/go-github/github/gists.go +++ /dev/null @@ -1,388 +0,0 @@ -// Copyright 2013 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by BSD-style -// license that can be found in the LICENSE file. - -package github - -import ( - "context" - "fmt" - "time" -) - -// GistsService handles communication with the Gist related -// methods of the GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/gists/ -type GistsService service - -// Gist represents a GitHub's gist. -type Gist struct { - ID *string `json:"id,omitempty"` - Description *string `json:"description,omitempty"` - Public *bool `json:"public,omitempty"` - Owner *User `json:"owner,omitempty"` - Files map[GistFilename]GistFile `json:"files,omitempty"` - Comments *int `json:"comments,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - GitPullURL *string `json:"git_pull_url,omitempty"` - GitPushURL *string `json:"git_push_url,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - NodeID *string `json:"node_id,omitempty"` -} - -func (g Gist) String() string { - return Stringify(g) -} - -// GistFilename represents filename on a gist. -type GistFilename string - -// GistFile represents a file on a gist. -type GistFile struct { - Size *int `json:"size,omitempty"` - Filename *string `json:"filename,omitempty"` - Language *string `json:"language,omitempty"` - Type *string `json:"type,omitempty"` - RawURL *string `json:"raw_url,omitempty"` - Content *string `json:"content,omitempty"` -} - -func (g GistFile) String() string { - return Stringify(g) -} - -// GistCommit represents a commit on a gist. -type GistCommit struct { - URL *string `json:"url,omitempty"` - Version *string `json:"version,omitempty"` - User *User `json:"user,omitempty"` - ChangeStatus *CommitStats `json:"change_status,omitempty"` - CommittedAt *Timestamp `json:"committed_at,omitempty"` - NodeID *string `json:"node_id,omitempty"` -} - -func (gc GistCommit) String() string { - return Stringify(gc) -} - -// GistFork represents a fork of a gist. -type GistFork struct { - URL *string `json:"url,omitempty"` - User *User `json:"user,omitempty"` - ID *string `json:"id,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - NodeID *string `json:"node_id,omitempty"` -} - -func (gf GistFork) String() string { - return Stringify(gf) -} - -// GistListOptions specifies the optional parameters to the -// GistsService.List, GistsService.ListAll, and GistsService.ListStarred methods. -type GistListOptions struct { - // Since filters Gists by time. - Since time.Time `url:"since,omitempty"` - - ListOptions -} - -// List gists for a user. Passing the empty string will list -// all public gists if called anonymously. However, if the call -// is authenticated, it will returns all gists for the authenticated -// user. -// -// GitHub API docs: https://developer.github.com/v3/gists/#list-gists -func (s *GistsService) List(ctx context.Context, user string, opt *GistListOptions) ([]*Gist, *Response, error) { - var u string - if user != "" { - u = fmt.Sprintf("users/%v/gists", user) - } else { - u = "gists" - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var gists []*Gist - resp, err := s.client.Do(ctx, req, &gists) - if err != nil { - return nil, resp, err - } - - return gists, resp, nil -} - -// ListAll lists all public gists. -// -// GitHub API docs: https://developer.github.com/v3/gists/#list-gists -func (s *GistsService) ListAll(ctx context.Context, opt *GistListOptions) ([]*Gist, *Response, error) { - u, err := addOptions("gists/public", opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var gists []*Gist - resp, err := s.client.Do(ctx, req, &gists) - if err != nil { - return nil, resp, err - } - - return gists, resp, nil -} - -// ListStarred lists starred gists of authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/gists/#list-gists -func (s *GistsService) ListStarred(ctx context.Context, opt *GistListOptions) ([]*Gist, *Response, error) { - u, err := addOptions("gists/starred", opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var gists []*Gist - resp, err := s.client.Do(ctx, req, &gists) - if err != nil { - return nil, resp, err - } - - return gists, resp, nil -} - -// Get a single gist. -// -// GitHub API docs: https://developer.github.com/v3/gists/#get-a-single-gist -func (s *GistsService) Get(ctx context.Context, id string) (*Gist, *Response, error) { - u := fmt.Sprintf("gists/%v", id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - gist := new(Gist) - resp, err := s.client.Do(ctx, req, gist) - if err != nil { - return nil, resp, err - } - - return gist, resp, nil -} - -// GetRevision gets a specific revision of a gist. -// -// GitHub API docs: https://developer.github.com/v3/gists/#get-a-specific-revision-of-a-gist -func (s *GistsService) GetRevision(ctx context.Context, id, sha string) (*Gist, *Response, error) { - u := fmt.Sprintf("gists/%v/%v", id, sha) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - gist := new(Gist) - resp, err := s.client.Do(ctx, req, gist) - if err != nil { - return nil, resp, err - } - - return gist, resp, nil -} - -// Create a gist for authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/gists/#create-a-gist -func (s *GistsService) Create(ctx context.Context, gist *Gist) (*Gist, *Response, error) { - u := "gists" - req, err := s.client.NewRequest("POST", u, gist) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - g := new(Gist) - resp, err := s.client.Do(ctx, req, g) - if err != nil { - return nil, resp, err - } - - return g, resp, nil -} - -// Edit a gist. -// -// GitHub API docs: https://developer.github.com/v3/gists/#edit-a-gist -func (s *GistsService) Edit(ctx context.Context, id string, gist *Gist) (*Gist, *Response, error) { - u := fmt.Sprintf("gists/%v", id) - req, err := s.client.NewRequest("PATCH", u, gist) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - g := new(Gist) - resp, err := s.client.Do(ctx, req, g) - if err != nil { - return nil, resp, err - } - - return g, resp, nil -} - -// ListCommits lists commits of a gist. -// -// GitHub API docs: https://developer.github.com/v3/gists/#list-gist-commits -func (s *GistsService) ListCommits(ctx context.Context, id string, opt *ListOptions) ([]*GistCommit, *Response, error) { - u := fmt.Sprintf("gists/%v/commits", id) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var gistCommits []*GistCommit - resp, err := s.client.Do(ctx, req, &gistCommits) - if err != nil { - return nil, resp, err - } - - return gistCommits, resp, nil -} - -// Delete a gist. -// -// GitHub API docs: https://developer.github.com/v3/gists/#delete-a-gist -func (s *GistsService) Delete(ctx context.Context, id string) (*Response, error) { - u := fmt.Sprintf("gists/%v", id) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} - -// Star a gist on behalf of authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/gists/#star-a-gist -func (s *GistsService) Star(ctx context.Context, id string) (*Response, error) { - u := fmt.Sprintf("gists/%v/star", id) - req, err := s.client.NewRequest("PUT", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} - -// Unstar a gist on a behalf of authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/gists/#unstar-a-gist -func (s *GistsService) Unstar(ctx context.Context, id string) (*Response, error) { - u := fmt.Sprintf("gists/%v/star", id) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} - -// IsStarred checks if a gist is starred by authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/gists/#check-if-a-gist-is-starred -func (s *GistsService) IsStarred(ctx context.Context, id string) (bool, *Response, error) { - u := fmt.Sprintf("gists/%v/star", id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return false, nil, err - } - resp, err := s.client.Do(ctx, req, nil) - starred, err := parseBoolResponse(err) - return starred, resp, err -} - -// Fork a gist. -// -// GitHub API docs: https://developer.github.com/v3/gists/#fork-a-gist -func (s *GistsService) Fork(ctx context.Context, id string) (*Gist, *Response, error) { - u := fmt.Sprintf("gists/%v/forks", id) - req, err := s.client.NewRequest("POST", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - g := new(Gist) - resp, err := s.client.Do(ctx, req, g) - if err != nil { - return nil, resp, err - } - - return g, resp, nil -} - -// ListForks lists forks of a gist. -// -// GitHub API docs: https://developer.github.com/v3/gists/#list-gist-forks -func (s *GistsService) ListForks(ctx context.Context, id string) ([]*GistFork, *Response, error) { - u := fmt.Sprintf("gists/%v/forks", id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var gistForks []*GistFork - resp, err := s.client.Do(ctx, req, &gistForks) - if err != nil { - return nil, resp, err - } - - return gistForks, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/gists_comments.go b/vendor/github.com/google/go-github/github/gists_comments.go deleted file mode 100644 index d5322e3d852..00000000000 --- a/vendor/github.com/google/go-github/github/gists_comments.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// GistComment represents a Gist comment. -type GistComment struct { - ID *int64 `json:"id,omitempty"` - URL *string `json:"url,omitempty"` - Body *string `json:"body,omitempty"` - User *User `json:"user,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` -} - -func (g GistComment) String() string { - return Stringify(g) -} - -// ListComments lists all comments for a gist. -// -// GitHub API docs: https://developer.github.com/v3/gists/comments/#list-comments-on-a-gist -func (s *GistsService) ListComments(ctx context.Context, gistID string, opt *ListOptions) ([]*GistComment, *Response, error) { - u := fmt.Sprintf("gists/%v/comments", gistID) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var comments []*GistComment - resp, err := s.client.Do(ctx, req, &comments) - if err != nil { - return nil, resp, err - } - - return comments, resp, nil -} - -// GetComment retrieves a single comment from a gist. -// -// GitHub API docs: https://developer.github.com/v3/gists/comments/#get-a-single-comment -func (s *GistsService) GetComment(ctx context.Context, gistID string, commentID int64) (*GistComment, *Response, error) { - u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - c := new(GistComment) - resp, err := s.client.Do(ctx, req, c) - if err != nil { - return nil, resp, err - } - - return c, resp, nil -} - -// CreateComment creates a comment for a gist. -// -// GitHub API docs: https://developer.github.com/v3/gists/comments/#create-a-comment -func (s *GistsService) CreateComment(ctx context.Context, gistID string, comment *GistComment) (*GistComment, *Response, error) { - u := fmt.Sprintf("gists/%v/comments", gistID) - req, err := s.client.NewRequest("POST", u, comment) - if err != nil { - return nil, nil, err - } - - c := new(GistComment) - resp, err := s.client.Do(ctx, req, c) - if err != nil { - return nil, resp, err - } - - return c, resp, nil -} - -// EditComment edits an existing gist comment. -// -// GitHub API docs: https://developer.github.com/v3/gists/comments/#edit-a-comment -func (s *GistsService) EditComment(ctx context.Context, gistID string, commentID int64, comment *GistComment) (*GistComment, *Response, error) { - u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) - req, err := s.client.NewRequest("PATCH", u, comment) - if err != nil { - return nil, nil, err - } - - c := new(GistComment) - resp, err := s.client.Do(ctx, req, c) - if err != nil { - return nil, resp, err - } - - return c, resp, nil -} - -// DeleteComment deletes a gist comment. -// -// GitHub API docs: https://developer.github.com/v3/gists/comments/#delete-a-comment -func (s *GistsService) DeleteComment(ctx context.Context, gistID string, commentID int64) (*Response, error) { - u := fmt.Sprintf("gists/%v/comments/%v", gistID, commentID) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/git.go b/vendor/github.com/google/go-github/github/git.go deleted file mode 100644 index 1ce47437bd3..00000000000 --- a/vendor/github.com/google/go-github/github/git.go +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright 2013 The go-github 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 github - -// GitService handles communication with the git data related -// methods of the GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/git/ -type GitService service diff --git a/vendor/github.com/google/go-github/github/git_blobs.go b/vendor/github.com/google/go-github/github/git_blobs.go deleted file mode 100644 index 9d8fd27bcca..00000000000 --- a/vendor/github.com/google/go-github/github/git_blobs.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// Blob represents a blob object. -type Blob struct { - Content *string `json:"content,omitempty"` - Encoding *string `json:"encoding,omitempty"` - SHA *string `json:"sha,omitempty"` - Size *int `json:"size,omitempty"` - URL *string `json:"url,omitempty"` - NodeID *string `json:"node_id,omitempty"` -} - -// GetBlob fetchs a blob from a repo given a SHA. -// -// GitHub API docs: https://developer.github.com/v3/git/blobs/#get-a-blob -func (s *GitService) GetBlob(ctx context.Context, owner string, repo string, sha string) (*Blob, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/git/blobs/%v", owner, repo, sha) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - blob := new(Blob) - resp, err := s.client.Do(ctx, req, blob) - return blob, resp, err -} - -// CreateBlob creates a blob object. -// -// GitHub API docs: https://developer.github.com/v3/git/blobs/#create-a-blob -func (s *GitService) CreateBlob(ctx context.Context, owner string, repo string, blob *Blob) (*Blob, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/git/blobs", owner, repo) - req, err := s.client.NewRequest("POST", u, blob) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - t := new(Blob) - resp, err := s.client.Do(ctx, req, t) - return t, resp, err -} diff --git a/vendor/github.com/google/go-github/github/git_commits.go b/vendor/github.com/google/go-github/github/git_commits.go deleted file mode 100644 index 29882569c94..00000000000 --- a/vendor/github.com/google/go-github/github/git_commits.go +++ /dev/null @@ -1,139 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" - "strings" - "time" -) - -// SignatureVerification represents GPG signature verification. -type SignatureVerification struct { - Verified *bool `json:"verified,omitempty"` - Reason *string `json:"reason,omitempty"` - Signature *string `json:"signature,omitempty"` - Payload *string `json:"payload,omitempty"` -} - -// Commit represents a GitHub commit. -type Commit struct { - SHA *string `json:"sha,omitempty"` - Author *CommitAuthor `json:"author,omitempty"` - Committer *CommitAuthor `json:"committer,omitempty"` - Message *string `json:"message,omitempty"` - Tree *Tree `json:"tree,omitempty"` - Parents []Commit `json:"parents,omitempty"` - Stats *CommitStats `json:"stats,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - URL *string `json:"url,omitempty"` - Verification *SignatureVerification `json:"verification,omitempty"` - NodeID *string `json:"node_id,omitempty"` - - // CommentCount is the number of GitHub comments on the commit. This - // is only populated for requests that fetch GitHub data like - // Pulls.ListCommits, Repositories.ListCommits, etc. - CommentCount *int `json:"comment_count,omitempty"` -} - -func (c Commit) String() string { - return Stringify(c) -} - -// CommitAuthor represents the author or committer of a commit. The commit -// author may not correspond to a GitHub User. -type CommitAuthor struct { - Date *time.Time `json:"date,omitempty"` - Name *string `json:"name,omitempty"` - Email *string `json:"email,omitempty"` - - // The following fields are only populated by Webhook events. - Login *string `json:"username,omitempty"` // Renamed for go-github consistency. -} - -func (c CommitAuthor) String() string { - return Stringify(c) -} - -// GetCommit fetchs the Commit object for a given SHA. -// -// GitHub API docs: https://developer.github.com/v3/git/commits/#get-a-commit -func (s *GitService) GetCommit(ctx context.Context, owner string, repo string, sha string) (*Commit, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/git/commits/%v", owner, repo, sha) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeGitSigningPreview, mediaTypeGraphQLNodeIDPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - c := new(Commit) - resp, err := s.client.Do(ctx, req, c) - if err != nil { - return nil, resp, err - } - - return c, resp, nil -} - -// createCommit represents the body of a CreateCommit request. -type createCommit struct { - Author *CommitAuthor `json:"author,omitempty"` - Committer *CommitAuthor `json:"committer,omitempty"` - Message *string `json:"message,omitempty"` - Tree *string `json:"tree,omitempty"` - Parents []string `json:"parents,omitempty"` -} - -// CreateCommit creates a new commit in a repository. -// commit must not be nil. -// -// The commit.Committer is optional and will be filled with the commit.Author -// data if omitted. If the commit.Author is omitted, it will be filled in with -// the authenticated user’s information and the current date. -// -// GitHub API docs: https://developer.github.com/v3/git/commits/#create-a-commit -func (s *GitService) CreateCommit(ctx context.Context, owner string, repo string, commit *Commit) (*Commit, *Response, error) { - if commit == nil { - return nil, nil, fmt.Errorf("commit must be provided") - } - - u := fmt.Sprintf("repos/%v/%v/git/commits", owner, repo) - - parents := make([]string, len(commit.Parents)) - for i, parent := range commit.Parents { - parents[i] = *parent.SHA - } - - body := &createCommit{ - Author: commit.Author, - Committer: commit.Committer, - Message: commit.Message, - Parents: parents, - } - if commit.Tree != nil { - body.Tree = commit.Tree.SHA - } - - req, err := s.client.NewRequest("POST", u, body) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - c := new(Commit) - resp, err := s.client.Do(ctx, req, c) - if err != nil { - return nil, resp, err - } - - return c, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/git_refs.go b/vendor/github.com/google/go-github/github/git_refs.go deleted file mode 100644 index 0947d866ab6..00000000000 --- a/vendor/github.com/google/go-github/github/git_refs.go +++ /dev/null @@ -1,233 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "encoding/json" - "errors" - "fmt" - "strings" -) - -// Reference represents a GitHub reference. -type Reference struct { - Ref *string `json:"ref"` - URL *string `json:"url"` - Object *GitObject `json:"object"` - NodeID *string `json:"node_id,omitempty"` -} - -func (r Reference) String() string { - return Stringify(r) -} - -// GitObject represents a Git object. -type GitObject struct { - Type *string `json:"type"` - SHA *string `json:"sha"` - URL *string `json:"url"` -} - -func (o GitObject) String() string { - return Stringify(o) -} - -// createRefRequest represents the payload for creating a reference. -type createRefRequest struct { - Ref *string `json:"ref"` - SHA *string `json:"sha"` -} - -// updateRefRequest represents the payload for updating a reference. -type updateRefRequest struct { - SHA *string `json:"sha"` - Force *bool `json:"force"` -} - -// GetRef fetches a single Reference object for a given Git ref. -// If there is no exact match, GetRef will return an error. -// -// Note: The GitHub API can return multiple matches. -// If you wish to use this functionality please use the GetRefs() method. -// -// GitHub API docs: https://developer.github.com/v3/git/refs/#get-a-reference -func (s *GitService) GetRef(ctx context.Context, owner string, repo string, ref string) (*Reference, *Response, error) { - ref = strings.TrimPrefix(ref, "refs/") - u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, ref) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - r := new(Reference) - resp, err := s.client.Do(ctx, req, r) - if _, ok := err.(*json.UnmarshalTypeError); ok { - // Multiple refs, means there wasn't an exact match. - return nil, resp, errors.New("no exact match found for this ref") - } else if err != nil { - return nil, resp, err - } - - return r, resp, nil -} - -// GetRefs fetches a slice of Reference objects for a given Git ref. -// If there is an exact match, only that ref is returned. -// If there is no exact match, GitHub returns all refs that start with ref. -// If returned error is nil, there will be at least 1 ref returned. -// For example: -// -// "heads/featureA" -> ["refs/heads/featureA"] // Exact match, single ref is returned. -// "heads/feature" -> ["refs/heads/featureA", "refs/heads/featureB"] // All refs that start with ref. -// "heads/notexist" -> [] // Returns an error. -// -// GitHub API docs: https://developer.github.com/v3/git/refs/#get-a-reference -func (s *GitService) GetRefs(ctx context.Context, owner string, repo string, ref string) ([]*Reference, *Response, error) { - ref = strings.TrimPrefix(ref, "refs/") - u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, ref) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var rawJSON json.RawMessage - resp, err := s.client.Do(ctx, req, &rawJSON) - if err != nil { - return nil, resp, err - } - - // Prioritize the most common case: a single returned ref. - r := new(Reference) - singleUnmarshalError := json.Unmarshal(rawJSON, r) - if singleUnmarshalError == nil { - return []*Reference{r}, resp, nil - } - - // Attempt to unmarshal multiple refs. - var rs []*Reference - multipleUnmarshalError := json.Unmarshal(rawJSON, &rs) - if multipleUnmarshalError == nil { - if len(rs) == 0 { - return nil, resp, fmt.Errorf("unexpected response from GitHub API: an array of refs with length 0") - } - return rs, resp, nil - } - - return nil, resp, fmt.Errorf("unmarshalling failed for both single and multiple refs: %s and %s", singleUnmarshalError, multipleUnmarshalError) -} - -// ReferenceListOptions specifies optional parameters to the -// GitService.ListRefs method. -type ReferenceListOptions struct { - Type string `url:"-"` - - ListOptions -} - -// ListRefs lists all refs in a repository. -// -// GitHub API docs: https://developer.github.com/v3/git/refs/#get-all-references -func (s *GitService) ListRefs(ctx context.Context, owner, repo string, opt *ReferenceListOptions) ([]*Reference, *Response, error) { - var u string - if opt != nil && opt.Type != "" { - u = fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, opt.Type) - } else { - u = fmt.Sprintf("repos/%v/%v/git/refs", owner, repo) - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var rs []*Reference - resp, err := s.client.Do(ctx, req, &rs) - if err != nil { - return nil, resp, err - } - - return rs, resp, nil -} - -// CreateRef creates a new ref in a repository. -// -// GitHub API docs: https://developer.github.com/v3/git/refs/#create-a-reference -func (s *GitService) CreateRef(ctx context.Context, owner string, repo string, ref *Reference) (*Reference, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/git/refs", owner, repo) - req, err := s.client.NewRequest("POST", u, &createRefRequest{ - // back-compat with previous behavior that didn't require 'refs/' prefix - Ref: String("refs/" + strings.TrimPrefix(*ref.Ref, "refs/")), - SHA: ref.Object.SHA, - }) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - r := new(Reference) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, nil -} - -// UpdateRef updates an existing ref in a repository. -// -// GitHub API docs: https://developer.github.com/v3/git/refs/#update-a-reference -func (s *GitService) UpdateRef(ctx context.Context, owner string, repo string, ref *Reference, force bool) (*Reference, *Response, error) { - refPath := strings.TrimPrefix(*ref.Ref, "refs/") - u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, refPath) - req, err := s.client.NewRequest("PATCH", u, &updateRefRequest{ - SHA: ref.Object.SHA, - Force: &force, - }) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - r := new(Reference) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, nil -} - -// DeleteRef deletes a ref from a repository. -// -// GitHub API docs: https://developer.github.com/v3/git/refs/#delete-a-reference -func (s *GitService) DeleteRef(ctx context.Context, owner string, repo string, ref string) (*Response, error) { - ref = strings.TrimPrefix(ref, "refs/") - u := fmt.Sprintf("repos/%v/%v/git/refs/%v", owner, repo, ref) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/git_tags.go b/vendor/github.com/google/go-github/github/git_tags.go deleted file mode 100644 index f3822ffaccc..00000000000 --- a/vendor/github.com/google/go-github/github/git_tags.go +++ /dev/null @@ -1,84 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" - "strings" -) - -// Tag represents a tag object. -type Tag struct { - Tag *string `json:"tag,omitempty"` - SHA *string `json:"sha,omitempty"` - URL *string `json:"url,omitempty"` - Message *string `json:"message,omitempty"` - Tagger *CommitAuthor `json:"tagger,omitempty"` - Object *GitObject `json:"object,omitempty"` - Verification *SignatureVerification `json:"verification,omitempty"` - NodeID *string `json:"node_id,omitempty"` -} - -// createTagRequest represents the body of a CreateTag request. This is mostly -// identical to Tag with the exception that the object SHA and Type are -// top-level fields, rather than being nested inside a JSON object. -type createTagRequest struct { - Tag *string `json:"tag,omitempty"` - Message *string `json:"message,omitempty"` - Object *string `json:"object,omitempty"` - Type *string `json:"type,omitempty"` - Tagger *CommitAuthor `json:"tagger,omitempty"` -} - -// GetTag fetchs a tag from a repo given a SHA. -// -// GitHub API docs: https://developer.github.com/v3/git/tags/#get-a-tag -func (s *GitService) GetTag(ctx context.Context, owner string, repo string, sha string) (*Tag, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/git/tags/%v", owner, repo, sha) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeGitSigningPreview, mediaTypeGraphQLNodeIDPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - tag := new(Tag) - resp, err := s.client.Do(ctx, req, tag) - return tag, resp, err -} - -// CreateTag creates a tag object. -// -// GitHub API docs: https://developer.github.com/v3/git/tags/#create-a-tag-object -func (s *GitService) CreateTag(ctx context.Context, owner string, repo string, tag *Tag) (*Tag, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/git/tags", owner, repo) - - // convert Tag into a createTagRequest - tagRequest := &createTagRequest{ - Tag: tag.Tag, - Message: tag.Message, - Tagger: tag.Tagger, - } - if tag.Object != nil { - tagRequest.Object = tag.Object.SHA - tagRequest.Type = tag.Object.Type - } - - req, err := s.client.NewRequest("POST", u, tagRequest) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - t := new(Tag) - resp, err := s.client.Do(ctx, req, t) - return t, resp, err -} diff --git a/vendor/github.com/google/go-github/github/git_trees.go b/vendor/github.com/google/go-github/github/git_trees.go deleted file mode 100644 index 4d6809a880e..00000000000 --- a/vendor/github.com/google/go-github/github/git_trees.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// Tree represents a GitHub tree. -type Tree struct { - SHA *string `json:"sha,omitempty"` - Entries []TreeEntry `json:"tree,omitempty"` -} - -func (t Tree) String() string { - return Stringify(t) -} - -// TreeEntry represents the contents of a tree structure. TreeEntry can -// represent either a blob, a commit (in the case of a submodule), or another -// tree. -type TreeEntry struct { - SHA *string `json:"sha,omitempty"` - Path *string `json:"path,omitempty"` - Mode *string `json:"mode,omitempty"` - Type *string `json:"type,omitempty"` - Size *int `json:"size,omitempty"` - Content *string `json:"content,omitempty"` - URL *string `json:"url,omitempty"` -} - -func (t TreeEntry) String() string { - return Stringify(t) -} - -// GetTree fetches the Tree object for a given sha hash from a repository. -// -// GitHub API docs: https://developer.github.com/v3/git/trees/#get-a-tree -func (s *GitService) GetTree(ctx context.Context, owner string, repo string, sha string, recursive bool) (*Tree, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/git/trees/%v", owner, repo, sha) - if recursive { - u += "?recursive=1" - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - t := new(Tree) - resp, err := s.client.Do(ctx, req, t) - if err != nil { - return nil, resp, err - } - - return t, resp, nil -} - -// createTree represents the body of a CreateTree request. -type createTree struct { - BaseTree string `json:"base_tree,omitempty"` - Entries []TreeEntry `json:"tree"` -} - -// CreateTree creates a new tree in a repository. If both a tree and a nested -// path modifying that tree are specified, it will overwrite the contents of -// that tree with the new path contents and write a new tree out. -// -// GitHub API docs: https://developer.github.com/v3/git/trees/#create-a-tree -func (s *GitService) CreateTree(ctx context.Context, owner string, repo string, baseTree string, entries []TreeEntry) (*Tree, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/git/trees", owner, repo) - - body := &createTree{ - BaseTree: baseTree, - Entries: entries, - } - req, err := s.client.NewRequest("POST", u, body) - if err != nil { - return nil, nil, err - } - - t := new(Tree) - resp, err := s.client.Do(ctx, req, t) - if err != nil { - return nil, resp, err - } - - return t, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/github-accessors.go b/vendor/github.com/google/go-github/github/github-accessors.go deleted file mode 100644 index 30b7673977b..00000000000 --- a/vendor/github.com/google/go-github/github/github-accessors.go +++ /dev/null @@ -1,10429 +0,0 @@ -// Copyright 2017 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Code generated by gen-accessors; DO NOT EDIT. - -package github - -import ( - "encoding/json" - "time" -) - -// GetRetryAfter returns the RetryAfter field if it's non-nil, zero value otherwise. -func (a *AbuseRateLimitError) GetRetryAfter() time.Duration { - if a == nil || a.RetryAfter == nil { - return 0 - } - return *a.RetryAfter -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (a *AdminEnforcement) GetURL() string { - if a == nil || a.URL == nil { - return "" - } - return *a.URL -} - -// GetComments returns the Comments field. -func (a *AdminStats) GetComments() *CommentStats { - if a == nil { - return nil - } - return a.Comments -} - -// GetGists returns the Gists field. -func (a *AdminStats) GetGists() *GistStats { - if a == nil { - return nil - } - return a.Gists -} - -// GetHooks returns the Hooks field. -func (a *AdminStats) GetHooks() *HookStats { - if a == nil { - return nil - } - return a.Hooks -} - -// GetIssues returns the Issues field. -func (a *AdminStats) GetIssues() *IssueStats { - if a == nil { - return nil - } - return a.Issues -} - -// GetMilestones returns the Milestones field. -func (a *AdminStats) GetMilestones() *MilestoneStats { - if a == nil { - return nil - } - return a.Milestones -} - -// GetOrgs returns the Orgs field. -func (a *AdminStats) GetOrgs() *OrgStats { - if a == nil { - return nil - } - return a.Orgs -} - -// GetPages returns the Pages field. -func (a *AdminStats) GetPages() *PageStats { - if a == nil { - return nil - } - return a.Pages -} - -// GetPulls returns the Pulls field. -func (a *AdminStats) GetPulls() *PullStats { - if a == nil { - return nil - } - return a.Pulls -} - -// GetRepos returns the Repos field. -func (a *AdminStats) GetRepos() *RepoStats { - if a == nil { - return nil - } - return a.Repos -} - -// GetUsers returns the Users field. -func (a *AdminStats) GetUsers() *UserStats { - if a == nil { - return nil - } - return a.Users -} - -// GetVerifiablePasswordAuthentication returns the VerifiablePasswordAuthentication field if it's non-nil, zero value otherwise. -func (a *APIMeta) GetVerifiablePasswordAuthentication() bool { - if a == nil || a.VerifiablePasswordAuthentication == nil { - return false - } - return *a.VerifiablePasswordAuthentication -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (a *App) GetCreatedAt() time.Time { - if a == nil || a.CreatedAt == nil { - return time.Time{} - } - return *a.CreatedAt -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (a *App) GetDescription() string { - if a == nil || a.Description == nil { - return "" - } - return *a.Description -} - -// GetExternalURL returns the ExternalURL field if it's non-nil, zero value otherwise. -func (a *App) GetExternalURL() string { - if a == nil || a.ExternalURL == nil { - return "" - } - return *a.ExternalURL -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (a *App) GetHTMLURL() string { - if a == nil || a.HTMLURL == nil { - return "" - } - return *a.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (a *App) GetID() int64 { - if a == nil || a.ID == nil { - return 0 - } - return *a.ID -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (a *App) GetName() string { - if a == nil || a.Name == nil { - return "" - } - return *a.Name -} - -// GetOwner returns the Owner field. -func (a *App) GetOwner() *User { - if a == nil { - return nil - } - return a.Owner -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (a *App) GetUpdatedAt() time.Time { - if a == nil || a.UpdatedAt == nil { - return time.Time{} - } - return *a.UpdatedAt -} - -// GetApp returns the App field. -func (a *Authorization) GetApp() *AuthorizationApp { - if a == nil { - return nil - } - return a.App -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (a *Authorization) GetCreatedAt() Timestamp { - if a == nil || a.CreatedAt == nil { - return Timestamp{} - } - return *a.CreatedAt -} - -// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. -func (a *Authorization) GetFingerprint() string { - if a == nil || a.Fingerprint == nil { - return "" - } - return *a.Fingerprint -} - -// GetHashedToken returns the HashedToken field if it's non-nil, zero value otherwise. -func (a *Authorization) GetHashedToken() string { - if a == nil || a.HashedToken == nil { - return "" - } - return *a.HashedToken -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (a *Authorization) GetID() int64 { - if a == nil || a.ID == nil { - return 0 - } - return *a.ID -} - -// GetNote returns the Note field if it's non-nil, zero value otherwise. -func (a *Authorization) GetNote() string { - if a == nil || a.Note == nil { - return "" - } - return *a.Note -} - -// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise. -func (a *Authorization) GetNoteURL() string { - if a == nil || a.NoteURL == nil { - return "" - } - return *a.NoteURL -} - -// GetToken returns the Token field if it's non-nil, zero value otherwise. -func (a *Authorization) GetToken() string { - if a == nil || a.Token == nil { - return "" - } - return *a.Token -} - -// GetTokenLastEight returns the TokenLastEight field if it's non-nil, zero value otherwise. -func (a *Authorization) GetTokenLastEight() string { - if a == nil || a.TokenLastEight == nil { - return "" - } - return *a.TokenLastEight -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (a *Authorization) GetUpdatedAt() Timestamp { - if a == nil || a.UpdatedAt == nil { - return Timestamp{} - } - return *a.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (a *Authorization) GetURL() string { - if a == nil || a.URL == nil { - return "" - } - return *a.URL -} - -// GetUser returns the User field. -func (a *Authorization) GetUser() *User { - if a == nil { - return nil - } - return a.User -} - -// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. -func (a *AuthorizationApp) GetClientID() string { - if a == nil || a.ClientID == nil { - return "" - } - return *a.ClientID -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (a *AuthorizationApp) GetName() string { - if a == nil || a.Name == nil { - return "" - } - return *a.Name -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (a *AuthorizationApp) GetURL() string { - if a == nil || a.URL == nil { - return "" - } - return *a.URL -} - -// GetClientID returns the ClientID field if it's non-nil, zero value otherwise. -func (a *AuthorizationRequest) GetClientID() string { - if a == nil || a.ClientID == nil { - return "" - } - return *a.ClientID -} - -// GetClientSecret returns the ClientSecret field if it's non-nil, zero value otherwise. -func (a *AuthorizationRequest) GetClientSecret() string { - if a == nil || a.ClientSecret == nil { - return "" - } - return *a.ClientSecret -} - -// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. -func (a *AuthorizationRequest) GetFingerprint() string { - if a == nil || a.Fingerprint == nil { - return "" - } - return *a.Fingerprint -} - -// GetNote returns the Note field if it's non-nil, zero value otherwise. -func (a *AuthorizationRequest) GetNote() string { - if a == nil || a.Note == nil { - return "" - } - return *a.Note -} - -// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise. -func (a *AuthorizationRequest) GetNoteURL() string { - if a == nil || a.NoteURL == nil { - return "" - } - return *a.NoteURL -} - -// GetFingerprint returns the Fingerprint field if it's non-nil, zero value otherwise. -func (a *AuthorizationUpdateRequest) GetFingerprint() string { - if a == nil || a.Fingerprint == nil { - return "" - } - return *a.Fingerprint -} - -// GetNote returns the Note field if it's non-nil, zero value otherwise. -func (a *AuthorizationUpdateRequest) GetNote() string { - if a == nil || a.Note == nil { - return "" - } - return *a.Note -} - -// GetNoteURL returns the NoteURL field if it's non-nil, zero value otherwise. -func (a *AuthorizationUpdateRequest) GetNoteURL() string { - if a == nil || a.NoteURL == nil { - return "" - } - return *a.NoteURL -} - -// GetContent returns the Content field if it's non-nil, zero value otherwise. -func (b *Blob) GetContent() string { - if b == nil || b.Content == nil { - return "" - } - return *b.Content -} - -// GetEncoding returns the Encoding field if it's non-nil, zero value otherwise. -func (b *Blob) GetEncoding() string { - if b == nil || b.Encoding == nil { - return "" - } - return *b.Encoding -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (b *Blob) GetNodeID() string { - if b == nil || b.NodeID == nil { - return "" - } - return *b.NodeID -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (b *Blob) GetSHA() string { - if b == nil || b.SHA == nil { - return "" - } - return *b.SHA -} - -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (b *Blob) GetSize() int { - if b == nil || b.Size == nil { - return 0 - } - return *b.Size -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (b *Blob) GetURL() string { - if b == nil || b.URL == nil { - return "" - } - return *b.URL -} - -// GetCommit returns the Commit field. -func (b *Branch) GetCommit() *RepositoryCommit { - if b == nil { - return nil - } - return b.Commit -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (b *Branch) GetName() string { - if b == nil || b.Name == nil { - return "" - } - return *b.Name -} - -// GetProtected returns the Protected field if it's non-nil, zero value otherwise. -func (b *Branch) GetProtected() bool { - if b == nil || b.Protected == nil { - return false - } - return *b.Protected -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (c *CodeOfConduct) GetBody() string { - if c == nil || c.Body == nil { - return "" - } - return *c.Body -} - -// GetKey returns the Key field if it's non-nil, zero value otherwise. -func (c *CodeOfConduct) GetKey() string { - if c == nil || c.Key == nil { - return "" - } - return *c.Key -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (c *CodeOfConduct) GetName() string { - if c == nil || c.Name == nil { - return "" - } - return *c.Name -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (c *CodeOfConduct) GetURL() string { - if c == nil || c.URL == nil { - return "" - } - return *c.URL -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (c *CodeResult) GetHTMLURL() string { - if c == nil || c.HTMLURL == nil { - return "" - } - return *c.HTMLURL -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (c *CodeResult) GetName() string { - if c == nil || c.Name == nil { - return "" - } - return *c.Name -} - -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (c *CodeResult) GetPath() string { - if c == nil || c.Path == nil { - return "" - } - return *c.Path -} - -// GetRepository returns the Repository field. -func (c *CodeResult) GetRepository() *Repository { - if c == nil { - return nil - } - return c.Repository -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (c *CodeResult) GetSHA() string { - if c == nil || c.SHA == nil { - return "" - } - return *c.SHA -} - -// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. -func (c *CodeSearchResult) GetIncompleteResults() bool { - if c == nil || c.IncompleteResults == nil { - return false - } - return *c.IncompleteResults -} - -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (c *CodeSearchResult) GetTotal() int { - if c == nil || c.Total == nil { - return 0 - } - return *c.Total -} - -// GetCommitURL returns the CommitURL field if it's non-nil, zero value otherwise. -func (c *CombinedStatus) GetCommitURL() string { - if c == nil || c.CommitURL == nil { - return "" - } - return *c.CommitURL -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (c *CombinedStatus) GetName() string { - if c == nil || c.Name == nil { - return "" - } - return *c.Name -} - -// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. -func (c *CombinedStatus) GetRepositoryURL() string { - if c == nil || c.RepositoryURL == nil { - return "" - } - return *c.RepositoryURL -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (c *CombinedStatus) GetSHA() string { - if c == nil || c.SHA == nil { - return "" - } - return *c.SHA -} - -// GetState returns the State field if it's non-nil, zero value otherwise. -func (c *CombinedStatus) GetState() string { - if c == nil || c.State == nil { - return "" - } - return *c.State -} - -// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. -func (c *CombinedStatus) GetTotalCount() int { - if c == nil || c.TotalCount == nil { - return 0 - } - return *c.TotalCount -} - -// GetTotalCommitComments returns the TotalCommitComments field if it's non-nil, zero value otherwise. -func (c *CommentStats) GetTotalCommitComments() int { - if c == nil || c.TotalCommitComments == nil { - return 0 - } - return *c.TotalCommitComments -} - -// GetTotalGistComments returns the TotalGistComments field if it's non-nil, zero value otherwise. -func (c *CommentStats) GetTotalGistComments() int { - if c == nil || c.TotalGistComments == nil { - return 0 - } - return *c.TotalGistComments -} - -// GetTotalIssueComments returns the TotalIssueComments field if it's non-nil, zero value otherwise. -func (c *CommentStats) GetTotalIssueComments() int { - if c == nil || c.TotalIssueComments == nil { - return 0 - } - return *c.TotalIssueComments -} - -// GetTotalPullRequestComments returns the TotalPullRequestComments field if it's non-nil, zero value otherwise. -func (c *CommentStats) GetTotalPullRequestComments() int { - if c == nil || c.TotalPullRequestComments == nil { - return 0 - } - return *c.TotalPullRequestComments -} - -// GetAuthor returns the Author field. -func (c *Commit) GetAuthor() *CommitAuthor { - if c == nil { - return nil - } - return c.Author -} - -// GetCommentCount returns the CommentCount field if it's non-nil, zero value otherwise. -func (c *Commit) GetCommentCount() int { - if c == nil || c.CommentCount == nil { - return 0 - } - return *c.CommentCount -} - -// GetCommitter returns the Committer field. -func (c *Commit) GetCommitter() *CommitAuthor { - if c == nil { - return nil - } - return c.Committer -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (c *Commit) GetHTMLURL() string { - if c == nil || c.HTMLURL == nil { - return "" - } - return *c.HTMLURL -} - -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (c *Commit) GetMessage() string { - if c == nil || c.Message == nil { - return "" - } - return *c.Message -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (c *Commit) GetNodeID() string { - if c == nil || c.NodeID == nil { - return "" - } - return *c.NodeID -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (c *Commit) GetSHA() string { - if c == nil || c.SHA == nil { - return "" - } - return *c.SHA -} - -// GetStats returns the Stats field. -func (c *Commit) GetStats() *CommitStats { - if c == nil { - return nil - } - return c.Stats -} - -// GetTree returns the Tree field. -func (c *Commit) GetTree() *Tree { - if c == nil { - return nil - } - return c.Tree -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (c *Commit) GetURL() string { - if c == nil || c.URL == nil { - return "" - } - return *c.URL -} - -// GetVerification returns the Verification field. -func (c *Commit) GetVerification() *SignatureVerification { - if c == nil { - return nil - } - return c.Verification -} - -// GetDate returns the Date field if it's non-nil, zero value otherwise. -func (c *CommitAuthor) GetDate() time.Time { - if c == nil || c.Date == nil { - return time.Time{} - } - return *c.Date -} - -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (c *CommitAuthor) GetEmail() string { - if c == nil || c.Email == nil { - return "" - } - return *c.Email -} - -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (c *CommitAuthor) GetLogin() string { - if c == nil || c.Login == nil { - return "" - } - return *c.Login -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (c *CommitAuthor) GetName() string { - if c == nil || c.Name == nil { - return "" - } - return *c.Name -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (c *CommitCommentEvent) GetAction() string { - if c == nil || c.Action == nil { - return "" - } - return *c.Action -} - -// GetComment returns the Comment field. -func (c *CommitCommentEvent) GetComment() *RepositoryComment { - if c == nil { - return nil - } - return c.Comment -} - -// GetInstallation returns the Installation field. -func (c *CommitCommentEvent) GetInstallation() *Installation { - if c == nil { - return nil - } - return c.Installation -} - -// GetRepo returns the Repo field. -func (c *CommitCommentEvent) GetRepo() *Repository { - if c == nil { - return nil - } - return c.Repo -} - -// GetSender returns the Sender field. -func (c *CommitCommentEvent) GetSender() *User { - if c == nil { - return nil - } - return c.Sender -} - -// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetAdditions() int { - if c == nil || c.Additions == nil { - return 0 - } - return *c.Additions -} - -// GetBlobURL returns the BlobURL field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetBlobURL() string { - if c == nil || c.BlobURL == nil { - return "" - } - return *c.BlobURL -} - -// GetChanges returns the Changes field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetChanges() int { - if c == nil || c.Changes == nil { - return 0 - } - return *c.Changes -} - -// GetContentsURL returns the ContentsURL field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetContentsURL() string { - if c == nil || c.ContentsURL == nil { - return "" - } - return *c.ContentsURL -} - -// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetDeletions() int { - if c == nil || c.Deletions == nil { - return 0 - } - return *c.Deletions -} - -// GetFilename returns the Filename field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetFilename() string { - if c == nil || c.Filename == nil { - return "" - } - return *c.Filename -} - -// GetPatch returns the Patch field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetPatch() string { - if c == nil || c.Patch == nil { - return "" - } - return *c.Patch -} - -// GetRawURL returns the RawURL field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetRawURL() string { - if c == nil || c.RawURL == nil { - return "" - } - return *c.RawURL -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetSHA() string { - if c == nil || c.SHA == nil { - return "" - } - return *c.SHA -} - -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (c *CommitFile) GetStatus() string { - if c == nil || c.Status == nil { - return "" - } - return *c.Status -} - -// GetAuthor returns the Author field. -func (c *CommitResult) GetAuthor() *User { - if c == nil { - return nil - } - return c.Author -} - -// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. -func (c *CommitResult) GetCommentsURL() string { - if c == nil || c.CommentsURL == nil { - return "" - } - return *c.CommentsURL -} - -// GetCommit returns the Commit field. -func (c *CommitResult) GetCommit() *Commit { - if c == nil { - return nil - } - return c.Commit -} - -// GetCommitter returns the Committer field. -func (c *CommitResult) GetCommitter() *User { - if c == nil { - return nil - } - return c.Committer -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (c *CommitResult) GetHTMLURL() string { - if c == nil || c.HTMLURL == nil { - return "" - } - return *c.HTMLURL -} - -// GetRepository returns the Repository field. -func (c *CommitResult) GetRepository() *Repository { - if c == nil { - return nil - } - return c.Repository -} - -// GetScore returns the Score field. -func (c *CommitResult) GetScore() *float64 { - if c == nil { - return nil - } - return c.Score -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (c *CommitResult) GetSHA() string { - if c == nil || c.SHA == nil { - return "" - } - return *c.SHA -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (c *CommitResult) GetURL() string { - if c == nil || c.URL == nil { - return "" - } - return *c.URL -} - -// GetAheadBy returns the AheadBy field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetAheadBy() int { - if c == nil || c.AheadBy == nil { - return 0 - } - return *c.AheadBy -} - -// GetBaseCommit returns the BaseCommit field. -func (c *CommitsComparison) GetBaseCommit() *RepositoryCommit { - if c == nil { - return nil - } - return c.BaseCommit -} - -// GetBehindBy returns the BehindBy field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetBehindBy() int { - if c == nil || c.BehindBy == nil { - return 0 - } - return *c.BehindBy -} - -// GetDiffURL returns the DiffURL field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetDiffURL() string { - if c == nil || c.DiffURL == nil { - return "" - } - return *c.DiffURL -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetHTMLURL() string { - if c == nil || c.HTMLURL == nil { - return "" - } - return *c.HTMLURL -} - -// GetMergeBaseCommit returns the MergeBaseCommit field. -func (c *CommitsComparison) GetMergeBaseCommit() *RepositoryCommit { - if c == nil { - return nil - } - return c.MergeBaseCommit -} - -// GetPatchURL returns the PatchURL field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetPatchURL() string { - if c == nil || c.PatchURL == nil { - return "" - } - return *c.PatchURL -} - -// GetPermalinkURL returns the PermalinkURL field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetPermalinkURL() string { - if c == nil || c.PermalinkURL == nil { - return "" - } - return *c.PermalinkURL -} - -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetStatus() string { - if c == nil || c.Status == nil { - return "" - } - return *c.Status -} - -// GetTotalCommits returns the TotalCommits field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetTotalCommits() int { - if c == nil || c.TotalCommits == nil { - return 0 - } - return *c.TotalCommits -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (c *CommitsComparison) GetURL() string { - if c == nil || c.URL == nil { - return "" - } - return *c.URL -} - -// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. -func (c *CommitsSearchResult) GetIncompleteResults() bool { - if c == nil || c.IncompleteResults == nil { - return false - } - return *c.IncompleteResults -} - -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (c *CommitsSearchResult) GetTotal() int { - if c == nil || c.Total == nil { - return 0 - } - return *c.Total -} - -// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. -func (c *CommitStats) GetAdditions() int { - if c == nil || c.Additions == nil { - return 0 - } - return *c.Additions -} - -// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. -func (c *CommitStats) GetDeletions() int { - if c == nil || c.Deletions == nil { - return 0 - } - return *c.Deletions -} - -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (c *CommitStats) GetTotal() int { - if c == nil || c.Total == nil { - return 0 - } - return *c.Total -} - -// GetCodeOfConduct returns the CodeOfConduct field. -func (c *CommunityHealthFiles) GetCodeOfConduct() *Metric { - if c == nil { - return nil - } - return c.CodeOfConduct -} - -// GetContributing returns the Contributing field. -func (c *CommunityHealthFiles) GetContributing() *Metric { - if c == nil { - return nil - } - return c.Contributing -} - -// GetLicense returns the License field. -func (c *CommunityHealthFiles) GetLicense() *Metric { - if c == nil { - return nil - } - return c.License -} - -// GetReadme returns the Readme field. -func (c *CommunityHealthFiles) GetReadme() *Metric { - if c == nil { - return nil - } - return c.Readme -} - -// GetFiles returns the Files field. -func (c *CommunityHealthMetrics) GetFiles() *CommunityHealthFiles { - if c == nil { - return nil - } - return c.Files -} - -// GetHealthPercentage returns the HealthPercentage field if it's non-nil, zero value otherwise. -func (c *CommunityHealthMetrics) GetHealthPercentage() int { - if c == nil || c.HealthPercentage == nil { - return 0 - } - return *c.HealthPercentage -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (c *CommunityHealthMetrics) GetUpdatedAt() time.Time { - if c == nil || c.UpdatedAt == nil { - return time.Time{} - } - return *c.UpdatedAt -} - -// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetAvatarURL() string { - if c == nil || c.AvatarURL == nil { - return "" - } - return *c.AvatarURL -} - -// GetContributions returns the Contributions field if it's non-nil, zero value otherwise. -func (c *Contributor) GetContributions() int { - if c == nil || c.Contributions == nil { - return 0 - } - return *c.Contributions -} - -// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetEventsURL() string { - if c == nil || c.EventsURL == nil { - return "" - } - return *c.EventsURL -} - -// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetFollowersURL() string { - if c == nil || c.FollowersURL == nil { - return "" - } - return *c.FollowersURL -} - -// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetFollowingURL() string { - if c == nil || c.FollowingURL == nil { - return "" - } - return *c.FollowingURL -} - -// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetGistsURL() string { - if c == nil || c.GistsURL == nil { - return "" - } - return *c.GistsURL -} - -// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. -func (c *Contributor) GetGravatarID() string { - if c == nil || c.GravatarID == nil { - return "" - } - return *c.GravatarID -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetHTMLURL() string { - if c == nil || c.HTMLURL == nil { - return "" - } - return *c.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (c *Contributor) GetID() int64 { - if c == nil || c.ID == nil { - return 0 - } - return *c.ID -} - -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (c *Contributor) GetLogin() string { - if c == nil || c.Login == nil { - return "" - } - return *c.Login -} - -// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetOrganizationsURL() string { - if c == nil || c.OrganizationsURL == nil { - return "" - } - return *c.OrganizationsURL -} - -// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetReceivedEventsURL() string { - if c == nil || c.ReceivedEventsURL == nil { - return "" - } - return *c.ReceivedEventsURL -} - -// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetReposURL() string { - if c == nil || c.ReposURL == nil { - return "" - } - return *c.ReposURL -} - -// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. -func (c *Contributor) GetSiteAdmin() bool { - if c == nil || c.SiteAdmin == nil { - return false - } - return *c.SiteAdmin -} - -// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetStarredURL() string { - if c == nil || c.StarredURL == nil { - return "" - } - return *c.StarredURL -} - -// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetSubscriptionsURL() string { - if c == nil || c.SubscriptionsURL == nil { - return "" - } - return *c.SubscriptionsURL -} - -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (c *Contributor) GetType() string { - if c == nil || c.Type == nil { - return "" - } - return *c.Type -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (c *Contributor) GetURL() string { - if c == nil || c.URL == nil { - return "" - } - return *c.URL -} - -// GetAuthor returns the Author field. -func (c *ContributorStats) GetAuthor() *Contributor { - if c == nil { - return nil - } - return c.Author -} - -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (c *ContributorStats) GetTotal() int { - if c == nil || c.Total == nil { - return 0 - } - return *c.Total -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (c *CreateEvent) GetDescription() string { - if c == nil || c.Description == nil { - return "" - } - return *c.Description -} - -// GetInstallation returns the Installation field. -func (c *CreateEvent) GetInstallation() *Installation { - if c == nil { - return nil - } - return c.Installation -} - -// GetMasterBranch returns the MasterBranch field if it's non-nil, zero value otherwise. -func (c *CreateEvent) GetMasterBranch() string { - if c == nil || c.MasterBranch == nil { - return "" - } - return *c.MasterBranch -} - -// GetPusherType returns the PusherType field if it's non-nil, zero value otherwise. -func (c *CreateEvent) GetPusherType() string { - if c == nil || c.PusherType == nil { - return "" - } - return *c.PusherType -} - -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (c *CreateEvent) GetRef() string { - if c == nil || c.Ref == nil { - return "" - } - return *c.Ref -} - -// GetRefType returns the RefType field if it's non-nil, zero value otherwise. -func (c *CreateEvent) GetRefType() string { - if c == nil || c.RefType == nil { - return "" - } - return *c.RefType -} - -// GetRepo returns the Repo field. -func (c *CreateEvent) GetRepo() *Repository { - if c == nil { - return nil - } - return c.Repo -} - -// GetSender returns the Sender field. -func (c *CreateEvent) GetSender() *User { - if c == nil { - return nil - } - return c.Sender -} - -// GetInstallation returns the Installation field. -func (d *DeleteEvent) GetInstallation() *Installation { - if d == nil { - return nil - } - return d.Installation -} - -// GetPusherType returns the PusherType field if it's non-nil, zero value otherwise. -func (d *DeleteEvent) GetPusherType() string { - if d == nil || d.PusherType == nil { - return "" - } - return *d.PusherType -} - -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (d *DeleteEvent) GetRef() string { - if d == nil || d.Ref == nil { - return "" - } - return *d.Ref -} - -// GetRefType returns the RefType field if it's non-nil, zero value otherwise. -func (d *DeleteEvent) GetRefType() string { - if d == nil || d.RefType == nil { - return "" - } - return *d.RefType -} - -// GetRepo returns the Repo field. -func (d *DeleteEvent) GetRepo() *Repository { - if d == nil { - return nil - } - return d.Repo -} - -// GetSender returns the Sender field. -func (d *DeleteEvent) GetSender() *User { - if d == nil { - return nil - } - return d.Sender -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (d *Deployment) GetCreatedAt() Timestamp { - if d == nil || d.CreatedAt == nil { - return Timestamp{} - } - return *d.CreatedAt -} - -// GetCreator returns the Creator field. -func (d *Deployment) GetCreator() *User { - if d == nil { - return nil - } - return d.Creator -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (d *Deployment) GetDescription() string { - if d == nil || d.Description == nil { - return "" - } - return *d.Description -} - -// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. -func (d *Deployment) GetEnvironment() string { - if d == nil || d.Environment == nil { - return "" - } - return *d.Environment -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (d *Deployment) GetID() int64 { - if d == nil || d.ID == nil { - return 0 - } - return *d.ID -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (d *Deployment) GetNodeID() string { - if d == nil || d.NodeID == nil { - return "" - } - return *d.NodeID -} - -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (d *Deployment) GetRef() string { - if d == nil || d.Ref == nil { - return "" - } - return *d.Ref -} - -// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. -func (d *Deployment) GetRepositoryURL() string { - if d == nil || d.RepositoryURL == nil { - return "" - } - return *d.RepositoryURL -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (d *Deployment) GetSHA() string { - if d == nil || d.SHA == nil { - return "" - } - return *d.SHA -} - -// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. -func (d *Deployment) GetStatusesURL() string { - if d == nil || d.StatusesURL == nil { - return "" - } - return *d.StatusesURL -} - -// GetTask returns the Task field if it's non-nil, zero value otherwise. -func (d *Deployment) GetTask() string { - if d == nil || d.Task == nil { - return "" - } - return *d.Task -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (d *Deployment) GetUpdatedAt() Timestamp { - if d == nil || d.UpdatedAt == nil { - return Timestamp{} - } - return *d.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (d *Deployment) GetURL() string { - if d == nil || d.URL == nil { - return "" - } - return *d.URL -} - -// GetDeployment returns the Deployment field. -func (d *DeploymentEvent) GetDeployment() *Deployment { - if d == nil { - return nil - } - return d.Deployment -} - -// GetInstallation returns the Installation field. -func (d *DeploymentEvent) GetInstallation() *Installation { - if d == nil { - return nil - } - return d.Installation -} - -// GetRepo returns the Repo field. -func (d *DeploymentEvent) GetRepo() *Repository { - if d == nil { - return nil - } - return d.Repo -} - -// GetSender returns the Sender field. -func (d *DeploymentEvent) GetSender() *User { - if d == nil { - return nil - } - return d.Sender -} - -// GetAutoMerge returns the AutoMerge field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetAutoMerge() bool { - if d == nil || d.AutoMerge == nil { - return false - } - return *d.AutoMerge -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetDescription() string { - if d == nil || d.Description == nil { - return "" - } - return *d.Description -} - -// GetEnvironment returns the Environment field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetEnvironment() string { - if d == nil || d.Environment == nil { - return "" - } - return *d.Environment -} - -// GetPayload returns the Payload field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetPayload() string { - if d == nil || d.Payload == nil { - return "" - } - return *d.Payload -} - -// GetProductionEnvironment returns the ProductionEnvironment field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetProductionEnvironment() bool { - if d == nil || d.ProductionEnvironment == nil { - return false - } - return *d.ProductionEnvironment -} - -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetRef() string { - if d == nil || d.Ref == nil { - return "" - } - return *d.Ref -} - -// GetRequiredContexts returns the RequiredContexts field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetRequiredContexts() []string { - if d == nil || d.RequiredContexts == nil { - return nil - } - return *d.RequiredContexts -} - -// GetTask returns the Task field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetTask() string { - if d == nil || d.Task == nil { - return "" - } - return *d.Task -} - -// GetTransientEnvironment returns the TransientEnvironment field if it's non-nil, zero value otherwise. -func (d *DeploymentRequest) GetTransientEnvironment() bool { - if d == nil || d.TransientEnvironment == nil { - return false - } - return *d.TransientEnvironment -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetCreatedAt() Timestamp { - if d == nil || d.CreatedAt == nil { - return Timestamp{} - } - return *d.CreatedAt -} - -// GetCreator returns the Creator field. -func (d *DeploymentStatus) GetCreator() *User { - if d == nil { - return nil - } - return d.Creator -} - -// GetDeploymentURL returns the DeploymentURL field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetDeploymentURL() string { - if d == nil || d.DeploymentURL == nil { - return "" - } - return *d.DeploymentURL -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetDescription() string { - if d == nil || d.Description == nil { - return "" - } - return *d.Description -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetID() int64 { - if d == nil || d.ID == nil { - return 0 - } - return *d.ID -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetNodeID() string { - if d == nil || d.NodeID == nil { - return "" - } - return *d.NodeID -} - -// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetRepositoryURL() string { - if d == nil || d.RepositoryURL == nil { - return "" - } - return *d.RepositoryURL -} - -// GetState returns the State field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetState() string { - if d == nil || d.State == nil { - return "" - } - return *d.State -} - -// GetTargetURL returns the TargetURL field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetTargetURL() string { - if d == nil || d.TargetURL == nil { - return "" - } - return *d.TargetURL -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (d *DeploymentStatus) GetUpdatedAt() Timestamp { - if d == nil || d.UpdatedAt == nil { - return Timestamp{} - } - return *d.UpdatedAt -} - -// GetDeployment returns the Deployment field. -func (d *DeploymentStatusEvent) GetDeployment() *Deployment { - if d == nil { - return nil - } - return d.Deployment -} - -// GetDeploymentStatus returns the DeploymentStatus field. -func (d *DeploymentStatusEvent) GetDeploymentStatus() *DeploymentStatus { - if d == nil { - return nil - } - return d.DeploymentStatus -} - -// GetInstallation returns the Installation field. -func (d *DeploymentStatusEvent) GetInstallation() *Installation { - if d == nil { - return nil - } - return d.Installation -} - -// GetRepo returns the Repo field. -func (d *DeploymentStatusEvent) GetRepo() *Repository { - if d == nil { - return nil - } - return d.Repo -} - -// GetSender returns the Sender field. -func (d *DeploymentStatusEvent) GetSender() *User { - if d == nil { - return nil - } - return d.Sender -} - -// GetAutoInactive returns the AutoInactive field if it's non-nil, zero value otherwise. -func (d *DeploymentStatusRequest) GetAutoInactive() bool { - if d == nil || d.AutoInactive == nil { - return false - } - return *d.AutoInactive -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (d *DeploymentStatusRequest) GetDescription() string { - if d == nil || d.Description == nil { - return "" - } - return *d.Description -} - -// GetEnvironmentURL returns the EnvironmentURL field if it's non-nil, zero value otherwise. -func (d *DeploymentStatusRequest) GetEnvironmentURL() string { - if d == nil || d.EnvironmentURL == nil { - return "" - } - return *d.EnvironmentURL -} - -// GetLogURL returns the LogURL field if it's non-nil, zero value otherwise. -func (d *DeploymentStatusRequest) GetLogURL() string { - if d == nil || d.LogURL == nil { - return "" - } - return *d.LogURL -} - -// GetState returns the State field if it's non-nil, zero value otherwise. -func (d *DeploymentStatusRequest) GetState() string { - if d == nil || d.State == nil { - return "" - } - return *d.State -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (d *DraftReviewComment) GetBody() string { - if d == nil || d.Body == nil { - return "" - } - return *d.Body -} - -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (d *DraftReviewComment) GetPath() string { - if d == nil || d.Path == nil { - return "" - } - return *d.Path -} - -// GetPosition returns the Position field if it's non-nil, zero value otherwise. -func (d *DraftReviewComment) GetPosition() int { - if d == nil || d.Position == nil { - return 0 - } - return *d.Position -} - -// GetActor returns the Actor field. -func (e *Event) GetActor() *User { - if e == nil { - return nil - } - return e.Actor -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (e *Event) GetCreatedAt() time.Time { - if e == nil || e.CreatedAt == nil { - return time.Time{} - } - return *e.CreatedAt -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (e *Event) GetID() string { - if e == nil || e.ID == nil { - return "" - } - return *e.ID -} - -// GetOrg returns the Org field. -func (e *Event) GetOrg() *Organization { - if e == nil { - return nil - } - return e.Org -} - -// GetPublic returns the Public field if it's non-nil, zero value otherwise. -func (e *Event) GetPublic() bool { - if e == nil || e.Public == nil { - return false - } - return *e.Public -} - -// GetRawPayload returns the RawPayload field if it's non-nil, zero value otherwise. -func (e *Event) GetRawPayload() json.RawMessage { - if e == nil || e.RawPayload == nil { - return json.RawMessage{} - } - return *e.RawPayload -} - -// GetRepo returns the Repo field. -func (e *Event) GetRepo() *Repository { - if e == nil { - return nil - } - return e.Repo -} - -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (e *Event) GetType() string { - if e == nil || e.Type == nil { - return "" - } - return *e.Type -} - -// GetHRef returns the HRef field if it's non-nil, zero value otherwise. -func (f *FeedLink) GetHRef() string { - if f == nil || f.HRef == nil { - return "" - } - return *f.HRef -} - -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (f *FeedLink) GetType() string { - if f == nil || f.Type == nil { - return "" - } - return *f.Type -} - -// GetCurrentUserActorURL returns the CurrentUserActorURL field if it's non-nil, zero value otherwise. -func (f *Feeds) GetCurrentUserActorURL() string { - if f == nil || f.CurrentUserActorURL == nil { - return "" - } - return *f.CurrentUserActorURL -} - -// GetCurrentUserOrganizationURL returns the CurrentUserOrganizationURL field if it's non-nil, zero value otherwise. -func (f *Feeds) GetCurrentUserOrganizationURL() string { - if f == nil || f.CurrentUserOrganizationURL == nil { - return "" - } - return *f.CurrentUserOrganizationURL -} - -// GetCurrentUserPublicURL returns the CurrentUserPublicURL field if it's non-nil, zero value otherwise. -func (f *Feeds) GetCurrentUserPublicURL() string { - if f == nil || f.CurrentUserPublicURL == nil { - return "" - } - return *f.CurrentUserPublicURL -} - -// GetCurrentUserURL returns the CurrentUserURL field if it's non-nil, zero value otherwise. -func (f *Feeds) GetCurrentUserURL() string { - if f == nil || f.CurrentUserURL == nil { - return "" - } - return *f.CurrentUserURL -} - -// GetTimelineURL returns the TimelineURL field if it's non-nil, zero value otherwise. -func (f *Feeds) GetTimelineURL() string { - if f == nil || f.TimelineURL == nil { - return "" - } - return *f.TimelineURL -} - -// GetUserURL returns the UserURL field if it's non-nil, zero value otherwise. -func (f *Feeds) GetUserURL() string { - if f == nil || f.UserURL == nil { - return "" - } - return *f.UserURL -} - -// GetForkee returns the Forkee field. -func (f *ForkEvent) GetForkee() *Repository { - if f == nil { - return nil - } - return f.Forkee -} - -// GetInstallation returns the Installation field. -func (f *ForkEvent) GetInstallation() *Installation { - if f == nil { - return nil - } - return f.Installation -} - -// GetRepo returns the Repo field. -func (f *ForkEvent) GetRepo() *Repository { - if f == nil { - return nil - } - return f.Repo -} - -// GetSender returns the Sender field. -func (f *ForkEvent) GetSender() *User { - if f == nil { - return nil - } - return f.Sender -} - -// GetComments returns the Comments field if it's non-nil, zero value otherwise. -func (g *Gist) GetComments() int { - if g == nil || g.Comments == nil { - return 0 - } - return *g.Comments -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (g *Gist) GetCreatedAt() time.Time { - if g == nil || g.CreatedAt == nil { - return time.Time{} - } - return *g.CreatedAt -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (g *Gist) GetDescription() string { - if g == nil || g.Description == nil { - return "" - } - return *g.Description -} - -// GetGitPullURL returns the GitPullURL field if it's non-nil, zero value otherwise. -func (g *Gist) GetGitPullURL() string { - if g == nil || g.GitPullURL == nil { - return "" - } - return *g.GitPullURL -} - -// GetGitPushURL returns the GitPushURL field if it's non-nil, zero value otherwise. -func (g *Gist) GetGitPushURL() string { - if g == nil || g.GitPushURL == nil { - return "" - } - return *g.GitPushURL -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (g *Gist) GetHTMLURL() string { - if g == nil || g.HTMLURL == nil { - return "" - } - return *g.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (g *Gist) GetID() string { - if g == nil || g.ID == nil { - return "" - } - return *g.ID -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (g *Gist) GetNodeID() string { - if g == nil || g.NodeID == nil { - return "" - } - return *g.NodeID -} - -// GetOwner returns the Owner field. -func (g *Gist) GetOwner() *User { - if g == nil { - return nil - } - return g.Owner -} - -// GetPublic returns the Public field if it's non-nil, zero value otherwise. -func (g *Gist) GetPublic() bool { - if g == nil || g.Public == nil { - return false - } - return *g.Public -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (g *Gist) GetUpdatedAt() time.Time { - if g == nil || g.UpdatedAt == nil { - return time.Time{} - } - return *g.UpdatedAt -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (g *GistComment) GetBody() string { - if g == nil || g.Body == nil { - return "" - } - return *g.Body -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (g *GistComment) GetCreatedAt() time.Time { - if g == nil || g.CreatedAt == nil { - return time.Time{} - } - return *g.CreatedAt -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (g *GistComment) GetID() int64 { - if g == nil || g.ID == nil { - return 0 - } - return *g.ID -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (g *GistComment) GetURL() string { - if g == nil || g.URL == nil { - return "" - } - return *g.URL -} - -// GetUser returns the User field. -func (g *GistComment) GetUser() *User { - if g == nil { - return nil - } - return g.User -} - -// GetChangeStatus returns the ChangeStatus field. -func (g *GistCommit) GetChangeStatus() *CommitStats { - if g == nil { - return nil - } - return g.ChangeStatus -} - -// GetCommittedAt returns the CommittedAt field if it's non-nil, zero value otherwise. -func (g *GistCommit) GetCommittedAt() Timestamp { - if g == nil || g.CommittedAt == nil { - return Timestamp{} - } - return *g.CommittedAt -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (g *GistCommit) GetNodeID() string { - if g == nil || g.NodeID == nil { - return "" - } - return *g.NodeID -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (g *GistCommit) GetURL() string { - if g == nil || g.URL == nil { - return "" - } - return *g.URL -} - -// GetUser returns the User field. -func (g *GistCommit) GetUser() *User { - if g == nil { - return nil - } - return g.User -} - -// GetVersion returns the Version field if it's non-nil, zero value otherwise. -func (g *GistCommit) GetVersion() string { - if g == nil || g.Version == nil { - return "" - } - return *g.Version -} - -// GetContent returns the Content field if it's non-nil, zero value otherwise. -func (g *GistFile) GetContent() string { - if g == nil || g.Content == nil { - return "" - } - return *g.Content -} - -// GetFilename returns the Filename field if it's non-nil, zero value otherwise. -func (g *GistFile) GetFilename() string { - if g == nil || g.Filename == nil { - return "" - } - return *g.Filename -} - -// GetLanguage returns the Language field if it's non-nil, zero value otherwise. -func (g *GistFile) GetLanguage() string { - if g == nil || g.Language == nil { - return "" - } - return *g.Language -} - -// GetRawURL returns the RawURL field if it's non-nil, zero value otherwise. -func (g *GistFile) GetRawURL() string { - if g == nil || g.RawURL == nil { - return "" - } - return *g.RawURL -} - -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (g *GistFile) GetSize() int { - if g == nil || g.Size == nil { - return 0 - } - return *g.Size -} - -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (g *GistFile) GetType() string { - if g == nil || g.Type == nil { - return "" - } - return *g.Type -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (g *GistFork) GetCreatedAt() Timestamp { - if g == nil || g.CreatedAt == nil { - return Timestamp{} - } - return *g.CreatedAt -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (g *GistFork) GetID() string { - if g == nil || g.ID == nil { - return "" - } - return *g.ID -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (g *GistFork) GetNodeID() string { - if g == nil || g.NodeID == nil { - return "" - } - return *g.NodeID -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (g *GistFork) GetUpdatedAt() Timestamp { - if g == nil || g.UpdatedAt == nil { - return Timestamp{} - } - return *g.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (g *GistFork) GetURL() string { - if g == nil || g.URL == nil { - return "" - } - return *g.URL -} - -// GetUser returns the User field. -func (g *GistFork) GetUser() *User { - if g == nil { - return nil - } - return g.User -} - -// GetPrivateGists returns the PrivateGists field if it's non-nil, zero value otherwise. -func (g *GistStats) GetPrivateGists() int { - if g == nil || g.PrivateGists == nil { - return 0 - } - return *g.PrivateGists -} - -// GetPublicGists returns the PublicGists field if it's non-nil, zero value otherwise. -func (g *GistStats) GetPublicGists() int { - if g == nil || g.PublicGists == nil { - return 0 - } - return *g.PublicGists -} - -// GetTotalGists returns the TotalGists field if it's non-nil, zero value otherwise. -func (g *GistStats) GetTotalGists() int { - if g == nil || g.TotalGists == nil { - return 0 - } - return *g.TotalGists -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (g *Gitignore) GetName() string { - if g == nil || g.Name == nil { - return "" - } - return *g.Name -} - -// GetSource returns the Source field if it's non-nil, zero value otherwise. -func (g *Gitignore) GetSource() string { - if g == nil || g.Source == nil { - return "" - } - return *g.Source -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (g *GitObject) GetSHA() string { - if g == nil || g.SHA == nil { - return "" - } - return *g.SHA -} - -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (g *GitObject) GetType() string { - if g == nil || g.Type == nil { - return "" - } - return *g.Type -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (g *GitObject) GetURL() string { - if g == nil || g.URL == nil { - return "" - } - return *g.URL -} - -// GetInstallation returns the Installation field. -func (g *GollumEvent) GetInstallation() *Installation { - if g == nil { - return nil - } - return g.Installation -} - -// GetRepo returns the Repo field. -func (g *GollumEvent) GetRepo() *Repository { - if g == nil { - return nil - } - return g.Repo -} - -// GetSender returns the Sender field. -func (g *GollumEvent) GetSender() *User { - if g == nil { - return nil - } - return g.Sender -} - -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (g *GPGEmail) GetEmail() string { - if g == nil || g.Email == nil { - return "" - } - return *g.Email -} - -// GetVerified returns the Verified field if it's non-nil, zero value otherwise. -func (g *GPGEmail) GetVerified() bool { - if g == nil || g.Verified == nil { - return false - } - return *g.Verified -} - -// GetCanCertify returns the CanCertify field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetCanCertify() bool { - if g == nil || g.CanCertify == nil { - return false - } - return *g.CanCertify -} - -// GetCanEncryptComms returns the CanEncryptComms field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetCanEncryptComms() bool { - if g == nil || g.CanEncryptComms == nil { - return false - } - return *g.CanEncryptComms -} - -// GetCanEncryptStorage returns the CanEncryptStorage field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetCanEncryptStorage() bool { - if g == nil || g.CanEncryptStorage == nil { - return false - } - return *g.CanEncryptStorage -} - -// GetCanSign returns the CanSign field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetCanSign() bool { - if g == nil || g.CanSign == nil { - return false - } - return *g.CanSign -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetCreatedAt() time.Time { - if g == nil || g.CreatedAt == nil { - return time.Time{} - } - return *g.CreatedAt -} - -// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetExpiresAt() time.Time { - if g == nil || g.ExpiresAt == nil { - return time.Time{} - } - return *g.ExpiresAt -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetID() int64 { - if g == nil || g.ID == nil { - return 0 - } - return *g.ID -} - -// GetKeyID returns the KeyID field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetKeyID() string { - if g == nil || g.KeyID == nil { - return "" - } - return *g.KeyID -} - -// GetPrimaryKeyID returns the PrimaryKeyID field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetPrimaryKeyID() int64 { - if g == nil || g.PrimaryKeyID == nil { - return 0 - } - return *g.PrimaryKeyID -} - -// GetPublicKey returns the PublicKey field if it's non-nil, zero value otherwise. -func (g *GPGKey) GetPublicKey() string { - if g == nil || g.PublicKey == nil { - return "" - } - return *g.PublicKey -} - -// GetApp returns the App field. -func (g *Grant) GetApp() *AuthorizationApp { - if g == nil { - return nil - } - return g.App -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (g *Grant) GetCreatedAt() Timestamp { - if g == nil || g.CreatedAt == nil { - return Timestamp{} - } - return *g.CreatedAt -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (g *Grant) GetID() int64 { - if g == nil || g.ID == nil { - return 0 - } - return *g.ID -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (g *Grant) GetUpdatedAt() Timestamp { - if g == nil || g.UpdatedAt == nil { - return Timestamp{} - } - return *g.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (g *Grant) GetURL() string { - if g == nil || g.URL == nil { - return "" - } - return *g.URL -} - -// GetActive returns the Active field if it's non-nil, zero value otherwise. -func (h *Hook) GetActive() bool { - if h == nil || h.Active == nil { - return false - } - return *h.Active -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (h *Hook) GetCreatedAt() time.Time { - if h == nil || h.CreatedAt == nil { - return time.Time{} - } - return *h.CreatedAt -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (h *Hook) GetID() int64 { - if h == nil || h.ID == nil { - return 0 - } - return *h.ID -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (h *Hook) GetName() string { - if h == nil || h.Name == nil { - return "" - } - return *h.Name -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (h *Hook) GetUpdatedAt() time.Time { - if h == nil || h.UpdatedAt == nil { - return time.Time{} - } - return *h.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (h *Hook) GetURL() string { - if h == nil || h.URL == nil { - return "" - } - return *h.URL -} - -// GetActiveHooks returns the ActiveHooks field if it's non-nil, zero value otherwise. -func (h *HookStats) GetActiveHooks() int { - if h == nil || h.ActiveHooks == nil { - return 0 - } - return *h.ActiveHooks -} - -// GetInactiveHooks returns the InactiveHooks field if it's non-nil, zero value otherwise. -func (h *HookStats) GetInactiveHooks() int { - if h == nil || h.InactiveHooks == nil { - return 0 - } - return *h.InactiveHooks -} - -// GetTotalHooks returns the TotalHooks field if it's non-nil, zero value otherwise. -func (h *HookStats) GetTotalHooks() int { - if h == nil || h.TotalHooks == nil { - return 0 - } - return *h.TotalHooks -} - -// GetAuthorsCount returns the AuthorsCount field if it's non-nil, zero value otherwise. -func (i *Import) GetAuthorsCount() int { - if i == nil || i.AuthorsCount == nil { - return 0 - } - return *i.AuthorsCount -} - -// GetAuthorsURL returns the AuthorsURL field if it's non-nil, zero value otherwise. -func (i *Import) GetAuthorsURL() string { - if i == nil || i.AuthorsURL == nil { - return "" - } - return *i.AuthorsURL -} - -// GetCommitCount returns the CommitCount field if it's non-nil, zero value otherwise. -func (i *Import) GetCommitCount() int { - if i == nil || i.CommitCount == nil { - return 0 - } - return *i.CommitCount -} - -// GetFailedStep returns the FailedStep field if it's non-nil, zero value otherwise. -func (i *Import) GetFailedStep() string { - if i == nil || i.FailedStep == nil { - return "" - } - return *i.FailedStep -} - -// GetHasLargeFiles returns the HasLargeFiles field if it's non-nil, zero value otherwise. -func (i *Import) GetHasLargeFiles() bool { - if i == nil || i.HasLargeFiles == nil { - return false - } - return *i.HasLargeFiles -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (i *Import) GetHTMLURL() string { - if i == nil || i.HTMLURL == nil { - return "" - } - return *i.HTMLURL -} - -// GetHumanName returns the HumanName field if it's non-nil, zero value otherwise. -func (i *Import) GetHumanName() string { - if i == nil || i.HumanName == nil { - return "" - } - return *i.HumanName -} - -// GetLargeFilesCount returns the LargeFilesCount field if it's non-nil, zero value otherwise. -func (i *Import) GetLargeFilesCount() int { - if i == nil || i.LargeFilesCount == nil { - return 0 - } - return *i.LargeFilesCount -} - -// GetLargeFilesSize returns the LargeFilesSize field if it's non-nil, zero value otherwise. -func (i *Import) GetLargeFilesSize() int { - if i == nil || i.LargeFilesSize == nil { - return 0 - } - return *i.LargeFilesSize -} - -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (i *Import) GetMessage() string { - if i == nil || i.Message == nil { - return "" - } - return *i.Message -} - -// GetPercent returns the Percent field if it's non-nil, zero value otherwise. -func (i *Import) GetPercent() int { - if i == nil || i.Percent == nil { - return 0 - } - return *i.Percent -} - -// GetPushPercent returns the PushPercent field if it's non-nil, zero value otherwise. -func (i *Import) GetPushPercent() int { - if i == nil || i.PushPercent == nil { - return 0 - } - return *i.PushPercent -} - -// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. -func (i *Import) GetRepositoryURL() string { - if i == nil || i.RepositoryURL == nil { - return "" - } - return *i.RepositoryURL -} - -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (i *Import) GetStatus() string { - if i == nil || i.Status == nil { - return "" - } - return *i.Status -} - -// GetStatusText returns the StatusText field if it's non-nil, zero value otherwise. -func (i *Import) GetStatusText() string { - if i == nil || i.StatusText == nil { - return "" - } - return *i.StatusText -} - -// GetTFVCProject returns the TFVCProject field if it's non-nil, zero value otherwise. -func (i *Import) GetTFVCProject() string { - if i == nil || i.TFVCProject == nil { - return "" - } - return *i.TFVCProject -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (i *Import) GetURL() string { - if i == nil || i.URL == nil { - return "" - } - return *i.URL -} - -// GetUseLFS returns the UseLFS field if it's non-nil, zero value otherwise. -func (i *Import) GetUseLFS() string { - if i == nil || i.UseLFS == nil { - return "" - } - return *i.UseLFS -} - -// GetVCS returns the VCS field if it's non-nil, zero value otherwise. -func (i *Import) GetVCS() string { - if i == nil || i.VCS == nil { - return "" - } - return *i.VCS -} - -// GetVCSPassword returns the VCSPassword field if it's non-nil, zero value otherwise. -func (i *Import) GetVCSPassword() string { - if i == nil || i.VCSPassword == nil { - return "" - } - return *i.VCSPassword -} - -// GetVCSURL returns the VCSURL field if it's non-nil, zero value otherwise. -func (i *Import) GetVCSURL() string { - if i == nil || i.VCSURL == nil { - return "" - } - return *i.VCSURL -} - -// GetVCSUsername returns the VCSUsername field if it's non-nil, zero value otherwise. -func (i *Import) GetVCSUsername() string { - if i == nil || i.VCSUsername == nil { - return "" - } - return *i.VCSUsername -} - -// GetAccessTokensURL returns the AccessTokensURL field if it's non-nil, zero value otherwise. -func (i *Installation) GetAccessTokensURL() string { - if i == nil || i.AccessTokensURL == nil { - return "" - } - return *i.AccessTokensURL -} - -// GetAccount returns the Account field. -func (i *Installation) GetAccount() *User { - if i == nil { - return nil - } - return i.Account -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (i *Installation) GetHTMLURL() string { - if i == nil || i.HTMLURL == nil { - return "" - } - return *i.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (i *Installation) GetID() int64 { - if i == nil || i.ID == nil { - return 0 - } - return *i.ID -} - -// GetRepositoriesURL returns the RepositoriesURL field if it's non-nil, zero value otherwise. -func (i *Installation) GetRepositoriesURL() string { - if i == nil || i.RepositoriesURL == nil { - return "" - } - return *i.RepositoriesURL -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (i *InstallationEvent) GetAction() string { - if i == nil || i.Action == nil { - return "" - } - return *i.Action -} - -// GetInstallation returns the Installation field. -func (i *InstallationEvent) GetInstallation() *Installation { - if i == nil { - return nil - } - return i.Installation -} - -// GetSender returns the Sender field. -func (i *InstallationEvent) GetSender() *User { - if i == nil { - return nil - } - return i.Sender -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (i *InstallationRepositoriesEvent) GetAction() string { - if i == nil || i.Action == nil { - return "" - } - return *i.Action -} - -// GetInstallation returns the Installation field. -func (i *InstallationRepositoriesEvent) GetInstallation() *Installation { - if i == nil { - return nil - } - return i.Installation -} - -// GetRepositorySelection returns the RepositorySelection field if it's non-nil, zero value otherwise. -func (i *InstallationRepositoriesEvent) GetRepositorySelection() string { - if i == nil || i.RepositorySelection == nil { - return "" - } - return *i.RepositorySelection -} - -// GetSender returns the Sender field. -func (i *InstallationRepositoriesEvent) GetSender() *User { - if i == nil { - return nil - } - return i.Sender -} - -// GetExpiresAt returns the ExpiresAt field if it's non-nil, zero value otherwise. -func (i *InstallationToken) GetExpiresAt() time.Time { - if i == nil || i.ExpiresAt == nil { - return time.Time{} - } - return *i.ExpiresAt -} - -// GetToken returns the Token field if it's non-nil, zero value otherwise. -func (i *InstallationToken) GetToken() string { - if i == nil || i.Token == nil { - return "" - } - return *i.Token -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *Invitation) GetCreatedAt() time.Time { - if i == nil || i.CreatedAt == nil { - return time.Time{} - } - return *i.CreatedAt -} - -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (i *Invitation) GetEmail() string { - if i == nil || i.Email == nil { - return "" - } - return *i.Email -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (i *Invitation) GetID() int64 { - if i == nil || i.ID == nil { - return 0 - } - return *i.ID -} - -// GetInviter returns the Inviter field. -func (i *Invitation) GetInviter() *User { - if i == nil { - return nil - } - return i.Inviter -} - -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (i *Invitation) GetLogin() string { - if i == nil || i.Login == nil { - return "" - } - return *i.Login -} - -// GetRole returns the Role field if it's non-nil, zero value otherwise. -func (i *Invitation) GetRole() string { - if i == nil || i.Role == nil { - return "" - } - return *i.Role -} - -// GetAssignee returns the Assignee field. -func (i *Issue) GetAssignee() *User { - if i == nil { - return nil - } - return i.Assignee -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (i *Issue) GetBody() string { - if i == nil || i.Body == nil { - return "" - } - return *i.Body -} - -// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. -func (i *Issue) GetClosedAt() time.Time { - if i == nil || i.ClosedAt == nil { - return time.Time{} - } - return *i.ClosedAt -} - -// GetClosedBy returns the ClosedBy field. -func (i *Issue) GetClosedBy() *User { - if i == nil { - return nil - } - return i.ClosedBy -} - -// GetComments returns the Comments field if it's non-nil, zero value otherwise. -func (i *Issue) GetComments() int { - if i == nil || i.Comments == nil { - return 0 - } - return *i.Comments -} - -// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. -func (i *Issue) GetCommentsURL() string { - if i == nil || i.CommentsURL == nil { - return "" - } - return *i.CommentsURL -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *Issue) GetCreatedAt() time.Time { - if i == nil || i.CreatedAt == nil { - return time.Time{} - } - return *i.CreatedAt -} - -// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. -func (i *Issue) GetEventsURL() string { - if i == nil || i.EventsURL == nil { - return "" - } - return *i.EventsURL -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (i *Issue) GetHTMLURL() string { - if i == nil || i.HTMLURL == nil { - return "" - } - return *i.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (i *Issue) GetID() int64 { - if i == nil || i.ID == nil { - return 0 - } - return *i.ID -} - -// GetLabelsURL returns the LabelsURL field if it's non-nil, zero value otherwise. -func (i *Issue) GetLabelsURL() string { - if i == nil || i.LabelsURL == nil { - return "" - } - return *i.LabelsURL -} - -// GetLocked returns the Locked field if it's non-nil, zero value otherwise. -func (i *Issue) GetLocked() bool { - if i == nil || i.Locked == nil { - return false - } - return *i.Locked -} - -// GetMilestone returns the Milestone field. -func (i *Issue) GetMilestone() *Milestone { - if i == nil { - return nil - } - return i.Milestone -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (i *Issue) GetNodeID() string { - if i == nil || i.NodeID == nil { - return "" - } - return *i.NodeID -} - -// GetNumber returns the Number field if it's non-nil, zero value otherwise. -func (i *Issue) GetNumber() int { - if i == nil || i.Number == nil { - return 0 - } - return *i.Number -} - -// GetPullRequestLinks returns the PullRequestLinks field. -func (i *Issue) GetPullRequestLinks() *PullRequestLinks { - if i == nil { - return nil - } - return i.PullRequestLinks -} - -// GetReactions returns the Reactions field. -func (i *Issue) GetReactions() *Reactions { - if i == nil { - return nil - } - return i.Reactions -} - -// GetRepository returns the Repository field. -func (i *Issue) GetRepository() *Repository { - if i == nil { - return nil - } - return i.Repository -} - -// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. -func (i *Issue) GetRepositoryURL() string { - if i == nil || i.RepositoryURL == nil { - return "" - } - return *i.RepositoryURL -} - -// GetState returns the State field if it's non-nil, zero value otherwise. -func (i *Issue) GetState() string { - if i == nil || i.State == nil { - return "" - } - return *i.State -} - -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (i *Issue) GetTitle() string { - if i == nil || i.Title == nil { - return "" - } - return *i.Title -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (i *Issue) GetUpdatedAt() time.Time { - if i == nil || i.UpdatedAt == nil { - return time.Time{} - } - return *i.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (i *Issue) GetURL() string { - if i == nil || i.URL == nil { - return "" - } - return *i.URL -} - -// GetUser returns the User field. -func (i *Issue) GetUser() *User { - if i == nil { - return nil - } - return i.User -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetBody() string { - if i == nil || i.Body == nil { - return "" - } - return *i.Body -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetCreatedAt() time.Time { - if i == nil || i.CreatedAt == nil { - return time.Time{} - } - return *i.CreatedAt -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetHTMLURL() string { - if i == nil || i.HTMLURL == nil { - return "" - } - return *i.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetID() int64 { - if i == nil || i.ID == nil { - return 0 - } - return *i.ID -} - -// GetIssueURL returns the IssueURL field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetIssueURL() string { - if i == nil || i.IssueURL == nil { - return "" - } - return *i.IssueURL -} - -// GetReactions returns the Reactions field. -func (i *IssueComment) GetReactions() *Reactions { - if i == nil { - return nil - } - return i.Reactions -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetUpdatedAt() time.Time { - if i == nil || i.UpdatedAt == nil { - return time.Time{} - } - return *i.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (i *IssueComment) GetURL() string { - if i == nil || i.URL == nil { - return "" - } - return *i.URL -} - -// GetUser returns the User field. -func (i *IssueComment) GetUser() *User { - if i == nil { - return nil - } - return i.User -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (i *IssueCommentEvent) GetAction() string { - if i == nil || i.Action == nil { - return "" - } - return *i.Action -} - -// GetChanges returns the Changes field. -func (i *IssueCommentEvent) GetChanges() *EditChange { - if i == nil { - return nil - } - return i.Changes -} - -// GetComment returns the Comment field. -func (i *IssueCommentEvent) GetComment() *IssueComment { - if i == nil { - return nil - } - return i.Comment -} - -// GetInstallation returns the Installation field. -func (i *IssueCommentEvent) GetInstallation() *Installation { - if i == nil { - return nil - } - return i.Installation -} - -// GetIssue returns the Issue field. -func (i *IssueCommentEvent) GetIssue() *Issue { - if i == nil { - return nil - } - return i.Issue -} - -// GetRepo returns the Repo field. -func (i *IssueCommentEvent) GetRepo() *Repository { - if i == nil { - return nil - } - return i.Repo -} - -// GetSender returns the Sender field. -func (i *IssueCommentEvent) GetSender() *User { - if i == nil { - return nil - } - return i.Sender -} - -// GetActor returns the Actor field. -func (i *IssueEvent) GetActor() *User { - if i == nil { - return nil - } - return i.Actor -} - -// GetAssignee returns the Assignee field. -func (i *IssueEvent) GetAssignee() *User { - if i == nil { - return nil - } - return i.Assignee -} - -// GetAssigner returns the Assigner field. -func (i *IssueEvent) GetAssigner() *User { - if i == nil { - return nil - } - return i.Assigner -} - -// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. -func (i *IssueEvent) GetCommitID() string { - if i == nil || i.CommitID == nil { - return "" - } - return *i.CommitID -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (i *IssueEvent) GetCreatedAt() time.Time { - if i == nil || i.CreatedAt == nil { - return time.Time{} - } - return *i.CreatedAt -} - -// GetEvent returns the Event field if it's non-nil, zero value otherwise. -func (i *IssueEvent) GetEvent() string { - if i == nil || i.Event == nil { - return "" - } - return *i.Event -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (i *IssueEvent) GetID() int64 { - if i == nil || i.ID == nil { - return 0 - } - return *i.ID -} - -// GetIssue returns the Issue field. -func (i *IssueEvent) GetIssue() *Issue { - if i == nil { - return nil - } - return i.Issue -} - -// GetLabel returns the Label field. -func (i *IssueEvent) GetLabel() *Label { - if i == nil { - return nil - } - return i.Label -} - -// GetMilestone returns the Milestone field. -func (i *IssueEvent) GetMilestone() *Milestone { - if i == nil { - return nil - } - return i.Milestone -} - -// GetRename returns the Rename field. -func (i *IssueEvent) GetRename() *Rename { - if i == nil { - return nil - } - return i.Rename -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (i *IssueEvent) GetURL() string { - if i == nil || i.URL == nil { - return "" - } - return *i.URL -} - -// GetAssignee returns the Assignee field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetAssignee() string { - if i == nil || i.Assignee == nil { - return "" - } - return *i.Assignee -} - -// GetAssignees returns the Assignees field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetAssignees() []string { - if i == nil || i.Assignees == nil { - return nil - } - return *i.Assignees -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetBody() string { - if i == nil || i.Body == nil { - return "" - } - return *i.Body -} - -// GetLabels returns the Labels field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetLabels() []string { - if i == nil || i.Labels == nil { - return nil - } - return *i.Labels -} - -// GetMilestone returns the Milestone field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetMilestone() int { - if i == nil || i.Milestone == nil { - return 0 - } - return *i.Milestone -} - -// GetState returns the State field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetState() string { - if i == nil || i.State == nil { - return "" - } - return *i.State -} - -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (i *IssueRequest) GetTitle() string { - if i == nil || i.Title == nil { - return "" - } - return *i.Title -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (i *IssuesEvent) GetAction() string { - if i == nil || i.Action == nil { - return "" - } - return *i.Action -} - -// GetAssignee returns the Assignee field. -func (i *IssuesEvent) GetAssignee() *User { - if i == nil { - return nil - } - return i.Assignee -} - -// GetChanges returns the Changes field. -func (i *IssuesEvent) GetChanges() *EditChange { - if i == nil { - return nil - } - return i.Changes -} - -// GetInstallation returns the Installation field. -func (i *IssuesEvent) GetInstallation() *Installation { - if i == nil { - return nil - } - return i.Installation -} - -// GetIssue returns the Issue field. -func (i *IssuesEvent) GetIssue() *Issue { - if i == nil { - return nil - } - return i.Issue -} - -// GetLabel returns the Label field. -func (i *IssuesEvent) GetLabel() *Label { - if i == nil { - return nil - } - return i.Label -} - -// GetRepo returns the Repo field. -func (i *IssuesEvent) GetRepo() *Repository { - if i == nil { - return nil - } - return i.Repo -} - -// GetSender returns the Sender field. -func (i *IssuesEvent) GetSender() *User { - if i == nil { - return nil - } - return i.Sender -} - -// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. -func (i *IssuesSearchResult) GetIncompleteResults() bool { - if i == nil || i.IncompleteResults == nil { - return false - } - return *i.IncompleteResults -} - -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (i *IssuesSearchResult) GetTotal() int { - if i == nil || i.Total == nil { - return 0 - } - return *i.Total -} - -// GetClosedIssues returns the ClosedIssues field if it's non-nil, zero value otherwise. -func (i *IssueStats) GetClosedIssues() int { - if i == nil || i.ClosedIssues == nil { - return 0 - } - return *i.ClosedIssues -} - -// GetOpenIssues returns the OpenIssues field if it's non-nil, zero value otherwise. -func (i *IssueStats) GetOpenIssues() int { - if i == nil || i.OpenIssues == nil { - return 0 - } - return *i.OpenIssues -} - -// GetTotalIssues returns the TotalIssues field if it's non-nil, zero value otherwise. -func (i *IssueStats) GetTotalIssues() int { - if i == nil || i.TotalIssues == nil { - return 0 - } - return *i.TotalIssues -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (k *Key) GetID() int64 { - if k == nil || k.ID == nil { - return 0 - } - return *k.ID -} - -// GetKey returns the Key field if it's non-nil, zero value otherwise. -func (k *Key) GetKey() string { - if k == nil || k.Key == nil { - return "" - } - return *k.Key -} - -// GetReadOnly returns the ReadOnly field if it's non-nil, zero value otherwise. -func (k *Key) GetReadOnly() bool { - if k == nil || k.ReadOnly == nil { - return false - } - return *k.ReadOnly -} - -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (k *Key) GetTitle() string { - if k == nil || k.Title == nil { - return "" - } - return *k.Title -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (k *Key) GetURL() string { - if k == nil || k.URL == nil { - return "" - } - return *k.URL -} - -// GetColor returns the Color field if it's non-nil, zero value otherwise. -func (l *Label) GetColor() string { - if l == nil || l.Color == nil { - return "" - } - return *l.Color -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (l *Label) GetID() int64 { - if l == nil || l.ID == nil { - return 0 - } - return *l.ID -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (l *Label) GetName() string { - if l == nil || l.Name == nil { - return "" - } - return *l.Name -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (l *Label) GetNodeID() string { - if l == nil || l.NodeID == nil { - return "" - } - return *l.NodeID -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (l *Label) GetURL() string { - if l == nil || l.URL == nil { - return "" - } - return *l.URL -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (l *LabelEvent) GetAction() string { - if l == nil || l.Action == nil { - return "" - } - return *l.Action -} - -// GetChanges returns the Changes field. -func (l *LabelEvent) GetChanges() *EditChange { - if l == nil { - return nil - } - return l.Changes -} - -// GetInstallation returns the Installation field. -func (l *LabelEvent) GetInstallation() *Installation { - if l == nil { - return nil - } - return l.Installation -} - -// GetLabel returns the Label field. -func (l *LabelEvent) GetLabel() *Label { - if l == nil { - return nil - } - return l.Label -} - -// GetOrg returns the Org field. -func (l *LabelEvent) GetOrg() *Organization { - if l == nil { - return nil - } - return l.Org -} - -// GetRepo returns the Repo field. -func (l *LabelEvent) GetRepo() *Repository { - if l == nil { - return nil - } - return l.Repo -} - -// GetOID returns the OID field if it's non-nil, zero value otherwise. -func (l *LargeFile) GetOID() string { - if l == nil || l.OID == nil { - return "" - } - return *l.OID -} - -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (l *LargeFile) GetPath() string { - if l == nil || l.Path == nil { - return "" - } - return *l.Path -} - -// GetRefName returns the RefName field if it's non-nil, zero value otherwise. -func (l *LargeFile) GetRefName() string { - if l == nil || l.RefName == nil { - return "" - } - return *l.RefName -} - -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (l *LargeFile) GetSize() int { - if l == nil || l.Size == nil { - return 0 - } - return *l.Size -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (l *License) GetBody() string { - if l == nil || l.Body == nil { - return "" - } - return *l.Body -} - -// GetConditions returns the Conditions field if it's non-nil, zero value otherwise. -func (l *License) GetConditions() []string { - if l == nil || l.Conditions == nil { - return nil - } - return *l.Conditions -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (l *License) GetDescription() string { - if l == nil || l.Description == nil { - return "" - } - return *l.Description -} - -// GetFeatured returns the Featured field if it's non-nil, zero value otherwise. -func (l *License) GetFeatured() bool { - if l == nil || l.Featured == nil { - return false - } - return *l.Featured -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (l *License) GetHTMLURL() string { - if l == nil || l.HTMLURL == nil { - return "" - } - return *l.HTMLURL -} - -// GetImplementation returns the Implementation field if it's non-nil, zero value otherwise. -func (l *License) GetImplementation() string { - if l == nil || l.Implementation == nil { - return "" - } - return *l.Implementation -} - -// GetKey returns the Key field if it's non-nil, zero value otherwise. -func (l *License) GetKey() string { - if l == nil || l.Key == nil { - return "" - } - return *l.Key -} - -// GetLimitations returns the Limitations field if it's non-nil, zero value otherwise. -func (l *License) GetLimitations() []string { - if l == nil || l.Limitations == nil { - return nil - } - return *l.Limitations -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (l *License) GetName() string { - if l == nil || l.Name == nil { - return "" - } - return *l.Name -} - -// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. -func (l *License) GetPermissions() []string { - if l == nil || l.Permissions == nil { - return nil - } - return *l.Permissions -} - -// GetSPDXID returns the SPDXID field if it's non-nil, zero value otherwise. -func (l *License) GetSPDXID() string { - if l == nil || l.SPDXID == nil { - return "" - } - return *l.SPDXID -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (l *License) GetURL() string { - if l == nil || l.URL == nil { - return "" - } - return *l.URL -} - -// GetAccountsURL returns the AccountsURL field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetAccountsURL() string { - if m == nil || m.AccountsURL == nil { - return "" - } - return *m.AccountsURL -} - -// GetBullets returns the Bullets field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetBullets() []string { - if m == nil || m.Bullets == nil { - return nil - } - return *m.Bullets -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetDescription() string { - if m == nil || m.Description == nil { - return "" - } - return *m.Description -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetID() int64 { - if m == nil || m.ID == nil { - return 0 - } - return *m.ID -} - -// GetMonthlyPriceInCents returns the MonthlyPriceInCents field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetMonthlyPriceInCents() int { - if m == nil || m.MonthlyPriceInCents == nil { - return 0 - } - return *m.MonthlyPriceInCents -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetName() string { - if m == nil || m.Name == nil { - return "" - } - return *m.Name -} - -// GetPriceModel returns the PriceModel field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetPriceModel() string { - if m == nil || m.PriceModel == nil { - return "" - } - return *m.PriceModel -} - -// GetUnitName returns the UnitName field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetUnitName() string { - if m == nil || m.UnitName == nil { - return "" - } - return *m.UnitName -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetURL() string { - if m == nil || m.URL == nil { - return "" - } - return *m.URL -} - -// GetYearlyPriceInCents returns the YearlyPriceInCents field if it's non-nil, zero value otherwise. -func (m *MarketplacePlan) GetYearlyPriceInCents() int { - if m == nil || m.YearlyPriceInCents == nil { - return 0 - } - return *m.YearlyPriceInCents -} - -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (m *MarketplacePlanAccount) GetEmail() string { - if m == nil || m.Email == nil { - return "" - } - return *m.Email -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (m *MarketplacePlanAccount) GetID() int64 { - if m == nil || m.ID == nil { - return 0 - } - return *m.ID -} - -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (m *MarketplacePlanAccount) GetLogin() string { - if m == nil || m.Login == nil { - return "" - } - return *m.Login -} - -// GetMarketplacePurchase returns the MarketplacePurchase field. -func (m *MarketplacePlanAccount) GetMarketplacePurchase() *MarketplacePurchase { - if m == nil { - return nil - } - return m.MarketplacePurchase -} - -// GetOrganizationBillingEmail returns the OrganizationBillingEmail field if it's non-nil, zero value otherwise. -func (m *MarketplacePlanAccount) GetOrganizationBillingEmail() string { - if m == nil || m.OrganizationBillingEmail == nil { - return "" - } - return *m.OrganizationBillingEmail -} - -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (m *MarketplacePlanAccount) GetType() string { - if m == nil || m.Type == nil { - return "" - } - return *m.Type -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (m *MarketplacePlanAccount) GetURL() string { - if m == nil || m.URL == nil { - return "" - } - return *m.URL -} - -// GetAccount returns the Account field. -func (m *MarketplacePurchase) GetAccount() *MarketplacePlanAccount { - if m == nil { - return nil - } - return m.Account -} - -// GetBillingCycle returns the BillingCycle field if it's non-nil, zero value otherwise. -func (m *MarketplacePurchase) GetBillingCycle() string { - if m == nil || m.BillingCycle == nil { - return "" - } - return *m.BillingCycle -} - -// GetNextBillingDate returns the NextBillingDate field if it's non-nil, zero value otherwise. -func (m *MarketplacePurchase) GetNextBillingDate() string { - if m == nil || m.NextBillingDate == nil { - return "" - } - return *m.NextBillingDate -} - -// GetPlan returns the Plan field. -func (m *MarketplacePurchase) GetPlan() *MarketplacePlan { - if m == nil { - return nil - } - return m.Plan -} - -// GetUnitCount returns the UnitCount field if it's non-nil, zero value otherwise. -func (m *MarketplacePurchase) GetUnitCount() int { - if m == nil || m.UnitCount == nil { - return 0 - } - return *m.UnitCount -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (m *MarketplacePurchaseEvent) GetAction() string { - if m == nil || m.Action == nil { - return "" - } - return *m.Action -} - -// GetEffectiveDate returns the EffectiveDate field if it's non-nil, zero value otherwise. -func (m *MarketplacePurchaseEvent) GetEffectiveDate() Timestamp { - if m == nil || m.EffectiveDate == nil { - return Timestamp{} - } - return *m.EffectiveDate -} - -// GetInstallation returns the Installation field. -func (m *MarketplacePurchaseEvent) GetInstallation() *Installation { - if m == nil { - return nil - } - return m.Installation -} - -// GetMarketplacePurchase returns the MarketplacePurchase field. -func (m *MarketplacePurchaseEvent) GetMarketplacePurchase() *MarketplacePurchase { - if m == nil { - return nil - } - return m.MarketplacePurchase -} - -// GetPreviousMarketplacePurchase returns the PreviousMarketplacePurchase field. -func (m *MarketplacePurchaseEvent) GetPreviousMarketplacePurchase() *MarketplacePurchase { - if m == nil { - return nil - } - return m.PreviousMarketplacePurchase -} - -// GetSender returns the Sender field. -func (m *MarketplacePurchaseEvent) GetSender() *User { - if m == nil { - return nil - } - return m.Sender -} - -// GetText returns the Text field if it's non-nil, zero value otherwise. -func (m *Match) GetText() string { - if m == nil || m.Text == nil { - return "" - } - return *m.Text -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (m *MemberEvent) GetAction() string { - if m == nil || m.Action == nil { - return "" - } - return *m.Action -} - -// GetInstallation returns the Installation field. -func (m *MemberEvent) GetInstallation() *Installation { - if m == nil { - return nil - } - return m.Installation -} - -// GetMember returns the Member field. -func (m *MemberEvent) GetMember() *User { - if m == nil { - return nil - } - return m.Member -} - -// GetRepo returns the Repo field. -func (m *MemberEvent) GetRepo() *Repository { - if m == nil { - return nil - } - return m.Repo -} - -// GetSender returns the Sender field. -func (m *MemberEvent) GetSender() *User { - if m == nil { - return nil - } - return m.Sender -} - -// GetOrganization returns the Organization field. -func (m *Membership) GetOrganization() *Organization { - if m == nil { - return nil - } - return m.Organization -} - -// GetOrganizationURL returns the OrganizationURL field if it's non-nil, zero value otherwise. -func (m *Membership) GetOrganizationURL() string { - if m == nil || m.OrganizationURL == nil { - return "" - } - return *m.OrganizationURL -} - -// GetRole returns the Role field if it's non-nil, zero value otherwise. -func (m *Membership) GetRole() string { - if m == nil || m.Role == nil { - return "" - } - return *m.Role -} - -// GetState returns the State field if it's non-nil, zero value otherwise. -func (m *Membership) GetState() string { - if m == nil || m.State == nil { - return "" - } - return *m.State -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (m *Membership) GetURL() string { - if m == nil || m.URL == nil { - return "" - } - return *m.URL -} - -// GetUser returns the User field. -func (m *Membership) GetUser() *User { - if m == nil { - return nil - } - return m.User -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (m *MembershipEvent) GetAction() string { - if m == nil || m.Action == nil { - return "" - } - return *m.Action -} - -// GetInstallation returns the Installation field. -func (m *MembershipEvent) GetInstallation() *Installation { - if m == nil { - return nil - } - return m.Installation -} - -// GetMember returns the Member field. -func (m *MembershipEvent) GetMember() *User { - if m == nil { - return nil - } - return m.Member -} - -// GetOrg returns the Org field. -func (m *MembershipEvent) GetOrg() *Organization { - if m == nil { - return nil - } - return m.Org -} - -// GetScope returns the Scope field if it's non-nil, zero value otherwise. -func (m *MembershipEvent) GetScope() string { - if m == nil || m.Scope == nil { - return "" - } - return *m.Scope -} - -// GetSender returns the Sender field. -func (m *MembershipEvent) GetSender() *User { - if m == nil { - return nil - } - return m.Sender -} - -// GetTeam returns the Team field. -func (m *MembershipEvent) GetTeam() *Team { - if m == nil { - return nil - } - return m.Team -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (m *Metric) GetHTMLURL() string { - if m == nil || m.HTMLURL == nil { - return "" - } - return *m.HTMLURL -} - -// GetKey returns the Key field if it's non-nil, zero value otherwise. -func (m *Metric) GetKey() string { - if m == nil || m.Key == nil { - return "" - } - return *m.Key -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (m *Metric) GetName() string { - if m == nil || m.Name == nil { - return "" - } - return *m.Name -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (m *Metric) GetURL() string { - if m == nil || m.URL == nil { - return "" - } - return *m.URL -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (m *Migration) GetCreatedAt() string { - if m == nil || m.CreatedAt == nil { - return "" - } - return *m.CreatedAt -} - -// GetExcludeAttachments returns the ExcludeAttachments field if it's non-nil, zero value otherwise. -func (m *Migration) GetExcludeAttachments() bool { - if m == nil || m.ExcludeAttachments == nil { - return false - } - return *m.ExcludeAttachments -} - -// GetGUID returns the GUID field if it's non-nil, zero value otherwise. -func (m *Migration) GetGUID() string { - if m == nil || m.GUID == nil { - return "" - } - return *m.GUID -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (m *Migration) GetID() int64 { - if m == nil || m.ID == nil { - return 0 - } - return *m.ID -} - -// GetLockRepositories returns the LockRepositories field if it's non-nil, zero value otherwise. -func (m *Migration) GetLockRepositories() bool { - if m == nil || m.LockRepositories == nil { - return false - } - return *m.LockRepositories -} - -// GetState returns the State field if it's non-nil, zero value otherwise. -func (m *Migration) GetState() string { - if m == nil || m.State == nil { - return "" - } - return *m.State -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (m *Migration) GetUpdatedAt() string { - if m == nil || m.UpdatedAt == nil { - return "" - } - return *m.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (m *Migration) GetURL() string { - if m == nil || m.URL == nil { - return "" - } - return *m.URL -} - -// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. -func (m *Milestone) GetClosedAt() time.Time { - if m == nil || m.ClosedAt == nil { - return time.Time{} - } - return *m.ClosedAt -} - -// GetClosedIssues returns the ClosedIssues field if it's non-nil, zero value otherwise. -func (m *Milestone) GetClosedIssues() int { - if m == nil || m.ClosedIssues == nil { - return 0 - } - return *m.ClosedIssues -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (m *Milestone) GetCreatedAt() time.Time { - if m == nil || m.CreatedAt == nil { - return time.Time{} - } - return *m.CreatedAt -} - -// GetCreator returns the Creator field. -func (m *Milestone) GetCreator() *User { - if m == nil { - return nil - } - return m.Creator -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (m *Milestone) GetDescription() string { - if m == nil || m.Description == nil { - return "" - } - return *m.Description -} - -// GetDueOn returns the DueOn field if it's non-nil, zero value otherwise. -func (m *Milestone) GetDueOn() time.Time { - if m == nil || m.DueOn == nil { - return time.Time{} - } - return *m.DueOn -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (m *Milestone) GetHTMLURL() string { - if m == nil || m.HTMLURL == nil { - return "" - } - return *m.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (m *Milestone) GetID() int64 { - if m == nil || m.ID == nil { - return 0 - } - return *m.ID -} - -// GetLabelsURL returns the LabelsURL field if it's non-nil, zero value otherwise. -func (m *Milestone) GetLabelsURL() string { - if m == nil || m.LabelsURL == nil { - return "" - } - return *m.LabelsURL -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (m *Milestone) GetNodeID() string { - if m == nil || m.NodeID == nil { - return "" - } - return *m.NodeID -} - -// GetNumber returns the Number field if it's non-nil, zero value otherwise. -func (m *Milestone) GetNumber() int { - if m == nil || m.Number == nil { - return 0 - } - return *m.Number -} - -// GetOpenIssues returns the OpenIssues field if it's non-nil, zero value otherwise. -func (m *Milestone) GetOpenIssues() int { - if m == nil || m.OpenIssues == nil { - return 0 - } - return *m.OpenIssues -} - -// GetState returns the State field if it's non-nil, zero value otherwise. -func (m *Milestone) GetState() string { - if m == nil || m.State == nil { - return "" - } - return *m.State -} - -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (m *Milestone) GetTitle() string { - if m == nil || m.Title == nil { - return "" - } - return *m.Title -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (m *Milestone) GetUpdatedAt() time.Time { - if m == nil || m.UpdatedAt == nil { - return time.Time{} - } - return *m.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (m *Milestone) GetURL() string { - if m == nil || m.URL == nil { - return "" - } - return *m.URL -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (m *MilestoneEvent) GetAction() string { - if m == nil || m.Action == nil { - return "" - } - return *m.Action -} - -// GetChanges returns the Changes field. -func (m *MilestoneEvent) GetChanges() *EditChange { - if m == nil { - return nil - } - return m.Changes -} - -// GetInstallation returns the Installation field. -func (m *MilestoneEvent) GetInstallation() *Installation { - if m == nil { - return nil - } - return m.Installation -} - -// GetMilestone returns the Milestone field. -func (m *MilestoneEvent) GetMilestone() *Milestone { - if m == nil { - return nil - } - return m.Milestone -} - -// GetOrg returns the Org field. -func (m *MilestoneEvent) GetOrg() *Organization { - if m == nil { - return nil - } - return m.Org -} - -// GetRepo returns the Repo field. -func (m *MilestoneEvent) GetRepo() *Repository { - if m == nil { - return nil - } - return m.Repo -} - -// GetSender returns the Sender field. -func (m *MilestoneEvent) GetSender() *User { - if m == nil { - return nil - } - return m.Sender -} - -// GetClosedMilestones returns the ClosedMilestones field if it's non-nil, zero value otherwise. -func (m *MilestoneStats) GetClosedMilestones() int { - if m == nil || m.ClosedMilestones == nil { - return 0 - } - return *m.ClosedMilestones -} - -// GetOpenMilestones returns the OpenMilestones field if it's non-nil, zero value otherwise. -func (m *MilestoneStats) GetOpenMilestones() int { - if m == nil || m.OpenMilestones == nil { - return 0 - } - return *m.OpenMilestones -} - -// GetTotalMilestones returns the TotalMilestones field if it's non-nil, zero value otherwise. -func (m *MilestoneStats) GetTotalMilestones() int { - if m == nil || m.TotalMilestones == nil { - return 0 - } - return *m.TotalMilestones -} - -// GetBase returns the Base field if it's non-nil, zero value otherwise. -func (n *NewPullRequest) GetBase() string { - if n == nil || n.Base == nil { - return "" - } - return *n.Base -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (n *NewPullRequest) GetBody() string { - if n == nil || n.Body == nil { - return "" - } - return *n.Body -} - -// GetHead returns the Head field if it's non-nil, zero value otherwise. -func (n *NewPullRequest) GetHead() string { - if n == nil || n.Head == nil { - return "" - } - return *n.Head -} - -// GetIssue returns the Issue field if it's non-nil, zero value otherwise. -func (n *NewPullRequest) GetIssue() int { - if n == nil || n.Issue == nil { - return 0 - } - return *n.Issue -} - -// GetMaintainerCanModify returns the MaintainerCanModify field if it's non-nil, zero value otherwise. -func (n *NewPullRequest) GetMaintainerCanModify() bool { - if n == nil || n.MaintainerCanModify == nil { - return false - } - return *n.MaintainerCanModify -} - -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (n *NewPullRequest) GetTitle() string { - if n == nil || n.Title == nil { - return "" - } - return *n.Title -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (n *NewTeam) GetDescription() string { - if n == nil || n.Description == nil { - return "" - } - return *n.Description -} - -// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. -func (n *NewTeam) GetLDAPDN() string { - if n == nil || n.LDAPDN == nil { - return "" - } - return *n.LDAPDN -} - -// GetParentTeamID returns the ParentTeamID field if it's non-nil, zero value otherwise. -func (n *NewTeam) GetParentTeamID() int64 { - if n == nil || n.ParentTeamID == nil { - return 0 - } - return *n.ParentTeamID -} - -// GetPermission returns the Permission field if it's non-nil, zero value otherwise. -func (n *NewTeam) GetPermission() string { - if n == nil || n.Permission == nil { - return "" - } - return *n.Permission -} - -// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. -func (n *NewTeam) GetPrivacy() string { - if n == nil || n.Privacy == nil { - return "" - } - return *n.Privacy -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (n *Notification) GetID() string { - if n == nil || n.ID == nil { - return "" - } - return *n.ID -} - -// GetLastReadAt returns the LastReadAt field if it's non-nil, zero value otherwise. -func (n *Notification) GetLastReadAt() time.Time { - if n == nil || n.LastReadAt == nil { - return time.Time{} - } - return *n.LastReadAt -} - -// GetReason returns the Reason field if it's non-nil, zero value otherwise. -func (n *Notification) GetReason() string { - if n == nil || n.Reason == nil { - return "" - } - return *n.Reason -} - -// GetRepository returns the Repository field. -func (n *Notification) GetRepository() *Repository { - if n == nil { - return nil - } - return n.Repository -} - -// GetSubject returns the Subject field. -func (n *Notification) GetSubject() *NotificationSubject { - if n == nil { - return nil - } - return n.Subject -} - -// GetUnread returns the Unread field if it's non-nil, zero value otherwise. -func (n *Notification) GetUnread() bool { - if n == nil || n.Unread == nil { - return false - } - return *n.Unread -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (n *Notification) GetUpdatedAt() time.Time { - if n == nil || n.UpdatedAt == nil { - return time.Time{} - } - return *n.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (n *Notification) GetURL() string { - if n == nil || n.URL == nil { - return "" - } - return *n.URL -} - -// GetLatestCommentURL returns the LatestCommentURL field if it's non-nil, zero value otherwise. -func (n *NotificationSubject) GetLatestCommentURL() string { - if n == nil || n.LatestCommentURL == nil { - return "" - } - return *n.LatestCommentURL -} - -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (n *NotificationSubject) GetTitle() string { - if n == nil || n.Title == nil { - return "" - } - return *n.Title -} - -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (n *NotificationSubject) GetType() string { - if n == nil || n.Type == nil { - return "" - } - return *n.Type -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (n *NotificationSubject) GetURL() string { - if n == nil || n.URL == nil { - return "" - } - return *n.URL -} - -// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetAvatarURL() string { - if o == nil || o.AvatarURL == nil { - return "" - } - return *o.AvatarURL -} - -// GetBillingEmail returns the BillingEmail field if it's non-nil, zero value otherwise. -func (o *Organization) GetBillingEmail() string { - if o == nil || o.BillingEmail == nil { - return "" - } - return *o.BillingEmail -} - -// GetBlog returns the Blog field if it's non-nil, zero value otherwise. -func (o *Organization) GetBlog() string { - if o == nil || o.Blog == nil { - return "" - } - return *o.Blog -} - -// GetCollaborators returns the Collaborators field if it's non-nil, zero value otherwise. -func (o *Organization) GetCollaborators() int { - if o == nil || o.Collaborators == nil { - return 0 - } - return *o.Collaborators -} - -// GetCompany returns the Company field if it's non-nil, zero value otherwise. -func (o *Organization) GetCompany() string { - if o == nil || o.Company == nil { - return "" - } - return *o.Company -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (o *Organization) GetCreatedAt() time.Time { - if o == nil || o.CreatedAt == nil { - return time.Time{} - } - return *o.CreatedAt -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (o *Organization) GetDescription() string { - if o == nil || o.Description == nil { - return "" - } - return *o.Description -} - -// GetDiskUsage returns the DiskUsage field if it's non-nil, zero value otherwise. -func (o *Organization) GetDiskUsage() int { - if o == nil || o.DiskUsage == nil { - return 0 - } - return *o.DiskUsage -} - -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (o *Organization) GetEmail() string { - if o == nil || o.Email == nil { - return "" - } - return *o.Email -} - -// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetEventsURL() string { - if o == nil || o.EventsURL == nil { - return "" - } - return *o.EventsURL -} - -// GetFollowers returns the Followers field if it's non-nil, zero value otherwise. -func (o *Organization) GetFollowers() int { - if o == nil || o.Followers == nil { - return 0 - } - return *o.Followers -} - -// GetFollowing returns the Following field if it's non-nil, zero value otherwise. -func (o *Organization) GetFollowing() int { - if o == nil || o.Following == nil { - return 0 - } - return *o.Following -} - -// GetHooksURL returns the HooksURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetHooksURL() string { - if o == nil || o.HooksURL == nil { - return "" - } - return *o.HooksURL -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetHTMLURL() string { - if o == nil || o.HTMLURL == nil { - return "" - } - return *o.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (o *Organization) GetID() int64 { - if o == nil || o.ID == nil { - return 0 - } - return *o.ID -} - -// GetIssuesURL returns the IssuesURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetIssuesURL() string { - if o == nil || o.IssuesURL == nil { - return "" - } - return *o.IssuesURL -} - -// GetLocation returns the Location field if it's non-nil, zero value otherwise. -func (o *Organization) GetLocation() string { - if o == nil || o.Location == nil { - return "" - } - return *o.Location -} - -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (o *Organization) GetLogin() string { - if o == nil || o.Login == nil { - return "" - } - return *o.Login -} - -// GetMembersURL returns the MembersURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetMembersURL() string { - if o == nil || o.MembersURL == nil { - return "" - } - return *o.MembersURL -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (o *Organization) GetName() string { - if o == nil || o.Name == nil { - return "" - } - return *o.Name -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (o *Organization) GetNodeID() string { - if o == nil || o.NodeID == nil { - return "" - } - return *o.NodeID -} - -// GetOwnedPrivateRepos returns the OwnedPrivateRepos field if it's non-nil, zero value otherwise. -func (o *Organization) GetOwnedPrivateRepos() int { - if o == nil || o.OwnedPrivateRepos == nil { - return 0 - } - return *o.OwnedPrivateRepos -} - -// GetPlan returns the Plan field. -func (o *Organization) GetPlan() *Plan { - if o == nil { - return nil - } - return o.Plan -} - -// GetPrivateGists returns the PrivateGists field if it's non-nil, zero value otherwise. -func (o *Organization) GetPrivateGists() int { - if o == nil || o.PrivateGists == nil { - return 0 - } - return *o.PrivateGists -} - -// GetPublicGists returns the PublicGists field if it's non-nil, zero value otherwise. -func (o *Organization) GetPublicGists() int { - if o == nil || o.PublicGists == nil { - return 0 - } - return *o.PublicGists -} - -// GetPublicMembersURL returns the PublicMembersURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetPublicMembersURL() string { - if o == nil || o.PublicMembersURL == nil { - return "" - } - return *o.PublicMembersURL -} - -// GetPublicRepos returns the PublicRepos field if it's non-nil, zero value otherwise. -func (o *Organization) GetPublicRepos() int { - if o == nil || o.PublicRepos == nil { - return 0 - } - return *o.PublicRepos -} - -// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. -func (o *Organization) GetReposURL() string { - if o == nil || o.ReposURL == nil { - return "" - } - return *o.ReposURL -} - -// GetTotalPrivateRepos returns the TotalPrivateRepos field if it's non-nil, zero value otherwise. -func (o *Organization) GetTotalPrivateRepos() int { - if o == nil || o.TotalPrivateRepos == nil { - return 0 - } - return *o.TotalPrivateRepos -} - -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (o *Organization) GetType() string { - if o == nil || o.Type == nil { - return "" - } - return *o.Type -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (o *Organization) GetUpdatedAt() time.Time { - if o == nil || o.UpdatedAt == nil { - return time.Time{} - } - return *o.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (o *Organization) GetURL() string { - if o == nil || o.URL == nil { - return "" - } - return *o.URL -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (o *OrganizationEvent) GetAction() string { - if o == nil || o.Action == nil { - return "" - } - return *o.Action -} - -// GetInstallation returns the Installation field. -func (o *OrganizationEvent) GetInstallation() *Installation { - if o == nil { - return nil - } - return o.Installation -} - -// GetInvitation returns the Invitation field. -func (o *OrganizationEvent) GetInvitation() *Invitation { - if o == nil { - return nil - } - return o.Invitation -} - -// GetMembership returns the Membership field. -func (o *OrganizationEvent) GetMembership() *Membership { - if o == nil { - return nil - } - return o.Membership -} - -// GetOrganization returns the Organization field. -func (o *OrganizationEvent) GetOrganization() *Organization { - if o == nil { - return nil - } - return o.Organization -} - -// GetSender returns the Sender field. -func (o *OrganizationEvent) GetSender() *User { - if o == nil { - return nil - } - return o.Sender -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (o *OrgBlockEvent) GetAction() string { - if o == nil || o.Action == nil { - return "" - } - return *o.Action -} - -// GetBlockedUser returns the BlockedUser field. -func (o *OrgBlockEvent) GetBlockedUser() *User { - if o == nil { - return nil - } - return o.BlockedUser -} - -// GetInstallation returns the Installation field. -func (o *OrgBlockEvent) GetInstallation() *Installation { - if o == nil { - return nil - } - return o.Installation -} - -// GetOrganization returns the Organization field. -func (o *OrgBlockEvent) GetOrganization() *Organization { - if o == nil { - return nil - } - return o.Organization -} - -// GetSender returns the Sender field. -func (o *OrgBlockEvent) GetSender() *User { - if o == nil { - return nil - } - return o.Sender -} - -// GetDisabledOrgs returns the DisabledOrgs field if it's non-nil, zero value otherwise. -func (o *OrgStats) GetDisabledOrgs() int { - if o == nil || o.DisabledOrgs == nil { - return 0 - } - return *o.DisabledOrgs -} - -// GetTotalOrgs returns the TotalOrgs field if it's non-nil, zero value otherwise. -func (o *OrgStats) GetTotalOrgs() int { - if o == nil || o.TotalOrgs == nil { - return 0 - } - return *o.TotalOrgs -} - -// GetTotalTeamMembers returns the TotalTeamMembers field if it's non-nil, zero value otherwise. -func (o *OrgStats) GetTotalTeamMembers() int { - if o == nil || o.TotalTeamMembers == nil { - return 0 - } - return *o.TotalTeamMembers -} - -// GetTotalTeams returns the TotalTeams field if it's non-nil, zero value otherwise. -func (o *OrgStats) GetTotalTeams() int { - if o == nil || o.TotalTeams == nil { - return 0 - } - return *o.TotalTeams -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *Page) GetAction() string { - if p == nil || p.Action == nil { - return "" - } - return *p.Action -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *Page) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { - return "" - } - return *p.HTMLURL -} - -// GetPageName returns the PageName field if it's non-nil, zero value otherwise. -func (p *Page) GetPageName() string { - if p == nil || p.PageName == nil { - return "" - } - return *p.PageName -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (p *Page) GetSHA() string { - if p == nil || p.SHA == nil { - return "" - } - return *p.SHA -} - -// GetSummary returns the Summary field if it's non-nil, zero value otherwise. -func (p *Page) GetSummary() string { - if p == nil || p.Summary == nil { - return "" - } - return *p.Summary -} - -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (p *Page) GetTitle() string { - if p == nil || p.Title == nil { - return "" - } - return *p.Title -} - -// GetBuild returns the Build field. -func (p *PageBuildEvent) GetBuild() *PagesBuild { - if p == nil { - return nil - } - return p.Build -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PageBuildEvent) GetID() int64 { - if p == nil || p.ID == nil { - return 0 - } - return *p.ID -} - -// GetInstallation returns the Installation field. -func (p *PageBuildEvent) GetInstallation() *Installation { - if p == nil { - return nil - } - return p.Installation -} - -// GetRepo returns the Repo field. -func (p *PageBuildEvent) GetRepo() *Repository { - if p == nil { - return nil - } - return p.Repo -} - -// GetSender returns the Sender field. -func (p *PageBuildEvent) GetSender() *User { - if p == nil { - return nil - } - return p.Sender -} - -// GetCNAME returns the CNAME field if it's non-nil, zero value otherwise. -func (p *Pages) GetCNAME() string { - if p == nil || p.CNAME == nil { - return "" - } - return *p.CNAME -} - -// GetCustom404 returns the Custom404 field if it's non-nil, zero value otherwise. -func (p *Pages) GetCustom404() bool { - if p == nil || p.Custom404 == nil { - return false - } - return *p.Custom404 -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *Pages) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { - return "" - } - return *p.HTMLURL -} - -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (p *Pages) GetStatus() string { - if p == nil || p.Status == nil { - return "" - } - return *p.Status -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *Pages) GetURL() string { - if p == nil || p.URL == nil { - return "" - } - return *p.URL -} - -// GetCommit returns the Commit field if it's non-nil, zero value otherwise. -func (p *PagesBuild) GetCommit() string { - if p == nil || p.Commit == nil { - return "" - } - return *p.Commit -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *PagesBuild) GetCreatedAt() Timestamp { - if p == nil || p.CreatedAt == nil { - return Timestamp{} - } - return *p.CreatedAt -} - -// GetDuration returns the Duration field if it's non-nil, zero value otherwise. -func (p *PagesBuild) GetDuration() int { - if p == nil || p.Duration == nil { - return 0 - } - return *p.Duration -} - -// GetError returns the Error field. -func (p *PagesBuild) GetError() *PagesError { - if p == nil { - return nil - } - return p.Error -} - -// GetPusher returns the Pusher field. -func (p *PagesBuild) GetPusher() *User { - if p == nil { - return nil - } - return p.Pusher -} - -// GetStatus returns the Status field if it's non-nil, zero value otherwise. -func (p *PagesBuild) GetStatus() string { - if p == nil || p.Status == nil { - return "" - } - return *p.Status -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *PagesBuild) GetUpdatedAt() Timestamp { - if p == nil || p.UpdatedAt == nil { - return Timestamp{} - } - return *p.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PagesBuild) GetURL() string { - if p == nil || p.URL == nil { - return "" - } - return *p.URL -} - -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (p *PagesError) GetMessage() string { - if p == nil || p.Message == nil { - return "" - } - return *p.Message -} - -// GetTotalPages returns the TotalPages field if it's non-nil, zero value otherwise. -func (p *PageStats) GetTotalPages() int { - if p == nil || p.TotalPages == nil { - return 0 - } - return *p.TotalPages -} - -// GetHook returns the Hook field. -func (p *PingEvent) GetHook() *Hook { - if p == nil { - return nil - } - return p.Hook -} - -// GetHookID returns the HookID field if it's non-nil, zero value otherwise. -func (p *PingEvent) GetHookID() int64 { - if p == nil || p.HookID == nil { - return 0 - } - return *p.HookID -} - -// GetInstallation returns the Installation field. -func (p *PingEvent) GetInstallation() *Installation { - if p == nil { - return nil - } - return p.Installation -} - -// GetZen returns the Zen field if it's non-nil, zero value otherwise. -func (p *PingEvent) GetZen() string { - if p == nil || p.Zen == nil { - return "" - } - return *p.Zen -} - -// GetCollaborators returns the Collaborators field if it's non-nil, zero value otherwise. -func (p *Plan) GetCollaborators() int { - if p == nil || p.Collaborators == nil { - return 0 - } - return *p.Collaborators -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (p *Plan) GetName() string { - if p == nil || p.Name == nil { - return "" - } - return *p.Name -} - -// GetPrivateRepos returns the PrivateRepos field if it's non-nil, zero value otherwise. -func (p *Plan) GetPrivateRepos() int { - if p == nil || p.PrivateRepos == nil { - return 0 - } - return *p.PrivateRepos -} - -// GetSpace returns the Space field if it's non-nil, zero value otherwise. -func (p *Plan) GetSpace() int { - if p == nil || p.Space == nil { - return 0 - } - return *p.Space -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (p *Project) GetBody() string { - if p == nil || p.Body == nil { - return "" - } - return *p.Body -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *Project) GetCreatedAt() Timestamp { - if p == nil || p.CreatedAt == nil { - return Timestamp{} - } - return *p.CreatedAt -} - -// GetCreator returns the Creator field. -func (p *Project) GetCreator() *User { - if p == nil { - return nil - } - return p.Creator -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *Project) GetID() int64 { - if p == nil || p.ID == nil { - return 0 - } - return *p.ID -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (p *Project) GetName() string { - if p == nil || p.Name == nil { - return "" - } - return *p.Name -} - -// GetNumber returns the Number field if it's non-nil, zero value otherwise. -func (p *Project) GetNumber() int { - if p == nil || p.Number == nil { - return 0 - } - return *p.Number -} - -// GetOwnerURL returns the OwnerURL field if it's non-nil, zero value otherwise. -func (p *Project) GetOwnerURL() string { - if p == nil || p.OwnerURL == nil { - return "" - } - return *p.OwnerURL -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *Project) GetUpdatedAt() Timestamp { - if p == nil || p.UpdatedAt == nil { - return Timestamp{} - } - return *p.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *Project) GetURL() string { - if p == nil || p.URL == nil { - return "" - } - return *p.URL -} - -// GetColumnID returns the ColumnID field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetColumnID() int64 { - if p == nil || p.ColumnID == nil { - return 0 - } - return *p.ColumnID -} - -// GetColumnURL returns the ColumnURL field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetColumnURL() string { - if p == nil || p.ColumnURL == nil { - return "" - } - return *p.ColumnURL -} - -// GetContentURL returns the ContentURL field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetContentURL() string { - if p == nil || p.ContentURL == nil { - return "" - } - return *p.ContentURL -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetCreatedAt() Timestamp { - if p == nil || p.CreatedAt == nil { - return Timestamp{} - } - return *p.CreatedAt -} - -// GetCreator returns the Creator field. -func (p *ProjectCard) GetCreator() *User { - if p == nil { - return nil - } - return p.Creator -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetID() int64 { - if p == nil || p.ID == nil { - return 0 - } - return *p.ID -} - -// GetNote returns the Note field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetNote() string { - if p == nil || p.Note == nil { - return "" - } - return *p.Note -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetUpdatedAt() Timestamp { - if p == nil || p.UpdatedAt == nil { - return Timestamp{} - } - return *p.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *ProjectCard) GetURL() string { - if p == nil || p.URL == nil { - return "" - } - return *p.URL -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *ProjectCardEvent) GetAction() string { - if p == nil || p.Action == nil { - return "" - } - return *p.Action -} - -// GetAfterID returns the AfterID field if it's non-nil, zero value otherwise. -func (p *ProjectCardEvent) GetAfterID() int64 { - if p == nil || p.AfterID == nil { - return 0 - } - return *p.AfterID -} - -// GetChanges returns the Changes field. -func (p *ProjectCardEvent) GetChanges() *ProjectCardChange { - if p == nil { - return nil - } - return p.Changes -} - -// GetInstallation returns the Installation field. -func (p *ProjectCardEvent) GetInstallation() *Installation { - if p == nil { - return nil - } - return p.Installation -} - -// GetOrg returns the Org field. -func (p *ProjectCardEvent) GetOrg() *Organization { - if p == nil { - return nil - } - return p.Org -} - -// GetProjectCard returns the ProjectCard field. -func (p *ProjectCardEvent) GetProjectCard() *ProjectCard { - if p == nil { - return nil - } - return p.ProjectCard -} - -// GetRepo returns the Repo field. -func (p *ProjectCardEvent) GetRepo() *Repository { - if p == nil { - return nil - } - return p.Repo -} - -// GetSender returns the Sender field. -func (p *ProjectCardEvent) GetSender() *User { - if p == nil { - return nil - } - return p.Sender -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *ProjectColumn) GetCreatedAt() Timestamp { - if p == nil || p.CreatedAt == nil { - return Timestamp{} - } - return *p.CreatedAt -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *ProjectColumn) GetID() int64 { - if p == nil || p.ID == nil { - return 0 - } - return *p.ID -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (p *ProjectColumn) GetName() string { - if p == nil || p.Name == nil { - return "" - } - return *p.Name -} - -// GetProjectURL returns the ProjectURL field if it's non-nil, zero value otherwise. -func (p *ProjectColumn) GetProjectURL() string { - if p == nil || p.ProjectURL == nil { - return "" - } - return *p.ProjectURL -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *ProjectColumn) GetUpdatedAt() Timestamp { - if p == nil || p.UpdatedAt == nil { - return Timestamp{} - } - return *p.UpdatedAt -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *ProjectColumnEvent) GetAction() string { - if p == nil || p.Action == nil { - return "" - } - return *p.Action -} - -// GetAfterID returns the AfterID field if it's non-nil, zero value otherwise. -func (p *ProjectColumnEvent) GetAfterID() int64 { - if p == nil || p.AfterID == nil { - return 0 - } - return *p.AfterID -} - -// GetChanges returns the Changes field. -func (p *ProjectColumnEvent) GetChanges() *ProjectColumnChange { - if p == nil { - return nil - } - return p.Changes -} - -// GetInstallation returns the Installation field. -func (p *ProjectColumnEvent) GetInstallation() *Installation { - if p == nil { - return nil - } - return p.Installation -} - -// GetOrg returns the Org field. -func (p *ProjectColumnEvent) GetOrg() *Organization { - if p == nil { - return nil - } - return p.Org -} - -// GetProjectColumn returns the ProjectColumn field. -func (p *ProjectColumnEvent) GetProjectColumn() *ProjectColumn { - if p == nil { - return nil - } - return p.ProjectColumn -} - -// GetRepo returns the Repo field. -func (p *ProjectColumnEvent) GetRepo() *Repository { - if p == nil { - return nil - } - return p.Repo -} - -// GetSender returns the Sender field. -func (p *ProjectColumnEvent) GetSender() *User { - if p == nil { - return nil - } - return p.Sender -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *ProjectEvent) GetAction() string { - if p == nil || p.Action == nil { - return "" - } - return *p.Action -} - -// GetChanges returns the Changes field. -func (p *ProjectEvent) GetChanges() *ProjectChange { - if p == nil { - return nil - } - return p.Changes -} - -// GetInstallation returns the Installation field. -func (p *ProjectEvent) GetInstallation() *Installation { - if p == nil { - return nil - } - return p.Installation -} - -// GetOrg returns the Org field. -func (p *ProjectEvent) GetOrg() *Organization { - if p == nil { - return nil - } - return p.Org -} - -// GetProject returns the Project field. -func (p *ProjectEvent) GetProject() *Project { - if p == nil { - return nil - } - return p.Project -} - -// GetRepo returns the Repo field. -func (p *ProjectEvent) GetRepo() *Repository { - if p == nil { - return nil - } - return p.Repo -} - -// GetSender returns the Sender field. -func (p *ProjectEvent) GetSender() *User { - if p == nil { - return nil - } - return p.Sender -} - -// GetEnforceAdmins returns the EnforceAdmins field. -func (p *Protection) GetEnforceAdmins() *AdminEnforcement { - if p == nil { - return nil - } - return p.EnforceAdmins -} - -// GetRequiredPullRequestReviews returns the RequiredPullRequestReviews field. -func (p *Protection) GetRequiredPullRequestReviews() *PullRequestReviewsEnforcement { - if p == nil { - return nil - } - return p.RequiredPullRequestReviews -} - -// GetRequiredStatusChecks returns the RequiredStatusChecks field. -func (p *Protection) GetRequiredStatusChecks() *RequiredStatusChecks { - if p == nil { - return nil - } - return p.RequiredStatusChecks -} - -// GetRestrictions returns the Restrictions field. -func (p *Protection) GetRestrictions() *BranchRestrictions { - if p == nil { - return nil - } - return p.Restrictions -} - -// GetRequiredPullRequestReviews returns the RequiredPullRequestReviews field. -func (p *ProtectionRequest) GetRequiredPullRequestReviews() *PullRequestReviewsEnforcementRequest { - if p == nil { - return nil - } - return p.RequiredPullRequestReviews -} - -// GetRequiredStatusChecks returns the RequiredStatusChecks field. -func (p *ProtectionRequest) GetRequiredStatusChecks() *RequiredStatusChecks { - if p == nil { - return nil - } - return p.RequiredStatusChecks -} - -// GetRestrictions returns the Restrictions field. -func (p *ProtectionRequest) GetRestrictions() *BranchRestrictionsRequest { - if p == nil { - return nil - } - return p.Restrictions -} - -// GetInstallation returns the Installation field. -func (p *PublicEvent) GetInstallation() *Installation { - if p == nil { - return nil - } - return p.Installation -} - -// GetRepo returns the Repo field. -func (p *PublicEvent) GetRepo() *Repository { - if p == nil { - return nil - } - return p.Repo -} - -// GetSender returns the Sender field. -func (p *PublicEvent) GetSender() *User { - if p == nil { - return nil - } - return p.Sender -} - -// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetAdditions() int { - if p == nil || p.Additions == nil { - return 0 - } - return *p.Additions -} - -// GetAssignee returns the Assignee field. -func (p *PullRequest) GetAssignee() *User { - if p == nil { - return nil - } - return p.Assignee -} - -// GetAuthorAssociation returns the AuthorAssociation field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetAuthorAssociation() string { - if p == nil || p.AuthorAssociation == nil { - return "" - } - return *p.AuthorAssociation -} - -// GetBase returns the Base field. -func (p *PullRequest) GetBase() *PullRequestBranch { - if p == nil { - return nil - } - return p.Base -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetBody() string { - if p == nil || p.Body == nil { - return "" - } - return *p.Body -} - -// GetChangedFiles returns the ChangedFiles field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetChangedFiles() int { - if p == nil || p.ChangedFiles == nil { - return 0 - } - return *p.ChangedFiles -} - -// GetClosedAt returns the ClosedAt field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetClosedAt() time.Time { - if p == nil || p.ClosedAt == nil { - return time.Time{} - } - return *p.ClosedAt -} - -// GetComments returns the Comments field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetComments() int { - if p == nil || p.Comments == nil { - return 0 - } - return *p.Comments -} - -// GetCommits returns the Commits field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetCommits() int { - if p == nil || p.Commits == nil { - return 0 - } - return *p.Commits -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetCreatedAt() time.Time { - if p == nil || p.CreatedAt == nil { - return time.Time{} - } - return *p.CreatedAt -} - -// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetDeletions() int { - if p == nil || p.Deletions == nil { - return 0 - } - return *p.Deletions -} - -// GetDiffURL returns the DiffURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetDiffURL() string { - if p == nil || p.DiffURL == nil { - return "" - } - return *p.DiffURL -} - -// GetHead returns the Head field. -func (p *PullRequest) GetHead() *PullRequestBranch { - if p == nil { - return nil - } - return p.Head -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { - return "" - } - return *p.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetID() int64 { - if p == nil || p.ID == nil { - return 0 - } - return *p.ID -} - -// GetIssueURL returns the IssueURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetIssueURL() string { - if p == nil || p.IssueURL == nil { - return "" - } - return *p.IssueURL -} - -// GetMaintainerCanModify returns the MaintainerCanModify field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetMaintainerCanModify() bool { - if p == nil || p.MaintainerCanModify == nil { - return false - } - return *p.MaintainerCanModify -} - -// GetMergeable returns the Mergeable field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetMergeable() bool { - if p == nil || p.Mergeable == nil { - return false - } - return *p.Mergeable -} - -// GetMergeableState returns the MergeableState field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetMergeableState() string { - if p == nil || p.MergeableState == nil { - return "" - } - return *p.MergeableState -} - -// GetMergeCommitSHA returns the MergeCommitSHA field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetMergeCommitSHA() string { - if p == nil || p.MergeCommitSHA == nil { - return "" - } - return *p.MergeCommitSHA -} - -// GetMerged returns the Merged field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetMerged() bool { - if p == nil || p.Merged == nil { - return false - } - return *p.Merged -} - -// GetMergedAt returns the MergedAt field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetMergedAt() time.Time { - if p == nil || p.MergedAt == nil { - return time.Time{} - } - return *p.MergedAt -} - -// GetMergedBy returns the MergedBy field. -func (p *PullRequest) GetMergedBy() *User { - if p == nil { - return nil - } - return p.MergedBy -} - -// GetMilestone returns the Milestone field. -func (p *PullRequest) GetMilestone() *Milestone { - if p == nil { - return nil - } - return p.Milestone -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetNodeID() string { - if p == nil || p.NodeID == nil { - return "" - } - return *p.NodeID -} - -// GetNumber returns the Number field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetNumber() int { - if p == nil || p.Number == nil { - return 0 - } - return *p.Number -} - -// GetPatchURL returns the PatchURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetPatchURL() string { - if p == nil || p.PatchURL == nil { - return "" - } - return *p.PatchURL -} - -// GetReviewCommentsURL returns the ReviewCommentsURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetReviewCommentsURL() string { - if p == nil || p.ReviewCommentsURL == nil { - return "" - } - return *p.ReviewCommentsURL -} - -// GetReviewCommentURL returns the ReviewCommentURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetReviewCommentURL() string { - if p == nil || p.ReviewCommentURL == nil { - return "" - } - return *p.ReviewCommentURL -} - -// GetState returns the State field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetState() string { - if p == nil || p.State == nil { - return "" - } - return *p.State -} - -// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetStatusesURL() string { - if p == nil || p.StatusesURL == nil { - return "" - } - return *p.StatusesURL -} - -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetTitle() string { - if p == nil || p.Title == nil { - return "" - } - return *p.Title -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetUpdatedAt() time.Time { - if p == nil || p.UpdatedAt == nil { - return time.Time{} - } - return *p.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PullRequest) GetURL() string { - if p == nil || p.URL == nil { - return "" - } - return *p.URL -} - -// GetUser returns the User field. -func (p *PullRequest) GetUser() *User { - if p == nil { - return nil - } - return p.User -} - -// GetLabel returns the Label field if it's non-nil, zero value otherwise. -func (p *PullRequestBranch) GetLabel() string { - if p == nil || p.Label == nil { - return "" - } - return *p.Label -} - -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (p *PullRequestBranch) GetRef() string { - if p == nil || p.Ref == nil { - return "" - } - return *p.Ref -} - -// GetRepo returns the Repo field. -func (p *PullRequestBranch) GetRepo() *Repository { - if p == nil { - return nil - } - return p.Repo -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (p *PullRequestBranch) GetSHA() string { - if p == nil || p.SHA == nil { - return "" - } - return *p.SHA -} - -// GetUser returns the User field. -func (p *PullRequestBranch) GetUser() *User { - if p == nil { - return nil - } - return p.User -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetBody() string { - if p == nil || p.Body == nil { - return "" - } - return *p.Body -} - -// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetCommitID() string { - if p == nil || p.CommitID == nil { - return "" - } - return *p.CommitID -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetCreatedAt() time.Time { - if p == nil || p.CreatedAt == nil { - return time.Time{} - } - return *p.CreatedAt -} - -// GetDiffHunk returns the DiffHunk field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetDiffHunk() string { - if p == nil || p.DiffHunk == nil { - return "" - } - return *p.DiffHunk -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { - return "" - } - return *p.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetID() int64 { - if p == nil || p.ID == nil { - return 0 - } - return *p.ID -} - -// GetInReplyTo returns the InReplyTo field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetInReplyTo() int64 { - if p == nil || p.InReplyTo == nil { - return 0 - } - return *p.InReplyTo -} - -// GetOriginalCommitID returns the OriginalCommitID field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetOriginalCommitID() string { - if p == nil || p.OriginalCommitID == nil { - return "" - } - return *p.OriginalCommitID -} - -// GetOriginalPosition returns the OriginalPosition field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetOriginalPosition() int { - if p == nil || p.OriginalPosition == nil { - return 0 - } - return *p.OriginalPosition -} - -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetPath() string { - if p == nil || p.Path == nil { - return "" - } - return *p.Path -} - -// GetPosition returns the Position field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetPosition() int { - if p == nil || p.Position == nil { - return 0 - } - return *p.Position -} - -// GetPullRequestURL returns the PullRequestURL field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetPullRequestURL() string { - if p == nil || p.PullRequestURL == nil { - return "" - } - return *p.PullRequestURL -} - -// GetReactions returns the Reactions field. -func (p *PullRequestComment) GetReactions() *Reactions { - if p == nil { - return nil - } - return p.Reactions -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetUpdatedAt() time.Time { - if p == nil || p.UpdatedAt == nil { - return time.Time{} - } - return *p.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PullRequestComment) GetURL() string { - if p == nil || p.URL == nil { - return "" - } - return *p.URL -} - -// GetUser returns the User field. -func (p *PullRequestComment) GetUser() *User { - if p == nil { - return nil - } - return p.User -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *PullRequestEvent) GetAction() string { - if p == nil || p.Action == nil { - return "" - } - return *p.Action -} - -// GetChanges returns the Changes field. -func (p *PullRequestEvent) GetChanges() *EditChange { - if p == nil { - return nil - } - return p.Changes -} - -// GetInstallation returns the Installation field. -func (p *PullRequestEvent) GetInstallation() *Installation { - if p == nil { - return nil - } - return p.Installation -} - -// GetNumber returns the Number field if it's non-nil, zero value otherwise. -func (p *PullRequestEvent) GetNumber() int { - if p == nil || p.Number == nil { - return 0 - } - return *p.Number -} - -// GetPullRequest returns the PullRequest field. -func (p *PullRequestEvent) GetPullRequest() *PullRequest { - if p == nil { - return nil - } - return p.PullRequest -} - -// GetRepo returns the Repo field. -func (p *PullRequestEvent) GetRepo() *Repository { - if p == nil { - return nil - } - return p.Repo -} - -// GetSender returns the Sender field. -func (p *PullRequestEvent) GetSender() *User { - if p == nil { - return nil - } - return p.Sender -} - -// GetDiffURL returns the DiffURL field if it's non-nil, zero value otherwise. -func (p *PullRequestLinks) GetDiffURL() string { - if p == nil || p.DiffURL == nil { - return "" - } - return *p.DiffURL -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *PullRequestLinks) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { - return "" - } - return *p.HTMLURL -} - -// GetPatchURL returns the PatchURL field if it's non-nil, zero value otherwise. -func (p *PullRequestLinks) GetPatchURL() string { - if p == nil || p.PatchURL == nil { - return "" - } - return *p.PatchURL -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PullRequestLinks) GetURL() string { - if p == nil || p.URL == nil { - return "" - } - return *p.URL -} - -// GetMerged returns the Merged field if it's non-nil, zero value otherwise. -func (p *PullRequestMergeResult) GetMerged() bool { - if p == nil || p.Merged == nil { - return false - } - return *p.Merged -} - -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (p *PullRequestMergeResult) GetMessage() string { - if p == nil || p.Message == nil { - return "" - } - return *p.Message -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (p *PullRequestMergeResult) GetSHA() string { - if p == nil || p.SHA == nil { - return "" - } - return *p.SHA -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetBody() string { - if p == nil || p.Body == nil { - return "" - } - return *p.Body -} - -// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetCommitID() string { - if p == nil || p.CommitID == nil { - return "" - } - return *p.CommitID -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { - return "" - } - return *p.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetID() int64 { - if p == nil || p.ID == nil { - return 0 - } - return *p.ID -} - -// GetPullRequestURL returns the PullRequestURL field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetPullRequestURL() string { - if p == nil || p.PullRequestURL == nil { - return "" - } - return *p.PullRequestURL -} - -// GetState returns the State field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetState() string { - if p == nil || p.State == nil { - return "" - } - return *p.State -} - -// GetSubmittedAt returns the SubmittedAt field if it's non-nil, zero value otherwise. -func (p *PullRequestReview) GetSubmittedAt() time.Time { - if p == nil || p.SubmittedAt == nil { - return time.Time{} - } - return *p.SubmittedAt -} - -// GetUser returns the User field. -func (p *PullRequestReview) GetUser() *User { - if p == nil { - return nil - } - return p.User -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewCommentEvent) GetAction() string { - if p == nil || p.Action == nil { - return "" - } - return *p.Action -} - -// GetChanges returns the Changes field. -func (p *PullRequestReviewCommentEvent) GetChanges() *EditChange { - if p == nil { - return nil - } - return p.Changes -} - -// GetComment returns the Comment field. -func (p *PullRequestReviewCommentEvent) GetComment() *PullRequestComment { - if p == nil { - return nil - } - return p.Comment -} - -// GetInstallation returns the Installation field. -func (p *PullRequestReviewCommentEvent) GetInstallation() *Installation { - if p == nil { - return nil - } - return p.Installation -} - -// GetPullRequest returns the PullRequest field. -func (p *PullRequestReviewCommentEvent) GetPullRequest() *PullRequest { - if p == nil { - return nil - } - return p.PullRequest -} - -// GetRepo returns the Repo field. -func (p *PullRequestReviewCommentEvent) GetRepo() *Repository { - if p == nil { - return nil - } - return p.Repo -} - -// GetSender returns the Sender field. -func (p *PullRequestReviewCommentEvent) GetSender() *User { - if p == nil { - return nil - } - return p.Sender -} - -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewDismissalRequest) GetMessage() string { - if p == nil || p.Message == nil { - return "" - } - return *p.Message -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewEvent) GetAction() string { - if p == nil || p.Action == nil { - return "" - } - return *p.Action -} - -// GetInstallation returns the Installation field. -func (p *PullRequestReviewEvent) GetInstallation() *Installation { - if p == nil { - return nil - } - return p.Installation -} - -// GetOrganization returns the Organization field. -func (p *PullRequestReviewEvent) GetOrganization() *Organization { - if p == nil { - return nil - } - return p.Organization -} - -// GetPullRequest returns the PullRequest field. -func (p *PullRequestReviewEvent) GetPullRequest() *PullRequest { - if p == nil { - return nil - } - return p.PullRequest -} - -// GetRepo returns the Repo field. -func (p *PullRequestReviewEvent) GetRepo() *Repository { - if p == nil { - return nil - } - return p.Repo -} - -// GetReview returns the Review field. -func (p *PullRequestReviewEvent) GetReview() *PullRequestReview { - if p == nil { - return nil - } - return p.Review -} - -// GetSender returns the Sender field. -func (p *PullRequestReviewEvent) GetSender() *User { - if p == nil { - return nil - } - return p.Sender -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewRequest) GetBody() string { - if p == nil || p.Body == nil { - return "" - } - return *p.Body -} - -// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewRequest) GetCommitID() string { - if p == nil || p.CommitID == nil { - return "" - } - return *p.CommitID -} - -// GetEvent returns the Event field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewRequest) GetEvent() string { - if p == nil || p.Event == nil { - return "" - } - return *p.Event -} - -// GetDismissalRestrictionsRequest returns the DismissalRestrictionsRequest field. -func (p *PullRequestReviewsEnforcementRequest) GetDismissalRestrictionsRequest() *DismissalRestrictionsRequest { - if p == nil { - return nil - } - return p.DismissalRestrictionsRequest -} - -// GetDismissalRestrictionsRequest returns the DismissalRestrictionsRequest field. -func (p *PullRequestReviewsEnforcementUpdate) GetDismissalRestrictionsRequest() *DismissalRestrictionsRequest { - if p == nil { - return nil - } - return p.DismissalRestrictionsRequest -} - -// GetDismissStaleReviews returns the DismissStaleReviews field if it's non-nil, zero value otherwise. -func (p *PullRequestReviewsEnforcementUpdate) GetDismissStaleReviews() bool { - if p == nil || p.DismissStaleReviews == nil { - return false - } - return *p.DismissStaleReviews -} - -// GetMergablePulls returns the MergablePulls field if it's non-nil, zero value otherwise. -func (p *PullStats) GetMergablePulls() int { - if p == nil || p.MergablePulls == nil { - return 0 - } - return *p.MergablePulls -} - -// GetMergedPulls returns the MergedPulls field if it's non-nil, zero value otherwise. -func (p *PullStats) GetMergedPulls() int { - if p == nil || p.MergedPulls == nil { - return 0 - } - return *p.MergedPulls -} - -// GetTotalPulls returns the TotalPulls field if it's non-nil, zero value otherwise. -func (p *PullStats) GetTotalPulls() int { - if p == nil || p.TotalPulls == nil { - return 0 - } - return *p.TotalPulls -} - -// GetUnmergablePulls returns the UnmergablePulls field if it's non-nil, zero value otherwise. -func (p *PullStats) GetUnmergablePulls() int { - if p == nil || p.UnmergablePulls == nil { - return 0 - } - return *p.UnmergablePulls -} - -// GetCommits returns the Commits field if it's non-nil, zero value otherwise. -func (p *PunchCard) GetCommits() int { - if p == nil || p.Commits == nil { - return 0 - } - return *p.Commits -} - -// GetDay returns the Day field if it's non-nil, zero value otherwise. -func (p *PunchCard) GetDay() int { - if p == nil || p.Day == nil { - return 0 - } - return *p.Day -} - -// GetHour returns the Hour field if it's non-nil, zero value otherwise. -func (p *PunchCard) GetHour() int { - if p == nil || p.Hour == nil { - return 0 - } - return *p.Hour -} - -// GetAfter returns the After field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetAfter() string { - if p == nil || p.After == nil { - return "" - } - return *p.After -} - -// GetBaseRef returns the BaseRef field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetBaseRef() string { - if p == nil || p.BaseRef == nil { - return "" - } - return *p.BaseRef -} - -// GetBefore returns the Before field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetBefore() string { - if p == nil || p.Before == nil { - return "" - } - return *p.Before -} - -// GetCompare returns the Compare field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetCompare() string { - if p == nil || p.Compare == nil { - return "" - } - return *p.Compare -} - -// GetCreated returns the Created field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetCreated() bool { - if p == nil || p.Created == nil { - return false - } - return *p.Created -} - -// GetDeleted returns the Deleted field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetDeleted() bool { - if p == nil || p.Deleted == nil { - return false - } - return *p.Deleted -} - -// GetDistinctSize returns the DistinctSize field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetDistinctSize() int { - if p == nil || p.DistinctSize == nil { - return 0 - } - return *p.DistinctSize -} - -// GetForced returns the Forced field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetForced() bool { - if p == nil || p.Forced == nil { - return false - } - return *p.Forced -} - -// GetHead returns the Head field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetHead() string { - if p == nil || p.Head == nil { - return "" - } - return *p.Head -} - -// GetHeadCommit returns the HeadCommit field. -func (p *PushEvent) GetHeadCommit() *PushEventCommit { - if p == nil { - return nil - } - return p.HeadCommit -} - -// GetInstallation returns the Installation field. -func (p *PushEvent) GetInstallation() *Installation { - if p == nil { - return nil - } - return p.Installation -} - -// GetPusher returns the Pusher field. -func (p *PushEvent) GetPusher() *User { - if p == nil { - return nil - } - return p.Pusher -} - -// GetPushID returns the PushID field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetPushID() int64 { - if p == nil || p.PushID == nil { - return 0 - } - return *p.PushID -} - -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetRef() string { - if p == nil || p.Ref == nil { - return "" - } - return *p.Ref -} - -// GetRepo returns the Repo field. -func (p *PushEvent) GetRepo() *PushEventRepository { - if p == nil { - return nil - } - return p.Repo -} - -// GetSender returns the Sender field. -func (p *PushEvent) GetSender() *User { - if p == nil { - return nil - } - return p.Sender -} - -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (p *PushEvent) GetSize() int { - if p == nil || p.Size == nil { - return 0 - } - return *p.Size -} - -// GetAuthor returns the Author field. -func (p *PushEventCommit) GetAuthor() *CommitAuthor { - if p == nil { - return nil - } - return p.Author -} - -// GetCommitter returns the Committer field. -func (p *PushEventCommit) GetCommitter() *CommitAuthor { - if p == nil { - return nil - } - return p.Committer -} - -// GetDistinct returns the Distinct field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetDistinct() bool { - if p == nil || p.Distinct == nil { - return false - } - return *p.Distinct -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetID() string { - if p == nil || p.ID == nil { - return "" - } - return *p.ID -} - -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetMessage() string { - if p == nil || p.Message == nil { - return "" - } - return *p.Message -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetSHA() string { - if p == nil || p.SHA == nil { - return "" - } - return *p.SHA -} - -// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetTimestamp() Timestamp { - if p == nil || p.Timestamp == nil { - return Timestamp{} - } - return *p.Timestamp -} - -// GetTreeID returns the TreeID field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetTreeID() string { - if p == nil || p.TreeID == nil { - return "" - } - return *p.TreeID -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PushEventCommit) GetURL() string { - if p == nil || p.URL == nil { - return "" - } - return *p.URL -} - -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (p *PushEventRepoOwner) GetEmail() string { - if p == nil || p.Email == nil { - return "" - } - return *p.Email -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (p *PushEventRepoOwner) GetName() string { - if p == nil || p.Name == nil { - return "" - } - return *p.Name -} - -// GetArchiveURL returns the ArchiveURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetArchiveURL() string { - if p == nil || p.ArchiveURL == nil { - return "" - } - return *p.ArchiveURL -} - -// GetCloneURL returns the CloneURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetCloneURL() string { - if p == nil || p.CloneURL == nil { - return "" - } - return *p.CloneURL -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetCreatedAt() Timestamp { - if p == nil || p.CreatedAt == nil { - return Timestamp{} - } - return *p.CreatedAt -} - -// GetDefaultBranch returns the DefaultBranch field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetDefaultBranch() string { - if p == nil || p.DefaultBranch == nil { - return "" - } - return *p.DefaultBranch -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetDescription() string { - if p == nil || p.Description == nil { - return "" - } - return *p.Description -} - -// GetFork returns the Fork field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetFork() bool { - if p == nil || p.Fork == nil { - return false - } - return *p.Fork -} - -// GetForksCount returns the ForksCount field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetForksCount() int { - if p == nil || p.ForksCount == nil { - return 0 - } - return *p.ForksCount -} - -// GetFullName returns the FullName field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetFullName() string { - if p == nil || p.FullName == nil { - return "" - } - return *p.FullName -} - -// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetGitURL() string { - if p == nil || p.GitURL == nil { - return "" - } - return *p.GitURL -} - -// GetHasDownloads returns the HasDownloads field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetHasDownloads() bool { - if p == nil || p.HasDownloads == nil { - return false - } - return *p.HasDownloads -} - -// GetHasIssues returns the HasIssues field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetHasIssues() bool { - if p == nil || p.HasIssues == nil { - return false - } - return *p.HasIssues -} - -// GetHasPages returns the HasPages field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetHasPages() bool { - if p == nil || p.HasPages == nil { - return false - } - return *p.HasPages -} - -// GetHasWiki returns the HasWiki field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetHasWiki() bool { - if p == nil || p.HasWiki == nil { - return false - } - return *p.HasWiki -} - -// GetHomepage returns the Homepage field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetHomepage() string { - if p == nil || p.Homepage == nil { - return "" - } - return *p.Homepage -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetHTMLURL() string { - if p == nil || p.HTMLURL == nil { - return "" - } - return *p.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetID() int64 { - if p == nil || p.ID == nil { - return 0 - } - return *p.ID -} - -// GetLanguage returns the Language field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetLanguage() string { - if p == nil || p.Language == nil { - return "" - } - return *p.Language -} - -// GetMasterBranch returns the MasterBranch field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetMasterBranch() string { - if p == nil || p.MasterBranch == nil { - return "" - } - return *p.MasterBranch -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetName() string { - if p == nil || p.Name == nil { - return "" - } - return *p.Name -} - -// GetOpenIssuesCount returns the OpenIssuesCount field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetOpenIssuesCount() int { - if p == nil || p.OpenIssuesCount == nil { - return 0 - } - return *p.OpenIssuesCount -} - -// GetOrganization returns the Organization field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetOrganization() string { - if p == nil || p.Organization == nil { - return "" - } - return *p.Organization -} - -// GetOwner returns the Owner field. -func (p *PushEventRepository) GetOwner() *PushEventRepoOwner { - if p == nil { - return nil - } - return p.Owner -} - -// GetPrivate returns the Private field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetPrivate() bool { - if p == nil || p.Private == nil { - return false - } - return *p.Private -} - -// GetPushedAt returns the PushedAt field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetPushedAt() Timestamp { - if p == nil || p.PushedAt == nil { - return Timestamp{} - } - return *p.PushedAt -} - -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetSize() int { - if p == nil || p.Size == nil { - return 0 - } - return *p.Size -} - -// GetSSHURL returns the SSHURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetSSHURL() string { - if p == nil || p.SSHURL == nil { - return "" - } - return *p.SSHURL -} - -// GetStargazersCount returns the StargazersCount field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetStargazersCount() int { - if p == nil || p.StargazersCount == nil { - return 0 - } - return *p.StargazersCount -} - -// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetStatusesURL() string { - if p == nil || p.StatusesURL == nil { - return "" - } - return *p.StatusesURL -} - -// GetSVNURL returns the SVNURL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetSVNURL() string { - if p == nil || p.SVNURL == nil { - return "" - } - return *p.SVNURL -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetUpdatedAt() Timestamp { - if p == nil || p.UpdatedAt == nil { - return Timestamp{} - } - return *p.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetURL() string { - if p == nil || p.URL == nil { - return "" - } - return *p.URL -} - -// GetWatchersCount returns the WatchersCount field if it's non-nil, zero value otherwise. -func (p *PushEventRepository) GetWatchersCount() int { - if p == nil || p.WatchersCount == nil { - return 0 - } - return *p.WatchersCount -} - -// GetCore returns the Core field. -func (r *RateLimits) GetCore() *Rate { - if r == nil { - return nil - } - return r.Core -} - -// GetSearch returns the Search field. -func (r *RateLimits) GetSearch() *Rate { - if r == nil { - return nil - } - return r.Search -} - -// GetContent returns the Content field if it's non-nil, zero value otherwise. -func (r *Reaction) GetContent() string { - if r == nil || r.Content == nil { - return "" - } - return *r.Content -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *Reaction) GetID() int64 { - if r == nil || r.ID == nil { - return 0 - } - return *r.ID -} - -// GetUser returns the User field. -func (r *Reaction) GetUser() *User { - if r == nil { - return nil - } - return r.User -} - -// GetConfused returns the Confused field if it's non-nil, zero value otherwise. -func (r *Reactions) GetConfused() int { - if r == nil || r.Confused == nil { - return 0 - } - return *r.Confused -} - -// GetHeart returns the Heart field if it's non-nil, zero value otherwise. -func (r *Reactions) GetHeart() int { - if r == nil || r.Heart == nil { - return 0 - } - return *r.Heart -} - -// GetHooray returns the Hooray field if it's non-nil, zero value otherwise. -func (r *Reactions) GetHooray() int { - if r == nil || r.Hooray == nil { - return 0 - } - return *r.Hooray -} - -// GetLaugh returns the Laugh field if it's non-nil, zero value otherwise. -func (r *Reactions) GetLaugh() int { - if r == nil || r.Laugh == nil { - return 0 - } - return *r.Laugh -} - -// GetMinusOne returns the MinusOne field if it's non-nil, zero value otherwise. -func (r *Reactions) GetMinusOne() int { - if r == nil || r.MinusOne == nil { - return 0 - } - return *r.MinusOne -} - -// GetPlusOne returns the PlusOne field if it's non-nil, zero value otherwise. -func (r *Reactions) GetPlusOne() int { - if r == nil || r.PlusOne == nil { - return 0 - } - return *r.PlusOne -} - -// GetTotalCount returns the TotalCount field if it's non-nil, zero value otherwise. -func (r *Reactions) GetTotalCount() int { - if r == nil || r.TotalCount == nil { - return 0 - } - return *r.TotalCount -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *Reactions) GetURL() string { - if r == nil || r.URL == nil { - return "" - } - return *r.URL -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (r *Reference) GetNodeID() string { - if r == nil || r.NodeID == nil { - return "" - } - return *r.NodeID -} - -// GetObject returns the Object field. -func (r *Reference) GetObject() *GitObject { - if r == nil { - return nil - } - return r.Object -} - -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (r *Reference) GetRef() string { - if r == nil || r.Ref == nil { - return "" - } - return *r.Ref -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *Reference) GetURL() string { - if r == nil || r.URL == nil { - return "" - } - return *r.URL -} - -// GetBrowserDownloadURL returns the BrowserDownloadURL field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetBrowserDownloadURL() string { - if r == nil || r.BrowserDownloadURL == nil { - return "" - } - return *r.BrowserDownloadURL -} - -// GetContentType returns the ContentType field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetContentType() string { - if r == nil || r.ContentType == nil { - return "" - } - return *r.ContentType -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetCreatedAt() Timestamp { - if r == nil || r.CreatedAt == nil { - return Timestamp{} - } - return *r.CreatedAt -} - -// GetDownloadCount returns the DownloadCount field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetDownloadCount() int { - if r == nil || r.DownloadCount == nil { - return 0 - } - return *r.DownloadCount -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetID() int64 { - if r == nil || r.ID == nil { - return 0 - } - return *r.ID -} - -// GetLabel returns the Label field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetLabel() string { - if r == nil || r.Label == nil { - return "" - } - return *r.Label -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetName() string { - if r == nil || r.Name == nil { - return "" - } - return *r.Name -} - -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetSize() int { - if r == nil || r.Size == nil { - return 0 - } - return *r.Size -} - -// GetState returns the State field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetState() string { - if r == nil || r.State == nil { - return "" - } - return *r.State -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetUpdatedAt() Timestamp { - if r == nil || r.UpdatedAt == nil { - return Timestamp{} - } - return *r.UpdatedAt -} - -// GetUploader returns the Uploader field. -func (r *ReleaseAsset) GetUploader() *User { - if r == nil { - return nil - } - return r.Uploader -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *ReleaseAsset) GetURL() string { - if r == nil || r.URL == nil { - return "" - } - return *r.URL -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (r *ReleaseEvent) GetAction() string { - if r == nil || r.Action == nil { - return "" - } - return *r.Action -} - -// GetInstallation returns the Installation field. -func (r *ReleaseEvent) GetInstallation() *Installation { - if r == nil { - return nil - } - return r.Installation -} - -// GetRelease returns the Release field. -func (r *ReleaseEvent) GetRelease() *RepositoryRelease { - if r == nil { - return nil - } - return r.Release -} - -// GetRepo returns the Repo field. -func (r *ReleaseEvent) GetRepo() *Repository { - if r == nil { - return nil - } - return r.Repo -} - -// GetSender returns the Sender field. -func (r *ReleaseEvent) GetSender() *User { - if r == nil { - return nil - } - return r.Sender -} - -// GetFrom returns the From field if it's non-nil, zero value otherwise. -func (r *Rename) GetFrom() string { - if r == nil || r.From == nil { - return "" - } - return *r.From -} - -// GetTo returns the To field if it's non-nil, zero value otherwise. -func (r *Rename) GetTo() string { - if r == nil || r.To == nil { - return "" - } - return *r.To -} - -// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. -func (r *RepositoriesSearchResult) GetIncompleteResults() bool { - if r == nil || r.IncompleteResults == nil { - return false - } - return *r.IncompleteResults -} - -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (r *RepositoriesSearchResult) GetTotal() int { - if r == nil || r.Total == nil { - return 0 - } - return *r.Total -} - -// GetAllowMergeCommit returns the AllowMergeCommit field if it's non-nil, zero value otherwise. -func (r *Repository) GetAllowMergeCommit() bool { - if r == nil || r.AllowMergeCommit == nil { - return false - } - return *r.AllowMergeCommit -} - -// GetAllowRebaseMerge returns the AllowRebaseMerge field if it's non-nil, zero value otherwise. -func (r *Repository) GetAllowRebaseMerge() bool { - if r == nil || r.AllowRebaseMerge == nil { - return false - } - return *r.AllowRebaseMerge -} - -// GetAllowSquashMerge returns the AllowSquashMerge field if it's non-nil, zero value otherwise. -func (r *Repository) GetAllowSquashMerge() bool { - if r == nil || r.AllowSquashMerge == nil { - return false - } - return *r.AllowSquashMerge -} - -// GetArchived returns the Archived field if it's non-nil, zero value otherwise. -func (r *Repository) GetArchived() bool { - if r == nil || r.Archived == nil { - return false - } - return *r.Archived -} - -// GetArchiveURL returns the ArchiveURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetArchiveURL() string { - if r == nil || r.ArchiveURL == nil { - return "" - } - return *r.ArchiveURL -} - -// GetAssigneesURL returns the AssigneesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetAssigneesURL() string { - if r == nil || r.AssigneesURL == nil { - return "" - } - return *r.AssigneesURL -} - -// GetAutoInit returns the AutoInit field if it's non-nil, zero value otherwise. -func (r *Repository) GetAutoInit() bool { - if r == nil || r.AutoInit == nil { - return false - } - return *r.AutoInit -} - -// GetBlobsURL returns the BlobsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetBlobsURL() string { - if r == nil || r.BlobsURL == nil { - return "" - } - return *r.BlobsURL -} - -// GetBranchesURL returns the BranchesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetBranchesURL() string { - if r == nil || r.BranchesURL == nil { - return "" - } - return *r.BranchesURL -} - -// GetCloneURL returns the CloneURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetCloneURL() string { - if r == nil || r.CloneURL == nil { - return "" - } - return *r.CloneURL -} - -// GetCodeOfConduct returns the CodeOfConduct field. -func (r *Repository) GetCodeOfConduct() *CodeOfConduct { - if r == nil { - return nil - } - return r.CodeOfConduct -} - -// GetCollaboratorsURL returns the CollaboratorsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetCollaboratorsURL() string { - if r == nil || r.CollaboratorsURL == nil { - return "" - } - return *r.CollaboratorsURL -} - -// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetCommentsURL() string { - if r == nil || r.CommentsURL == nil { - return "" - } - return *r.CommentsURL -} - -// GetCommitsURL returns the CommitsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetCommitsURL() string { - if r == nil || r.CommitsURL == nil { - return "" - } - return *r.CommitsURL -} - -// GetCompareURL returns the CompareURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetCompareURL() string { - if r == nil || r.CompareURL == nil { - return "" - } - return *r.CompareURL -} - -// GetContentsURL returns the ContentsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetContentsURL() string { - if r == nil || r.ContentsURL == nil { - return "" - } - return *r.ContentsURL -} - -// GetContributorsURL returns the ContributorsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetContributorsURL() string { - if r == nil || r.ContributorsURL == nil { - return "" - } - return *r.ContributorsURL -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *Repository) GetCreatedAt() Timestamp { - if r == nil || r.CreatedAt == nil { - return Timestamp{} - } - return *r.CreatedAt -} - -// GetDefaultBranch returns the DefaultBranch field if it's non-nil, zero value otherwise. -func (r *Repository) GetDefaultBranch() string { - if r == nil || r.DefaultBranch == nil { - return "" - } - return *r.DefaultBranch -} - -// GetDeploymentsURL returns the DeploymentsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetDeploymentsURL() string { - if r == nil || r.DeploymentsURL == nil { - return "" - } - return *r.DeploymentsURL -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (r *Repository) GetDescription() string { - if r == nil || r.Description == nil { - return "" - } - return *r.Description -} - -// GetDownloadsURL returns the DownloadsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetDownloadsURL() string { - if r == nil || r.DownloadsURL == nil { - return "" - } - return *r.DownloadsURL -} - -// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetEventsURL() string { - if r == nil || r.EventsURL == nil { - return "" - } - return *r.EventsURL -} - -// GetFork returns the Fork field if it's non-nil, zero value otherwise. -func (r *Repository) GetFork() bool { - if r == nil || r.Fork == nil { - return false - } - return *r.Fork -} - -// GetForksCount returns the ForksCount field if it's non-nil, zero value otherwise. -func (r *Repository) GetForksCount() int { - if r == nil || r.ForksCount == nil { - return 0 - } - return *r.ForksCount -} - -// GetForksURL returns the ForksURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetForksURL() string { - if r == nil || r.ForksURL == nil { - return "" - } - return *r.ForksURL -} - -// GetFullName returns the FullName field if it's non-nil, zero value otherwise. -func (r *Repository) GetFullName() string { - if r == nil || r.FullName == nil { - return "" - } - return *r.FullName -} - -// GetGitCommitsURL returns the GitCommitsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetGitCommitsURL() string { - if r == nil || r.GitCommitsURL == nil { - return "" - } - return *r.GitCommitsURL -} - -// GetGitignoreTemplate returns the GitignoreTemplate field if it's non-nil, zero value otherwise. -func (r *Repository) GetGitignoreTemplate() string { - if r == nil || r.GitignoreTemplate == nil { - return "" - } - return *r.GitignoreTemplate -} - -// GetGitRefsURL returns the GitRefsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetGitRefsURL() string { - if r == nil || r.GitRefsURL == nil { - return "" - } - return *r.GitRefsURL -} - -// GetGitTagsURL returns the GitTagsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetGitTagsURL() string { - if r == nil || r.GitTagsURL == nil { - return "" - } - return *r.GitTagsURL -} - -// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetGitURL() string { - if r == nil || r.GitURL == nil { - return "" - } - return *r.GitURL -} - -// GetHasDownloads returns the HasDownloads field if it's non-nil, zero value otherwise. -func (r *Repository) GetHasDownloads() bool { - if r == nil || r.HasDownloads == nil { - return false - } - return *r.HasDownloads -} - -// GetHasIssues returns the HasIssues field if it's non-nil, zero value otherwise. -func (r *Repository) GetHasIssues() bool { - if r == nil || r.HasIssues == nil { - return false - } - return *r.HasIssues -} - -// GetHasPages returns the HasPages field if it's non-nil, zero value otherwise. -func (r *Repository) GetHasPages() bool { - if r == nil || r.HasPages == nil { - return false - } - return *r.HasPages -} - -// GetHasProjects returns the HasProjects field if it's non-nil, zero value otherwise. -func (r *Repository) GetHasProjects() bool { - if r == nil || r.HasProjects == nil { - return false - } - return *r.HasProjects -} - -// GetHasWiki returns the HasWiki field if it's non-nil, zero value otherwise. -func (r *Repository) GetHasWiki() bool { - if r == nil || r.HasWiki == nil { - return false - } - return *r.HasWiki -} - -// GetHomepage returns the Homepage field if it's non-nil, zero value otherwise. -func (r *Repository) GetHomepage() string { - if r == nil || r.Homepage == nil { - return "" - } - return *r.Homepage -} - -// GetHooksURL returns the HooksURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetHooksURL() string { - if r == nil || r.HooksURL == nil { - return "" - } - return *r.HooksURL -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { - return "" - } - return *r.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *Repository) GetID() int64 { - if r == nil || r.ID == nil { - return 0 - } - return *r.ID -} - -// GetIssueCommentURL returns the IssueCommentURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetIssueCommentURL() string { - if r == nil || r.IssueCommentURL == nil { - return "" - } - return *r.IssueCommentURL -} - -// GetIssueEventsURL returns the IssueEventsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetIssueEventsURL() string { - if r == nil || r.IssueEventsURL == nil { - return "" - } - return *r.IssueEventsURL -} - -// GetIssuesURL returns the IssuesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetIssuesURL() string { - if r == nil || r.IssuesURL == nil { - return "" - } - return *r.IssuesURL -} - -// GetKeysURL returns the KeysURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetKeysURL() string { - if r == nil || r.KeysURL == nil { - return "" - } - return *r.KeysURL -} - -// GetLabelsURL returns the LabelsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetLabelsURL() string { - if r == nil || r.LabelsURL == nil { - return "" - } - return *r.LabelsURL -} - -// GetLanguage returns the Language field if it's non-nil, zero value otherwise. -func (r *Repository) GetLanguage() string { - if r == nil || r.Language == nil { - return "" - } - return *r.Language -} - -// GetLanguagesURL returns the LanguagesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetLanguagesURL() string { - if r == nil || r.LanguagesURL == nil { - return "" - } - return *r.LanguagesURL -} - -// GetLicense returns the License field. -func (r *Repository) GetLicense() *License { - if r == nil { - return nil - } - return r.License -} - -// GetLicenseTemplate returns the LicenseTemplate field if it's non-nil, zero value otherwise. -func (r *Repository) GetLicenseTemplate() string { - if r == nil || r.LicenseTemplate == nil { - return "" - } - return *r.LicenseTemplate -} - -// GetMasterBranch returns the MasterBranch field if it's non-nil, zero value otherwise. -func (r *Repository) GetMasterBranch() string { - if r == nil || r.MasterBranch == nil { - return "" - } - return *r.MasterBranch -} - -// GetMergesURL returns the MergesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetMergesURL() string { - if r == nil || r.MergesURL == nil { - return "" - } - return *r.MergesURL -} - -// GetMilestonesURL returns the MilestonesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetMilestonesURL() string { - if r == nil || r.MilestonesURL == nil { - return "" - } - return *r.MilestonesURL -} - -// GetMirrorURL returns the MirrorURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetMirrorURL() string { - if r == nil || r.MirrorURL == nil { - return "" - } - return *r.MirrorURL -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (r *Repository) GetName() string { - if r == nil || r.Name == nil { - return "" - } - return *r.Name -} - -// GetNetworkCount returns the NetworkCount field if it's non-nil, zero value otherwise. -func (r *Repository) GetNetworkCount() int { - if r == nil || r.NetworkCount == nil { - return 0 - } - return *r.NetworkCount -} - -// GetNotificationsURL returns the NotificationsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetNotificationsURL() string { - if r == nil || r.NotificationsURL == nil { - return "" - } - return *r.NotificationsURL -} - -// GetOpenIssuesCount returns the OpenIssuesCount field if it's non-nil, zero value otherwise. -func (r *Repository) GetOpenIssuesCount() int { - if r == nil || r.OpenIssuesCount == nil { - return 0 - } - return *r.OpenIssuesCount -} - -// GetOrganization returns the Organization field. -func (r *Repository) GetOrganization() *Organization { - if r == nil { - return nil - } - return r.Organization -} - -// GetOwner returns the Owner field. -func (r *Repository) GetOwner() *User { - if r == nil { - return nil - } - return r.Owner -} - -// GetParent returns the Parent field. -func (r *Repository) GetParent() *Repository { - if r == nil { - return nil - } - return r.Parent -} - -// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. -func (r *Repository) GetPermissions() map[string]bool { - if r == nil || r.Permissions == nil { - return map[string]bool{} - } - return *r.Permissions -} - -// GetPrivate returns the Private field if it's non-nil, zero value otherwise. -func (r *Repository) GetPrivate() bool { - if r == nil || r.Private == nil { - return false - } - return *r.Private -} - -// GetPullsURL returns the PullsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetPullsURL() string { - if r == nil || r.PullsURL == nil { - return "" - } - return *r.PullsURL -} - -// GetPushedAt returns the PushedAt field if it's non-nil, zero value otherwise. -func (r *Repository) GetPushedAt() Timestamp { - if r == nil || r.PushedAt == nil { - return Timestamp{} - } - return *r.PushedAt -} - -// GetReleasesURL returns the ReleasesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetReleasesURL() string { - if r == nil || r.ReleasesURL == nil { - return "" - } - return *r.ReleasesURL -} - -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (r *Repository) GetSize() int { - if r == nil || r.Size == nil { - return 0 - } - return *r.Size -} - -// GetSource returns the Source field. -func (r *Repository) GetSource() *Repository { - if r == nil { - return nil - } - return r.Source -} - -// GetSSHURL returns the SSHURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetSSHURL() string { - if r == nil || r.SSHURL == nil { - return "" - } - return *r.SSHURL -} - -// GetStargazersCount returns the StargazersCount field if it's non-nil, zero value otherwise. -func (r *Repository) GetStargazersCount() int { - if r == nil || r.StargazersCount == nil { - return 0 - } - return *r.StargazersCount -} - -// GetStargazersURL returns the StargazersURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetStargazersURL() string { - if r == nil || r.StargazersURL == nil { - return "" - } - return *r.StargazersURL -} - -// GetStatusesURL returns the StatusesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetStatusesURL() string { - if r == nil || r.StatusesURL == nil { - return "" - } - return *r.StatusesURL -} - -// GetSubscribersCount returns the SubscribersCount field if it's non-nil, zero value otherwise. -func (r *Repository) GetSubscribersCount() int { - if r == nil || r.SubscribersCount == nil { - return 0 - } - return *r.SubscribersCount -} - -// GetSubscribersURL returns the SubscribersURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetSubscribersURL() string { - if r == nil || r.SubscribersURL == nil { - return "" - } - return *r.SubscribersURL -} - -// GetSubscriptionURL returns the SubscriptionURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetSubscriptionURL() string { - if r == nil || r.SubscriptionURL == nil { - return "" - } - return *r.SubscriptionURL -} - -// GetSVNURL returns the SVNURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetSVNURL() string { - if r == nil || r.SVNURL == nil { - return "" - } - return *r.SVNURL -} - -// GetTagsURL returns the TagsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetTagsURL() string { - if r == nil || r.TagsURL == nil { - return "" - } - return *r.TagsURL -} - -// GetTeamID returns the TeamID field if it's non-nil, zero value otherwise. -func (r *Repository) GetTeamID() int64 { - if r == nil || r.TeamID == nil { - return 0 - } - return *r.TeamID -} - -// GetTeamsURL returns the TeamsURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetTeamsURL() string { - if r == nil || r.TeamsURL == nil { - return "" - } - return *r.TeamsURL -} - -// GetTreesURL returns the TreesURL field if it's non-nil, zero value otherwise. -func (r *Repository) GetTreesURL() string { - if r == nil || r.TreesURL == nil { - return "" - } - return *r.TreesURL -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (r *Repository) GetUpdatedAt() Timestamp { - if r == nil || r.UpdatedAt == nil { - return Timestamp{} - } - return *r.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *Repository) GetURL() string { - if r == nil || r.URL == nil { - return "" - } - return *r.URL -} - -// GetWatchersCount returns the WatchersCount field if it's non-nil, zero value otherwise. -func (r *Repository) GetWatchersCount() int { - if r == nil || r.WatchersCount == nil { - return 0 - } - return *r.WatchersCount -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetBody() string { - if r == nil || r.Body == nil { - return "" - } - return *r.Body -} - -// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetCommitID() string { - if r == nil || r.CommitID == nil { - return "" - } - return *r.CommitID -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetCreatedAt() time.Time { - if r == nil || r.CreatedAt == nil { - return time.Time{} - } - return *r.CreatedAt -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { - return "" - } - return *r.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetID() int64 { - if r == nil || r.ID == nil { - return 0 - } - return *r.ID -} - -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetPath() string { - if r == nil || r.Path == nil { - return "" - } - return *r.Path -} - -// GetPosition returns the Position field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetPosition() int { - if r == nil || r.Position == nil { - return 0 - } - return *r.Position -} - -// GetReactions returns the Reactions field. -func (r *RepositoryComment) GetReactions() *Reactions { - if r == nil { - return nil - } - return r.Reactions -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetUpdatedAt() time.Time { - if r == nil || r.UpdatedAt == nil { - return time.Time{} - } - return *r.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepositoryComment) GetURL() string { - if r == nil || r.URL == nil { - return "" - } - return *r.URL -} - -// GetUser returns the User field. -func (r *RepositoryComment) GetUser() *User { - if r == nil { - return nil - } - return r.User -} - -// GetAuthor returns the Author field. -func (r *RepositoryCommit) GetAuthor() *User { - if r == nil { - return nil - } - return r.Author -} - -// GetCommentsURL returns the CommentsURL field if it's non-nil, zero value otherwise. -func (r *RepositoryCommit) GetCommentsURL() string { - if r == nil || r.CommentsURL == nil { - return "" - } - return *r.CommentsURL -} - -// GetCommit returns the Commit field. -func (r *RepositoryCommit) GetCommit() *Commit { - if r == nil { - return nil - } - return r.Commit -} - -// GetCommitter returns the Committer field. -func (r *RepositoryCommit) GetCommitter() *User { - if r == nil { - return nil - } - return r.Committer -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *RepositoryCommit) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { - return "" - } - return *r.HTMLURL -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (r *RepositoryCommit) GetSHA() string { - if r == nil || r.SHA == nil { - return "" - } - return *r.SHA -} - -// GetStats returns the Stats field. -func (r *RepositoryCommit) GetStats() *CommitStats { - if r == nil { - return nil - } - return r.Stats -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepositoryCommit) GetURL() string { - if r == nil || r.URL == nil { - return "" - } - return *r.URL -} - -// GetDownloadURL returns the DownloadURL field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetDownloadURL() string { - if r == nil || r.DownloadURL == nil { - return "" - } - return *r.DownloadURL -} - -// GetEncoding returns the Encoding field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetEncoding() string { - if r == nil || r.Encoding == nil { - return "" - } - return *r.Encoding -} - -// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetGitURL() string { - if r == nil || r.GitURL == nil { - return "" - } - return *r.GitURL -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { - return "" - } - return *r.HTMLURL -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetName() string { - if r == nil || r.Name == nil { - return "" - } - return *r.Name -} - -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetPath() string { - if r == nil || r.Path == nil { - return "" - } - return *r.Path -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetSHA() string { - if r == nil || r.SHA == nil { - return "" - } - return *r.SHA -} - -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetSize() int { - if r == nil || r.Size == nil { - return 0 - } - return *r.Size -} - -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetType() string { - if r == nil || r.Type == nil { - return "" - } - return *r.Type -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepositoryContent) GetURL() string { - if r == nil || r.URL == nil { - return "" - } - return *r.URL -} - -// GetAuthor returns the Author field. -func (r *RepositoryContentFileOptions) GetAuthor() *CommitAuthor { - if r == nil { - return nil - } - return r.Author -} - -// GetBranch returns the Branch field if it's non-nil, zero value otherwise. -func (r *RepositoryContentFileOptions) GetBranch() string { - if r == nil || r.Branch == nil { - return "" - } - return *r.Branch -} - -// GetCommitter returns the Committer field. -func (r *RepositoryContentFileOptions) GetCommitter() *CommitAuthor { - if r == nil { - return nil - } - return r.Committer -} - -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (r *RepositoryContentFileOptions) GetMessage() string { - if r == nil || r.Message == nil { - return "" - } - return *r.Message -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (r *RepositoryContentFileOptions) GetSHA() string { - if r == nil || r.SHA == nil { - return "" - } - return *r.SHA -} - -// GetContent returns the Content field. -func (r *RepositoryContentResponse) GetContent() *RepositoryContent { - if r == nil { - return nil - } - return r.Content -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (r *RepositoryEvent) GetAction() string { - if r == nil || r.Action == nil { - return "" - } - return *r.Action -} - -// GetInstallation returns the Installation field. -func (r *RepositoryEvent) GetInstallation() *Installation { - if r == nil { - return nil - } - return r.Installation -} - -// GetOrg returns the Org field. -func (r *RepositoryEvent) GetOrg() *Organization { - if r == nil { - return nil - } - return r.Org -} - -// GetRepo returns the Repo field. -func (r *RepositoryEvent) GetRepo() *Repository { - if r == nil { - return nil - } - return r.Repo -} - -// GetSender returns the Sender field. -func (r *RepositoryEvent) GetSender() *User { - if r == nil { - return nil - } - return r.Sender -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *RepositoryInvitation) GetCreatedAt() Timestamp { - if r == nil || r.CreatedAt == nil { - return Timestamp{} - } - return *r.CreatedAt -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *RepositoryInvitation) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { - return "" - } - return *r.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *RepositoryInvitation) GetID() int64 { - if r == nil || r.ID == nil { - return 0 - } - return *r.ID -} - -// GetInvitee returns the Invitee field. -func (r *RepositoryInvitation) GetInvitee() *User { - if r == nil { - return nil - } - return r.Invitee -} - -// GetInviter returns the Inviter field. -func (r *RepositoryInvitation) GetInviter() *User { - if r == nil { - return nil - } - return r.Inviter -} - -// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. -func (r *RepositoryInvitation) GetPermissions() string { - if r == nil || r.Permissions == nil { - return "" - } - return *r.Permissions -} - -// GetRepo returns the Repo field. -func (r *RepositoryInvitation) GetRepo() *Repository { - if r == nil { - return nil - } - return r.Repo -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepositoryInvitation) GetURL() string { - if r == nil || r.URL == nil { - return "" - } - return *r.URL -} - -// GetContent returns the Content field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetContent() string { - if r == nil || r.Content == nil { - return "" - } - return *r.Content -} - -// GetDownloadURL returns the DownloadURL field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetDownloadURL() string { - if r == nil || r.DownloadURL == nil { - return "" - } - return *r.DownloadURL -} - -// GetEncoding returns the Encoding field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetEncoding() string { - if r == nil || r.Encoding == nil { - return "" - } - return *r.Encoding -} - -// GetGitURL returns the GitURL field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetGitURL() string { - if r == nil || r.GitURL == nil { - return "" - } - return *r.GitURL -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { - return "" - } - return *r.HTMLURL -} - -// GetLicense returns the License field. -func (r *RepositoryLicense) GetLicense() *License { - if r == nil { - return nil - } - return r.License -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetName() string { - if r == nil || r.Name == nil { - return "" - } - return *r.Name -} - -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetPath() string { - if r == nil || r.Path == nil { - return "" - } - return *r.Path -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetSHA() string { - if r == nil || r.SHA == nil { - return "" - } - return *r.SHA -} - -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetSize() int { - if r == nil || r.Size == nil { - return 0 - } - return *r.Size -} - -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetType() string { - if r == nil || r.Type == nil { - return "" - } - return *r.Type -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepositoryLicense) GetURL() string { - if r == nil || r.URL == nil { - return "" - } - return *r.URL -} - -// GetBase returns the Base field if it's non-nil, zero value otherwise. -func (r *RepositoryMergeRequest) GetBase() string { - if r == nil || r.Base == nil { - return "" - } - return *r.Base -} - -// GetCommitMessage returns the CommitMessage field if it's non-nil, zero value otherwise. -func (r *RepositoryMergeRequest) GetCommitMessage() string { - if r == nil || r.CommitMessage == nil { - return "" - } - return *r.CommitMessage -} - -// GetHead returns the Head field if it's non-nil, zero value otherwise. -func (r *RepositoryMergeRequest) GetHead() string { - if r == nil || r.Head == nil { - return "" - } - return *r.Head -} - -// GetPermission returns the Permission field if it's non-nil, zero value otherwise. -func (r *RepositoryPermissionLevel) GetPermission() string { - if r == nil || r.Permission == nil { - return "" - } - return *r.Permission -} - -// GetUser returns the User field. -func (r *RepositoryPermissionLevel) GetUser() *User { - if r == nil { - return nil - } - return r.User -} - -// GetAssetsURL returns the AssetsURL field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetAssetsURL() string { - if r == nil || r.AssetsURL == nil { - return "" - } - return *r.AssetsURL -} - -// GetAuthor returns the Author field. -func (r *RepositoryRelease) GetAuthor() *User { - if r == nil { - return nil - } - return r.Author -} - -// GetBody returns the Body field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetBody() string { - if r == nil || r.Body == nil { - return "" - } - return *r.Body -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetCreatedAt() Timestamp { - if r == nil || r.CreatedAt == nil { - return Timestamp{} - } - return *r.CreatedAt -} - -// GetDraft returns the Draft field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetDraft() bool { - if r == nil || r.Draft == nil { - return false - } - return *r.Draft -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetHTMLURL() string { - if r == nil || r.HTMLURL == nil { - return "" - } - return *r.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetID() int64 { - if r == nil || r.ID == nil { - return 0 - } - return *r.ID -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetName() string { - if r == nil || r.Name == nil { - return "" - } - return *r.Name -} - -// GetPrerelease returns the Prerelease field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetPrerelease() bool { - if r == nil || r.Prerelease == nil { - return false - } - return *r.Prerelease -} - -// GetPublishedAt returns the PublishedAt field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetPublishedAt() Timestamp { - if r == nil || r.PublishedAt == nil { - return Timestamp{} - } - return *r.PublishedAt -} - -// GetTagName returns the TagName field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetTagName() string { - if r == nil || r.TagName == nil { - return "" - } - return *r.TagName -} - -// GetTarballURL returns the TarballURL field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetTarballURL() string { - if r == nil || r.TarballURL == nil { - return "" - } - return *r.TarballURL -} - -// GetTargetCommitish returns the TargetCommitish field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetTargetCommitish() string { - if r == nil || r.TargetCommitish == nil { - return "" - } - return *r.TargetCommitish -} - -// GetUploadURL returns the UploadURL field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetUploadURL() string { - if r == nil || r.UploadURL == nil { - return "" - } - return *r.UploadURL -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetURL() string { - if r == nil || r.URL == nil { - return "" - } - return *r.URL -} - -// GetZipballURL returns the ZipballURL field if it's non-nil, zero value otherwise. -func (r *RepositoryRelease) GetZipballURL() string { - if r == nil || r.ZipballURL == nil { - return "" - } - return *r.ZipballURL -} - -// GetCommit returns the Commit field. -func (r *RepositoryTag) GetCommit() *Commit { - if r == nil { - return nil - } - return r.Commit -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (r *RepositoryTag) GetName() string { - if r == nil || r.Name == nil { - return "" - } - return *r.Name -} - -// GetTarballURL returns the TarballURL field if it's non-nil, zero value otherwise. -func (r *RepositoryTag) GetTarballURL() string { - if r == nil || r.TarballURL == nil { - return "" - } - return *r.TarballURL -} - -// GetZipballURL returns the ZipballURL field if it's non-nil, zero value otherwise. -func (r *RepositoryTag) GetZipballURL() string { - if r == nil || r.ZipballURL == nil { - return "" - } - return *r.ZipballURL -} - -// GetForkRepos returns the ForkRepos field if it's non-nil, zero value otherwise. -func (r *RepoStats) GetForkRepos() int { - if r == nil || r.ForkRepos == nil { - return 0 - } - return *r.ForkRepos -} - -// GetOrgRepos returns the OrgRepos field if it's non-nil, zero value otherwise. -func (r *RepoStats) GetOrgRepos() int { - if r == nil || r.OrgRepos == nil { - return 0 - } - return *r.OrgRepos -} - -// GetRootRepos returns the RootRepos field if it's non-nil, zero value otherwise. -func (r *RepoStats) GetRootRepos() int { - if r == nil || r.RootRepos == nil { - return 0 - } - return *r.RootRepos -} - -// GetTotalPushes returns the TotalPushes field if it's non-nil, zero value otherwise. -func (r *RepoStats) GetTotalPushes() int { - if r == nil || r.TotalPushes == nil { - return 0 - } - return *r.TotalPushes -} - -// GetTotalRepos returns the TotalRepos field if it's non-nil, zero value otherwise. -func (r *RepoStats) GetTotalRepos() int { - if r == nil || r.TotalRepos == nil { - return 0 - } - return *r.TotalRepos -} - -// GetTotalWikis returns the TotalWikis field if it's non-nil, zero value otherwise. -func (r *RepoStats) GetTotalWikis() int { - if r == nil || r.TotalWikis == nil { - return 0 - } - return *r.TotalWikis -} - -// GetContext returns the Context field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetContext() string { - if r == nil || r.Context == nil { - return "" - } - return *r.Context -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetCreatedAt() time.Time { - if r == nil || r.CreatedAt == nil { - return time.Time{} - } - return *r.CreatedAt -} - -// GetCreator returns the Creator field. -func (r *RepoStatus) GetCreator() *User { - if r == nil { - return nil - } - return r.Creator -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetDescription() string { - if r == nil || r.Description == nil { - return "" - } - return *r.Description -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetID() int64 { - if r == nil || r.ID == nil { - return 0 - } - return *r.ID -} - -// GetState returns the State field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetState() string { - if r == nil || r.State == nil { - return "" - } - return *r.State -} - -// GetTargetURL returns the TargetURL field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetTargetURL() string { - if r == nil || r.TargetURL == nil { - return "" - } - return *r.TargetURL -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetUpdatedAt() time.Time { - if r == nil || r.UpdatedAt == nil { - return time.Time{} - } - return *r.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (r *RepoStatus) GetURL() string { - if r == nil || r.URL == nil { - return "" - } - return *r.URL -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (s *ServiceHook) GetName() string { - if s == nil || s.Name == nil { - return "" - } - return *s.Name -} - -// GetPayload returns the Payload field if it's non-nil, zero value otherwise. -func (s *SignatureVerification) GetPayload() string { - if s == nil || s.Payload == nil { - return "" - } - return *s.Payload -} - -// GetReason returns the Reason field if it's non-nil, zero value otherwise. -func (s *SignatureVerification) GetReason() string { - if s == nil || s.Reason == nil { - return "" - } - return *s.Reason -} - -// GetSignature returns the Signature field if it's non-nil, zero value otherwise. -func (s *SignatureVerification) GetSignature() string { - if s == nil || s.Signature == nil { - return "" - } - return *s.Signature -} - -// GetVerified returns the Verified field if it's non-nil, zero value otherwise. -func (s *SignatureVerification) GetVerified() bool { - if s == nil || s.Verified == nil { - return false - } - return *s.Verified -} - -// GetActor returns the Actor field. -func (s *Source) GetActor() *User { - if s == nil { - return nil - } - return s.Actor -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (s *Source) GetID() int64 { - if s == nil || s.ID == nil { - return 0 - } - return *s.ID -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (s *Source) GetURL() string { - if s == nil || s.URL == nil { - return "" - } - return *s.URL -} - -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetEmail() string { - if s == nil || s.Email == nil { - return "" - } - return *s.Email -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetID() int64 { - if s == nil || s.ID == nil { - return 0 - } - return *s.ID -} - -// GetImportURL returns the ImportURL field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetImportURL() string { - if s == nil || s.ImportURL == nil { - return "" - } - return *s.ImportURL -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetName() string { - if s == nil || s.Name == nil { - return "" - } - return *s.Name -} - -// GetRemoteID returns the RemoteID field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetRemoteID() string { - if s == nil || s.RemoteID == nil { - return "" - } - return *s.RemoteID -} - -// GetRemoteName returns the RemoteName field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetRemoteName() string { - if s == nil || s.RemoteName == nil { - return "" - } - return *s.RemoteName -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (s *SourceImportAuthor) GetURL() string { - if s == nil || s.URL == nil { - return "" - } - return *s.URL -} - -// GetStarredAt returns the StarredAt field if it's non-nil, zero value otherwise. -func (s *Stargazer) GetStarredAt() Timestamp { - if s == nil || s.StarredAt == nil { - return Timestamp{} - } - return *s.StarredAt -} - -// GetUser returns the User field. -func (s *Stargazer) GetUser() *User { - if s == nil { - return nil - } - return s.User -} - -// GetRepository returns the Repository field. -func (s *StarredRepository) GetRepository() *Repository { - if s == nil { - return nil - } - return s.Repository -} - -// GetStarredAt returns the StarredAt field if it's non-nil, zero value otherwise. -func (s *StarredRepository) GetStarredAt() Timestamp { - if s == nil || s.StarredAt == nil { - return Timestamp{} - } - return *s.StarredAt -} - -// GetCommit returns the Commit field. -func (s *StatusEvent) GetCommit() *RepositoryCommit { - if s == nil { - return nil - } - return s.Commit -} - -// GetContext returns the Context field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetContext() string { - if s == nil || s.Context == nil { - return "" - } - return *s.Context -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetCreatedAt() Timestamp { - if s == nil || s.CreatedAt == nil { - return Timestamp{} - } - return *s.CreatedAt -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetDescription() string { - if s == nil || s.Description == nil { - return "" - } - return *s.Description -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetID() int64 { - if s == nil || s.ID == nil { - return 0 - } - return *s.ID -} - -// GetInstallation returns the Installation field. -func (s *StatusEvent) GetInstallation() *Installation { - if s == nil { - return nil - } - return s.Installation -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetName() string { - if s == nil || s.Name == nil { - return "" - } - return *s.Name -} - -// GetRepo returns the Repo field. -func (s *StatusEvent) GetRepo() *Repository { - if s == nil { - return nil - } - return s.Repo -} - -// GetSender returns the Sender field. -func (s *StatusEvent) GetSender() *User { - if s == nil { - return nil - } - return s.Sender -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetSHA() string { - if s == nil || s.SHA == nil { - return "" - } - return *s.SHA -} - -// GetState returns the State field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetState() string { - if s == nil || s.State == nil { - return "" - } - return *s.State -} - -// GetTargetURL returns the TargetURL field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetTargetURL() string { - if s == nil || s.TargetURL == nil { - return "" - } - return *s.TargetURL -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (s *StatusEvent) GetUpdatedAt() Timestamp { - if s == nil || s.UpdatedAt == nil { - return Timestamp{} - } - return *s.UpdatedAt -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (s *Subscription) GetCreatedAt() Timestamp { - if s == nil || s.CreatedAt == nil { - return Timestamp{} - } - return *s.CreatedAt -} - -// GetIgnored returns the Ignored field if it's non-nil, zero value otherwise. -func (s *Subscription) GetIgnored() bool { - if s == nil || s.Ignored == nil { - return false - } - return *s.Ignored -} - -// GetReason returns the Reason field if it's non-nil, zero value otherwise. -func (s *Subscription) GetReason() string { - if s == nil || s.Reason == nil { - return "" - } - return *s.Reason -} - -// GetRepositoryURL returns the RepositoryURL field if it's non-nil, zero value otherwise. -func (s *Subscription) GetRepositoryURL() string { - if s == nil || s.RepositoryURL == nil { - return "" - } - return *s.RepositoryURL -} - -// GetSubscribed returns the Subscribed field if it's non-nil, zero value otherwise. -func (s *Subscription) GetSubscribed() bool { - if s == nil || s.Subscribed == nil { - return false - } - return *s.Subscribed -} - -// GetThreadURL returns the ThreadURL field if it's non-nil, zero value otherwise. -func (s *Subscription) GetThreadURL() string { - if s == nil || s.ThreadURL == nil { - return "" - } - return *s.ThreadURL -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (s *Subscription) GetURL() string { - if s == nil || s.URL == nil { - return "" - } - return *s.URL -} - -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (t *Tag) GetMessage() string { - if t == nil || t.Message == nil { - return "" - } - return *t.Message -} - -// GetNodeID returns the NodeID field if it's non-nil, zero value otherwise. -func (t *Tag) GetNodeID() string { - if t == nil || t.NodeID == nil { - return "" - } - return *t.NodeID -} - -// GetObject returns the Object field. -func (t *Tag) GetObject() *GitObject { - if t == nil { - return nil - } - return t.Object -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (t *Tag) GetSHA() string { - if t == nil || t.SHA == nil { - return "" - } - return *t.SHA -} - -// GetTag returns the Tag field if it's non-nil, zero value otherwise. -func (t *Tag) GetTag() string { - if t == nil || t.Tag == nil { - return "" - } - return *t.Tag -} - -// GetTagger returns the Tagger field. -func (t *Tag) GetTagger() *CommitAuthor { - if t == nil { - return nil - } - return t.Tagger -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (t *Tag) GetURL() string { - if t == nil || t.URL == nil { - return "" - } - return *t.URL -} - -// GetVerification returns the Verification field. -func (t *Tag) GetVerification() *SignatureVerification { - if t == nil { - return nil - } - return t.Verification -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (t *Team) GetDescription() string { - if t == nil || t.Description == nil { - return "" - } - return *t.Description -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (t *Team) GetID() int64 { - if t == nil || t.ID == nil { - return 0 - } - return *t.ID -} - -// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. -func (t *Team) GetLDAPDN() string { - if t == nil || t.LDAPDN == nil { - return "" - } - return *t.LDAPDN -} - -// GetMembersCount returns the MembersCount field if it's non-nil, zero value otherwise. -func (t *Team) GetMembersCount() int { - if t == nil || t.MembersCount == nil { - return 0 - } - return *t.MembersCount -} - -// GetMembersURL returns the MembersURL field if it's non-nil, zero value otherwise. -func (t *Team) GetMembersURL() string { - if t == nil || t.MembersURL == nil { - return "" - } - return *t.MembersURL -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (t *Team) GetName() string { - if t == nil || t.Name == nil { - return "" - } - return *t.Name -} - -// GetOrganization returns the Organization field. -func (t *Team) GetOrganization() *Organization { - if t == nil { - return nil - } - return t.Organization -} - -// GetParent returns the Parent field. -func (t *Team) GetParent() *Team { - if t == nil { - return nil - } - return t.Parent -} - -// GetPermission returns the Permission field if it's non-nil, zero value otherwise. -func (t *Team) GetPermission() string { - if t == nil || t.Permission == nil { - return "" - } - return *t.Permission -} - -// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. -func (t *Team) GetPrivacy() string { - if t == nil || t.Privacy == nil { - return "" - } - return *t.Privacy -} - -// GetReposCount returns the ReposCount field if it's non-nil, zero value otherwise. -func (t *Team) GetReposCount() int { - if t == nil || t.ReposCount == nil { - return 0 - } - return *t.ReposCount -} - -// GetRepositoriesURL returns the RepositoriesURL field if it's non-nil, zero value otherwise. -func (t *Team) GetRepositoriesURL() string { - if t == nil || t.RepositoriesURL == nil { - return "" - } - return *t.RepositoriesURL -} - -// GetSlug returns the Slug field if it's non-nil, zero value otherwise. -func (t *Team) GetSlug() string { - if t == nil || t.Slug == nil { - return "" - } - return *t.Slug -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (t *Team) GetURL() string { - if t == nil || t.URL == nil { - return "" - } - return *t.URL -} - -// GetInstallation returns the Installation field. -func (t *TeamAddEvent) GetInstallation() *Installation { - if t == nil { - return nil - } - return t.Installation -} - -// GetOrg returns the Org field. -func (t *TeamAddEvent) GetOrg() *Organization { - if t == nil { - return nil - } - return t.Org -} - -// GetRepo returns the Repo field. -func (t *TeamAddEvent) GetRepo() *Repository { - if t == nil { - return nil - } - return t.Repo -} - -// GetSender returns the Sender field. -func (t *TeamAddEvent) GetSender() *User { - if t == nil { - return nil - } - return t.Sender -} - -// GetTeam returns the Team field. -func (t *TeamAddEvent) GetTeam() *Team { - if t == nil { - return nil - } - return t.Team -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (t *TeamEvent) GetAction() string { - if t == nil || t.Action == nil { - return "" - } - return *t.Action -} - -// GetChanges returns the Changes field. -func (t *TeamEvent) GetChanges() *TeamChange { - if t == nil { - return nil - } - return t.Changes -} - -// GetInstallation returns the Installation field. -func (t *TeamEvent) GetInstallation() *Installation { - if t == nil { - return nil - } - return t.Installation -} - -// GetOrg returns the Org field. -func (t *TeamEvent) GetOrg() *Organization { - if t == nil { - return nil - } - return t.Org -} - -// GetRepo returns the Repo field. -func (t *TeamEvent) GetRepo() *Repository { - if t == nil { - return nil - } - return t.Repo -} - -// GetSender returns the Sender field. -func (t *TeamEvent) GetSender() *User { - if t == nil { - return nil - } - return t.Sender -} - -// GetTeam returns the Team field. -func (t *TeamEvent) GetTeam() *Team { - if t == nil { - return nil - } - return t.Team -} - -// GetDescription returns the Description field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetDescription() string { - if t == nil || t.Description == nil { - return "" - } - return *t.Description -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetID() int64 { - if t == nil || t.ID == nil { - return 0 - } - return *t.ID -} - -// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetLDAPDN() string { - if t == nil || t.LDAPDN == nil { - return "" - } - return *t.LDAPDN -} - -// GetMembersURL returns the MembersURL field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetMembersURL() string { - if t == nil || t.MembersURL == nil { - return "" - } - return *t.MembersURL -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetName() string { - if t == nil || t.Name == nil { - return "" - } - return *t.Name -} - -// GetPermission returns the Permission field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetPermission() string { - if t == nil || t.Permission == nil { - return "" - } - return *t.Permission -} - -// GetPrivacy returns the Privacy field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetPrivacy() string { - if t == nil || t.Privacy == nil { - return "" - } - return *t.Privacy -} - -// GetRepositoriesURL returns the RepositoriesURL field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetRepositoriesURL() string { - if t == nil || t.RepositoriesURL == nil { - return "" - } - return *t.RepositoriesURL -} - -// GetSlug returns the Slug field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetSlug() string { - if t == nil || t.Slug == nil { - return "" - } - return *t.Slug -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (t *TeamLDAPMapping) GetURL() string { - if t == nil || t.URL == nil { - return "" - } - return *t.URL -} - -// GetFragment returns the Fragment field if it's non-nil, zero value otherwise. -func (t *TextMatch) GetFragment() string { - if t == nil || t.Fragment == nil { - return "" - } - return *t.Fragment -} - -// GetObjectType returns the ObjectType field if it's non-nil, zero value otherwise. -func (t *TextMatch) GetObjectType() string { - if t == nil || t.ObjectType == nil { - return "" - } - return *t.ObjectType -} - -// GetObjectURL returns the ObjectURL field if it's non-nil, zero value otherwise. -func (t *TextMatch) GetObjectURL() string { - if t == nil || t.ObjectURL == nil { - return "" - } - return *t.ObjectURL -} - -// GetProperty returns the Property field if it's non-nil, zero value otherwise. -func (t *TextMatch) GetProperty() string { - if t == nil || t.Property == nil { - return "" - } - return *t.Property -} - -// GetActor returns the Actor field. -func (t *Timeline) GetActor() *User { - if t == nil { - return nil - } - return t.Actor -} - -// GetAssignee returns the Assignee field. -func (t *Timeline) GetAssignee() *User { - if t == nil { - return nil - } - return t.Assignee -} - -// GetCommitID returns the CommitID field if it's non-nil, zero value otherwise. -func (t *Timeline) GetCommitID() string { - if t == nil || t.CommitID == nil { - return "" - } - return *t.CommitID -} - -// GetCommitURL returns the CommitURL field if it's non-nil, zero value otherwise. -func (t *Timeline) GetCommitURL() string { - if t == nil || t.CommitURL == nil { - return "" - } - return *t.CommitURL -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (t *Timeline) GetCreatedAt() time.Time { - if t == nil || t.CreatedAt == nil { - return time.Time{} - } - return *t.CreatedAt -} - -// GetEvent returns the Event field if it's non-nil, zero value otherwise. -func (t *Timeline) GetEvent() string { - if t == nil || t.Event == nil { - return "" - } - return *t.Event -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (t *Timeline) GetID() int64 { - if t == nil || t.ID == nil { - return 0 - } - return *t.ID -} - -// GetLabel returns the Label field. -func (t *Timeline) GetLabel() *Label { - if t == nil { - return nil - } - return t.Label -} - -// GetMilestone returns the Milestone field. -func (t *Timeline) GetMilestone() *Milestone { - if t == nil { - return nil - } - return t.Milestone -} - -// GetRename returns the Rename field. -func (t *Timeline) GetRename() *Rename { - if t == nil { - return nil - } - return t.Rename -} - -// GetSource returns the Source field. -func (t *Timeline) GetSource() *Source { - if t == nil { - return nil - } - return t.Source -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (t *Timeline) GetURL() string { - if t == nil || t.URL == nil { - return "" - } - return *t.URL -} - -// GetCount returns the Count field if it's non-nil, zero value otherwise. -func (t *TrafficClones) GetCount() int { - if t == nil || t.Count == nil { - return 0 - } - return *t.Count -} - -// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. -func (t *TrafficClones) GetUniques() int { - if t == nil || t.Uniques == nil { - return 0 - } - return *t.Uniques -} - -// GetCount returns the Count field if it's non-nil, zero value otherwise. -func (t *TrafficData) GetCount() int { - if t == nil || t.Count == nil { - return 0 - } - return *t.Count -} - -// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. -func (t *TrafficData) GetTimestamp() Timestamp { - if t == nil || t.Timestamp == nil { - return Timestamp{} - } - return *t.Timestamp -} - -// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. -func (t *TrafficData) GetUniques() int { - if t == nil || t.Uniques == nil { - return 0 - } - return *t.Uniques -} - -// GetCount returns the Count field if it's non-nil, zero value otherwise. -func (t *TrafficPath) GetCount() int { - if t == nil || t.Count == nil { - return 0 - } - return *t.Count -} - -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (t *TrafficPath) GetPath() string { - if t == nil || t.Path == nil { - return "" - } - return *t.Path -} - -// GetTitle returns the Title field if it's non-nil, zero value otherwise. -func (t *TrafficPath) GetTitle() string { - if t == nil || t.Title == nil { - return "" - } - return *t.Title -} - -// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. -func (t *TrafficPath) GetUniques() int { - if t == nil || t.Uniques == nil { - return 0 - } - return *t.Uniques -} - -// GetCount returns the Count field if it's non-nil, zero value otherwise. -func (t *TrafficReferrer) GetCount() int { - if t == nil || t.Count == nil { - return 0 - } - return *t.Count -} - -// GetReferrer returns the Referrer field if it's non-nil, zero value otherwise. -func (t *TrafficReferrer) GetReferrer() string { - if t == nil || t.Referrer == nil { - return "" - } - return *t.Referrer -} - -// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. -func (t *TrafficReferrer) GetUniques() int { - if t == nil || t.Uniques == nil { - return 0 - } - return *t.Uniques -} - -// GetCount returns the Count field if it's non-nil, zero value otherwise. -func (t *TrafficViews) GetCount() int { - if t == nil || t.Count == nil { - return 0 - } - return *t.Count -} - -// GetUniques returns the Uniques field if it's non-nil, zero value otherwise. -func (t *TrafficViews) GetUniques() int { - if t == nil || t.Uniques == nil { - return 0 - } - return *t.Uniques -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (t *Tree) GetSHA() string { - if t == nil || t.SHA == nil { - return "" - } - return *t.SHA -} - -// GetContent returns the Content field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetContent() string { - if t == nil || t.Content == nil { - return "" - } - return *t.Content -} - -// GetMode returns the Mode field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetMode() string { - if t == nil || t.Mode == nil { - return "" - } - return *t.Mode -} - -// GetPath returns the Path field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetPath() string { - if t == nil || t.Path == nil { - return "" - } - return *t.Path -} - -// GetSHA returns the SHA field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetSHA() string { - if t == nil || t.SHA == nil { - return "" - } - return *t.SHA -} - -// GetSize returns the Size field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetSize() int { - if t == nil || t.Size == nil { - return 0 - } - return *t.Size -} - -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetType() string { - if t == nil || t.Type == nil { - return "" - } - return *t.Type -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (t *TreeEntry) GetURL() string { - if t == nil || t.URL == nil { - return "" - } - return *t.URL -} - -// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. -func (u *User) GetAvatarURL() string { - if u == nil || u.AvatarURL == nil { - return "" - } - return *u.AvatarURL -} - -// GetBio returns the Bio field if it's non-nil, zero value otherwise. -func (u *User) GetBio() string { - if u == nil || u.Bio == nil { - return "" - } - return *u.Bio -} - -// GetBlog returns the Blog field if it's non-nil, zero value otherwise. -func (u *User) GetBlog() string { - if u == nil || u.Blog == nil { - return "" - } - return *u.Blog -} - -// GetCollaborators returns the Collaborators field if it's non-nil, zero value otherwise. -func (u *User) GetCollaborators() int { - if u == nil || u.Collaborators == nil { - return 0 - } - return *u.Collaborators -} - -// GetCompany returns the Company field if it's non-nil, zero value otherwise. -func (u *User) GetCompany() string { - if u == nil || u.Company == nil { - return "" - } - return *u.Company -} - -// GetCreatedAt returns the CreatedAt field if it's non-nil, zero value otherwise. -func (u *User) GetCreatedAt() Timestamp { - if u == nil || u.CreatedAt == nil { - return Timestamp{} - } - return *u.CreatedAt -} - -// GetDiskUsage returns the DiskUsage field if it's non-nil, zero value otherwise. -func (u *User) GetDiskUsage() int { - if u == nil || u.DiskUsage == nil { - return 0 - } - return *u.DiskUsage -} - -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (u *User) GetEmail() string { - if u == nil || u.Email == nil { - return "" - } - return *u.Email -} - -// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. -func (u *User) GetEventsURL() string { - if u == nil || u.EventsURL == nil { - return "" - } - return *u.EventsURL -} - -// GetFollowers returns the Followers field if it's non-nil, zero value otherwise. -func (u *User) GetFollowers() int { - if u == nil || u.Followers == nil { - return 0 - } - return *u.Followers -} - -// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. -func (u *User) GetFollowersURL() string { - if u == nil || u.FollowersURL == nil { - return "" - } - return *u.FollowersURL -} - -// GetFollowing returns the Following field if it's non-nil, zero value otherwise. -func (u *User) GetFollowing() int { - if u == nil || u.Following == nil { - return 0 - } - return *u.Following -} - -// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. -func (u *User) GetFollowingURL() string { - if u == nil || u.FollowingURL == nil { - return "" - } - return *u.FollowingURL -} - -// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. -func (u *User) GetGistsURL() string { - if u == nil || u.GistsURL == nil { - return "" - } - return *u.GistsURL -} - -// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. -func (u *User) GetGravatarID() string { - if u == nil || u.GravatarID == nil { - return "" - } - return *u.GravatarID -} - -// GetHireable returns the Hireable field if it's non-nil, zero value otherwise. -func (u *User) GetHireable() bool { - if u == nil || u.Hireable == nil { - return false - } - return *u.Hireable -} - -// GetHTMLURL returns the HTMLURL field if it's non-nil, zero value otherwise. -func (u *User) GetHTMLURL() string { - if u == nil || u.HTMLURL == nil { - return "" - } - return *u.HTMLURL -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (u *User) GetID() int64 { - if u == nil || u.ID == nil { - return 0 - } - return *u.ID -} - -// GetLocation returns the Location field if it's non-nil, zero value otherwise. -func (u *User) GetLocation() string { - if u == nil || u.Location == nil { - return "" - } - return *u.Location -} - -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (u *User) GetLogin() string { - if u == nil || u.Login == nil { - return "" - } - return *u.Login -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (u *User) GetName() string { - if u == nil || u.Name == nil { - return "" - } - return *u.Name -} - -// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. -func (u *User) GetOrganizationsURL() string { - if u == nil || u.OrganizationsURL == nil { - return "" - } - return *u.OrganizationsURL -} - -// GetOwnedPrivateRepos returns the OwnedPrivateRepos field if it's non-nil, zero value otherwise. -func (u *User) GetOwnedPrivateRepos() int { - if u == nil || u.OwnedPrivateRepos == nil { - return 0 - } - return *u.OwnedPrivateRepos -} - -// GetPermissions returns the Permissions field if it's non-nil, zero value otherwise. -func (u *User) GetPermissions() map[string]bool { - if u == nil || u.Permissions == nil { - return map[string]bool{} - } - return *u.Permissions -} - -// GetPlan returns the Plan field. -func (u *User) GetPlan() *Plan { - if u == nil { - return nil - } - return u.Plan -} - -// GetPrivateGists returns the PrivateGists field if it's non-nil, zero value otherwise. -func (u *User) GetPrivateGists() int { - if u == nil || u.PrivateGists == nil { - return 0 - } - return *u.PrivateGists -} - -// GetPublicGists returns the PublicGists field if it's non-nil, zero value otherwise. -func (u *User) GetPublicGists() int { - if u == nil || u.PublicGists == nil { - return 0 - } - return *u.PublicGists -} - -// GetPublicRepos returns the PublicRepos field if it's non-nil, zero value otherwise. -func (u *User) GetPublicRepos() int { - if u == nil || u.PublicRepos == nil { - return 0 - } - return *u.PublicRepos -} - -// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. -func (u *User) GetReceivedEventsURL() string { - if u == nil || u.ReceivedEventsURL == nil { - return "" - } - return *u.ReceivedEventsURL -} - -// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. -func (u *User) GetReposURL() string { - if u == nil || u.ReposURL == nil { - return "" - } - return *u.ReposURL -} - -// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. -func (u *User) GetSiteAdmin() bool { - if u == nil || u.SiteAdmin == nil { - return false - } - return *u.SiteAdmin -} - -// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. -func (u *User) GetStarredURL() string { - if u == nil || u.StarredURL == nil { - return "" - } - return *u.StarredURL -} - -// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. -func (u *User) GetSubscriptionsURL() string { - if u == nil || u.SubscriptionsURL == nil { - return "" - } - return *u.SubscriptionsURL -} - -// GetSuspendedAt returns the SuspendedAt field if it's non-nil, zero value otherwise. -func (u *User) GetSuspendedAt() Timestamp { - if u == nil || u.SuspendedAt == nil { - return Timestamp{} - } - return *u.SuspendedAt -} - -// GetTotalPrivateRepos returns the TotalPrivateRepos field if it's non-nil, zero value otherwise. -func (u *User) GetTotalPrivateRepos() int { - if u == nil || u.TotalPrivateRepos == nil { - return 0 - } - return *u.TotalPrivateRepos -} - -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (u *User) GetType() string { - if u == nil || u.Type == nil { - return "" - } - return *u.Type -} - -// GetUpdatedAt returns the UpdatedAt field if it's non-nil, zero value otherwise. -func (u *User) GetUpdatedAt() Timestamp { - if u == nil || u.UpdatedAt == nil { - return Timestamp{} - } - return *u.UpdatedAt -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (u *User) GetURL() string { - if u == nil || u.URL == nil { - return "" - } - return *u.URL -} - -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (u *UserEmail) GetEmail() string { - if u == nil || u.Email == nil { - return "" - } - return *u.Email -} - -// GetPrimary returns the Primary field if it's non-nil, zero value otherwise. -func (u *UserEmail) GetPrimary() bool { - if u == nil || u.Primary == nil { - return false - } - return *u.Primary -} - -// GetVerified returns the Verified field if it's non-nil, zero value otherwise. -func (u *UserEmail) GetVerified() bool { - if u == nil || u.Verified == nil { - return false - } - return *u.Verified -} - -// GetAvatarURL returns the AvatarURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetAvatarURL() string { - if u == nil || u.AvatarURL == nil { - return "" - } - return *u.AvatarURL -} - -// GetEventsURL returns the EventsURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetEventsURL() string { - if u == nil || u.EventsURL == nil { - return "" - } - return *u.EventsURL -} - -// GetFollowersURL returns the FollowersURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetFollowersURL() string { - if u == nil || u.FollowersURL == nil { - return "" - } - return *u.FollowersURL -} - -// GetFollowingURL returns the FollowingURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetFollowingURL() string { - if u == nil || u.FollowingURL == nil { - return "" - } - return *u.FollowingURL -} - -// GetGistsURL returns the GistsURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetGistsURL() string { - if u == nil || u.GistsURL == nil { - return "" - } - return *u.GistsURL -} - -// GetGravatarID returns the GravatarID field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetGravatarID() string { - if u == nil || u.GravatarID == nil { - return "" - } - return *u.GravatarID -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetID() int64 { - if u == nil || u.ID == nil { - return 0 - } - return *u.ID -} - -// GetLDAPDN returns the LDAPDN field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetLDAPDN() string { - if u == nil || u.LDAPDN == nil { - return "" - } - return *u.LDAPDN -} - -// GetLogin returns the Login field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetLogin() string { - if u == nil || u.Login == nil { - return "" - } - return *u.Login -} - -// GetOrganizationsURL returns the OrganizationsURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetOrganizationsURL() string { - if u == nil || u.OrganizationsURL == nil { - return "" - } - return *u.OrganizationsURL -} - -// GetReceivedEventsURL returns the ReceivedEventsURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetReceivedEventsURL() string { - if u == nil || u.ReceivedEventsURL == nil { - return "" - } - return *u.ReceivedEventsURL -} - -// GetReposURL returns the ReposURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetReposURL() string { - if u == nil || u.ReposURL == nil { - return "" - } - return *u.ReposURL -} - -// GetSiteAdmin returns the SiteAdmin field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetSiteAdmin() bool { - if u == nil || u.SiteAdmin == nil { - return false - } - return *u.SiteAdmin -} - -// GetStarredURL returns the StarredURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetStarredURL() string { - if u == nil || u.StarredURL == nil { - return "" - } - return *u.StarredURL -} - -// GetSubscriptionsURL returns the SubscriptionsURL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetSubscriptionsURL() string { - if u == nil || u.SubscriptionsURL == nil { - return "" - } - return *u.SubscriptionsURL -} - -// GetType returns the Type field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetType() string { - if u == nil || u.Type == nil { - return "" - } - return *u.Type -} - -// GetURL returns the URL field if it's non-nil, zero value otherwise. -func (u *UserLDAPMapping) GetURL() string { - if u == nil || u.URL == nil { - return "" - } - return *u.URL -} - -// GetIncompleteResults returns the IncompleteResults field if it's non-nil, zero value otherwise. -func (u *UsersSearchResult) GetIncompleteResults() bool { - if u == nil || u.IncompleteResults == nil { - return false - } - return *u.IncompleteResults -} - -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (u *UsersSearchResult) GetTotal() int { - if u == nil || u.Total == nil { - return 0 - } - return *u.Total -} - -// GetAdminUsers returns the AdminUsers field if it's non-nil, zero value otherwise. -func (u *UserStats) GetAdminUsers() int { - if u == nil || u.AdminUsers == nil { - return 0 - } - return *u.AdminUsers -} - -// GetSuspendedUsers returns the SuspendedUsers field if it's non-nil, zero value otherwise. -func (u *UserStats) GetSuspendedUsers() int { - if u == nil || u.SuspendedUsers == nil { - return 0 - } - return *u.SuspendedUsers -} - -// GetTotalUsers returns the TotalUsers field if it's non-nil, zero value otherwise. -func (u *UserStats) GetTotalUsers() int { - if u == nil || u.TotalUsers == nil { - return 0 - } - return *u.TotalUsers -} - -// GetAction returns the Action field if it's non-nil, zero value otherwise. -func (w *WatchEvent) GetAction() string { - if w == nil || w.Action == nil { - return "" - } - return *w.Action -} - -// GetInstallation returns the Installation field. -func (w *WatchEvent) GetInstallation() *Installation { - if w == nil { - return nil - } - return w.Installation -} - -// GetRepo returns the Repo field. -func (w *WatchEvent) GetRepo() *Repository { - if w == nil { - return nil - } - return w.Repo -} - -// GetSender returns the Sender field. -func (w *WatchEvent) GetSender() *User { - if w == nil { - return nil - } - return w.Sender -} - -// GetEmail returns the Email field if it's non-nil, zero value otherwise. -func (w *WebHookAuthor) GetEmail() string { - if w == nil || w.Email == nil { - return "" - } - return *w.Email -} - -// GetName returns the Name field if it's non-nil, zero value otherwise. -func (w *WebHookAuthor) GetName() string { - if w == nil || w.Name == nil { - return "" - } - return *w.Name -} - -// GetUsername returns the Username field if it's non-nil, zero value otherwise. -func (w *WebHookAuthor) GetUsername() string { - if w == nil || w.Username == nil { - return "" - } - return *w.Username -} - -// GetAuthor returns the Author field. -func (w *WebHookCommit) GetAuthor() *WebHookAuthor { - if w == nil { - return nil - } - return w.Author -} - -// GetCommitter returns the Committer field. -func (w *WebHookCommit) GetCommitter() *WebHookAuthor { - if w == nil { - return nil - } - return w.Committer -} - -// GetDistinct returns the Distinct field if it's non-nil, zero value otherwise. -func (w *WebHookCommit) GetDistinct() bool { - if w == nil || w.Distinct == nil { - return false - } - return *w.Distinct -} - -// GetID returns the ID field if it's non-nil, zero value otherwise. -func (w *WebHookCommit) GetID() string { - if w == nil || w.ID == nil { - return "" - } - return *w.ID -} - -// GetMessage returns the Message field if it's non-nil, zero value otherwise. -func (w *WebHookCommit) GetMessage() string { - if w == nil || w.Message == nil { - return "" - } - return *w.Message -} - -// GetTimestamp returns the Timestamp field if it's non-nil, zero value otherwise. -func (w *WebHookCommit) GetTimestamp() time.Time { - if w == nil || w.Timestamp == nil { - return time.Time{} - } - return *w.Timestamp -} - -// GetAfter returns the After field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetAfter() string { - if w == nil || w.After == nil { - return "" - } - return *w.After -} - -// GetBefore returns the Before field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetBefore() string { - if w == nil || w.Before == nil { - return "" - } - return *w.Before -} - -// GetCompare returns the Compare field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetCompare() string { - if w == nil || w.Compare == nil { - return "" - } - return *w.Compare -} - -// GetCreated returns the Created field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetCreated() bool { - if w == nil || w.Created == nil { - return false - } - return *w.Created -} - -// GetDeleted returns the Deleted field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetDeleted() bool { - if w == nil || w.Deleted == nil { - return false - } - return *w.Deleted -} - -// GetForced returns the Forced field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetForced() bool { - if w == nil || w.Forced == nil { - return false - } - return *w.Forced -} - -// GetHeadCommit returns the HeadCommit field. -func (w *WebHookPayload) GetHeadCommit() *WebHookCommit { - if w == nil { - return nil - } - return w.HeadCommit -} - -// GetPusher returns the Pusher field. -func (w *WebHookPayload) GetPusher() *User { - if w == nil { - return nil - } - return w.Pusher -} - -// GetRef returns the Ref field if it's non-nil, zero value otherwise. -func (w *WebHookPayload) GetRef() string { - if w == nil || w.Ref == nil { - return "" - } - return *w.Ref -} - -// GetRepo returns the Repo field. -func (w *WebHookPayload) GetRepo() *Repository { - if w == nil { - return nil - } - return w.Repo -} - -// GetSender returns the Sender field. -func (w *WebHookPayload) GetSender() *User { - if w == nil { - return nil - } - return w.Sender -} - -// GetTotal returns the Total field if it's non-nil, zero value otherwise. -func (w *WeeklyCommitActivity) GetTotal() int { - if w == nil || w.Total == nil { - return 0 - } - return *w.Total -} - -// GetWeek returns the Week field if it's non-nil, zero value otherwise. -func (w *WeeklyCommitActivity) GetWeek() Timestamp { - if w == nil || w.Week == nil { - return Timestamp{} - } - return *w.Week -} - -// GetAdditions returns the Additions field if it's non-nil, zero value otherwise. -func (w *WeeklyStats) GetAdditions() int { - if w == nil || w.Additions == nil { - return 0 - } - return *w.Additions -} - -// GetCommits returns the Commits field if it's non-nil, zero value otherwise. -func (w *WeeklyStats) GetCommits() int { - if w == nil || w.Commits == nil { - return 0 - } - return *w.Commits -} - -// GetDeletions returns the Deletions field if it's non-nil, zero value otherwise. -func (w *WeeklyStats) GetDeletions() int { - if w == nil || w.Deletions == nil { - return 0 - } - return *w.Deletions -} - -// GetWeek returns the Week field if it's non-nil, zero value otherwise. -func (w *WeeklyStats) GetWeek() Timestamp { - if w == nil || w.Week == nil { - return Timestamp{} - } - return *w.Week -} diff --git a/vendor/github.com/google/go-github/github/github.go b/vendor/github.com/google/go-github/github/github.go deleted file mode 100644 index 082472260b1..00000000000 --- a/vendor/github.com/google/go-github/github/github.go +++ /dev/null @@ -1,980 +0,0 @@ -// Copyright 2013 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -//go:generate go run gen-accessors.go - -package github - -import ( - "bytes" - "context" - "encoding/json" - "errors" - "fmt" - "io" - "io/ioutil" - "net/http" - "net/url" - "reflect" - "strconv" - "strings" - "sync" - "time" - - "github.com/google/go-querystring/query" -) - -const ( - libraryVersion = "15" - defaultBaseURL = "https://api.github.com/" - uploadBaseURL = "https://uploads.github.com/" - userAgent = "go-github/" + libraryVersion - - headerRateLimit = "X-RateLimit-Limit" - headerRateRemaining = "X-RateLimit-Remaining" - headerRateReset = "X-RateLimit-Reset" - headerOTP = "X-GitHub-OTP" - - mediaTypeV3 = "application/vnd.github.v3+json" - defaultMediaType = "application/octet-stream" - mediaTypeV3SHA = "application/vnd.github.v3.sha" - mediaTypeV3Diff = "application/vnd.github.v3.diff" - mediaTypeV3Patch = "application/vnd.github.v3.patch" - mediaTypeOrgPermissionRepo = "application/vnd.github.v3.repository+json" - - // Media Type values to access preview APIs - - // https://developer.github.com/changes/2015-03-09-licenses-api/ - mediaTypeLicensesPreview = "application/vnd.github.drax-preview+json" - - // https://developer.github.com/changes/2014-12-09-new-attributes-for-stars-api/ - mediaTypeStarringPreview = "application/vnd.github.v3.star+json" - - // https://developer.github.com/changes/2015-11-11-protected-branches-api/ - mediaTypeProtectedBranchesPreview = "application/vnd.github.loki-preview+json" - - // https://help.github.com/enterprise/2.4/admin/guides/migrations/exporting-the-github-com-organization-s-repositories/ - mediaTypeMigrationsPreview = "application/vnd.github.wyandotte-preview+json" - - // https://developer.github.com/changes/2016-04-06-deployment-and-deployment-status-enhancements/ - mediaTypeDeploymentStatusPreview = "application/vnd.github.ant-man-preview+json" - - // https://developer.github.com/changes/2016-02-19-source-import-preview-api/ - mediaTypeImportPreview = "application/vnd.github.barred-rock-preview" - - // https://developer.github.com/changes/2016-05-12-reactions-api-preview/ - mediaTypeReactionsPreview = "application/vnd.github.squirrel-girl-preview" - - // https://developer.github.com/changes/2016-04-04-git-signing-api-preview/ - mediaTypeGitSigningPreview = "application/vnd.github.cryptographer-preview+json" - - // https://developer.github.com/changes/2016-05-23-timeline-preview-api/ - mediaTypeTimelinePreview = "application/vnd.github.mockingbird-preview+json" - - // https://developer.github.com/changes/2016-06-14-repository-invitations/ - mediaTypeRepositoryInvitationsPreview = "application/vnd.github.swamp-thing-preview+json" - - // https://developer.github.com/changes/2016-07-06-github-pages-preiew-api/ - mediaTypePagesPreview = "application/vnd.github.mister-fantastic-preview+json" - - // https://developer.github.com/changes/2016-09-14-projects-api/ - mediaTypeProjectsPreview = "application/vnd.github.inertia-preview+json" - - // https://developer.github.com/changes/2016-09-14-Integrations-Early-Access/ - mediaTypeIntegrationPreview = "application/vnd.github.machine-man-preview+json" - - // https://developer.github.com/changes/2017-01-05-commit-search-api/ - mediaTypeCommitSearchPreview = "application/vnd.github.cloak-preview+json" - - // https://developer.github.com/changes/2017-02-28-user-blocking-apis-and-webhook/ - mediaTypeBlockUsersPreview = "application/vnd.github.giant-sentry-fist-preview+json" - - // https://developer.github.com/changes/2017-02-09-community-health/ - mediaTypeRepositoryCommunityHealthMetricsPreview = "application/vnd.github.black-panther-preview+json" - - // https://developer.github.com/changes/2017-05-23-coc-api/ - mediaTypeCodesOfConductPreview = "application/vnd.github.scarlet-witch-preview+json" - - // https://developer.github.com/changes/2017-07-17-update-topics-on-repositories/ - mediaTypeTopicsPreview = "application/vnd.github.mercy-preview+json" - - // https://developer.github.com/changes/2017-07-26-team-review-request-thor-preview/ - mediaTypeTeamReviewPreview = "application/vnd.github.thor-preview+json" - - // https://developer.github.com/v3/apps/marketplace/ - mediaTypeMarketplacePreview = "application/vnd.github.valkyrie-preview+json" - - // https://developer.github.com/changes/2017-08-30-preview-nested-teams/ - mediaTypeNestedTeamsPreview = "application/vnd.github.hellcat-preview+json" - - // https://developer.github.com/changes/2017-11-09-repository-transfer-api-preview/ - mediaTypeRepositoryTransferPreview = "application/vnd.github.nightshade-preview+json" - - // https://developer.github.com/changes/2017-12-19-graphql-node-id/ - mediaTypeGraphQLNodeIDPreview = "application/vnd.github.jean-grey-preview+json" -) - -// A Client manages communication with the GitHub API. -type Client struct { - clientMu sync.Mutex // clientMu protects the client during calls that modify the CheckRedirect func. - client *http.Client // HTTP client used to communicate with the API. - - // Base URL for API requests. Defaults to the public GitHub API, but can be - // set to a domain endpoint to use with GitHub Enterprise. BaseURL should - // always be specified with a trailing slash. - BaseURL *url.URL - - // Base URL for uploading files. - UploadURL *url.URL - - // User agent used when communicating with the GitHub API. - UserAgent string - - rateMu sync.Mutex - rateLimits [categories]Rate // Rate limits for the client as determined by the most recent API calls. - - common service // Reuse a single struct instead of allocating one for each service on the heap. - - // Services used for talking to different parts of the GitHub API. - Activity *ActivityService - Admin *AdminService - Apps *AppsService - Authorizations *AuthorizationsService - Gists *GistsService - Git *GitService - Gitignores *GitignoresService - Issues *IssuesService - Licenses *LicensesService - Marketplace *MarketplaceService - Migrations *MigrationService - Organizations *OrganizationsService - Projects *ProjectsService - PullRequests *PullRequestsService - Reactions *ReactionsService - Repositories *RepositoriesService - Search *SearchService - Users *UsersService -} - -type service struct { - client *Client -} - -// ListOptions specifies the optional parameters to various List methods that -// support pagination. -type ListOptions struct { - // For paginated result sets, page of results to retrieve. - Page int `url:"page,omitempty"` - - // For paginated result sets, the number of results to include per page. - PerPage int `url:"per_page,omitempty"` -} - -// UploadOptions specifies the parameters to methods that support uploads. -type UploadOptions struct { - Name string `url:"name,omitempty"` -} - -// RawType represents type of raw format of a request instead of JSON. -type RawType uint8 - -const ( - // Diff format. - Diff RawType = 1 + iota - // Patch format. - Patch -) - -// RawOptions specifies parameters when user wants to get raw format of -// a response instead of JSON. -type RawOptions struct { - Type RawType -} - -// addOptions adds the parameters in opt as URL query parameters to s. opt -// must be a struct whose fields may contain "url" tags. -func addOptions(s string, opt interface{}) (string, error) { - v := reflect.ValueOf(opt) - if v.Kind() == reflect.Ptr && v.IsNil() { - return s, nil - } - - u, err := url.Parse(s) - if err != nil { - return s, err - } - - qs, err := query.Values(opt) - if err != nil { - return s, err - } - - u.RawQuery = qs.Encode() - return u.String(), nil -} - -// NewClient returns a new GitHub API client. If a nil httpClient is -// provided, http.DefaultClient will be used. To use API methods which require -// authentication, provide an http.Client that will perform the authentication -// for you (such as that provided by the golang.org/x/oauth2 library). -func NewClient(httpClient *http.Client) *Client { - if httpClient == nil { - httpClient = http.DefaultClient - } - baseURL, _ := url.Parse(defaultBaseURL) - uploadURL, _ := url.Parse(uploadBaseURL) - - c := &Client{client: httpClient, BaseURL: baseURL, UserAgent: userAgent, UploadURL: uploadURL} - c.common.client = c - c.Activity = (*ActivityService)(&c.common) - c.Admin = (*AdminService)(&c.common) - c.Apps = (*AppsService)(&c.common) - c.Authorizations = (*AuthorizationsService)(&c.common) - c.Gists = (*GistsService)(&c.common) - c.Git = (*GitService)(&c.common) - c.Gitignores = (*GitignoresService)(&c.common) - c.Issues = (*IssuesService)(&c.common) - c.Licenses = (*LicensesService)(&c.common) - c.Marketplace = &MarketplaceService{client: c} - c.Migrations = (*MigrationService)(&c.common) - c.Organizations = (*OrganizationsService)(&c.common) - c.Projects = (*ProjectsService)(&c.common) - c.PullRequests = (*PullRequestsService)(&c.common) - c.Reactions = (*ReactionsService)(&c.common) - c.Repositories = (*RepositoriesService)(&c.common) - c.Search = (*SearchService)(&c.common) - c.Users = (*UsersService)(&c.common) - return c -} - -// NewEnterpriseClient returns a new GitHub API client with provided -// base URL and upload URL (often the same URL). -// If either URL does not have a trailing slash, one is added automatically. -// If a nil httpClient is provided, http.DefaultClient will be used. -// -// Note that NewEnterpriseClient is a convenience helper only; -// its behavior is equivalent to using NewClient, followed by setting -// the BaseURL and UploadURL fields. -func NewEnterpriseClient(baseURL, uploadURL string, httpClient *http.Client) (*Client, error) { - baseEndpoint, err := url.Parse(baseURL) - if err != nil { - return nil, err - } - if !strings.HasSuffix(baseEndpoint.Path, "/") { - baseEndpoint.Path += "/" - } - - uploadEndpoint, err := url.Parse(uploadURL) - if err != nil { - return nil, err - } - if !strings.HasSuffix(uploadEndpoint.Path, "/") { - uploadEndpoint.Path += "/" - } - - c := NewClient(httpClient) - c.BaseURL = baseEndpoint - c.UploadURL = uploadEndpoint - return c, nil -} - -// NewRequest creates an API request. A relative URL can be provided in urlStr, -// in which case it is resolved relative to the BaseURL of the Client. -// Relative URLs should always be specified without a preceding slash. If -// specified, the value pointed to by body is JSON encoded and included as the -// request body. -func (c *Client) NewRequest(method, urlStr string, body interface{}) (*http.Request, error) { - if !strings.HasSuffix(c.BaseURL.Path, "/") { - return nil, fmt.Errorf("BaseURL must have a trailing slash, but %q does not", c.BaseURL) - } - u, err := c.BaseURL.Parse(urlStr) - if err != nil { - return nil, err - } - - var buf io.ReadWriter - if body != nil { - buf = new(bytes.Buffer) - enc := json.NewEncoder(buf) - enc.SetEscapeHTML(false) - err := enc.Encode(body) - if err != nil { - return nil, err - } - } - - req, err := http.NewRequest(method, u.String(), buf) - if err != nil { - return nil, err - } - - if body != nil { - req.Header.Set("Content-Type", "application/json") - } - req.Header.Set("Accept", mediaTypeV3) - if c.UserAgent != "" { - req.Header.Set("User-Agent", c.UserAgent) - } - return req, nil -} - -// NewUploadRequest creates an upload request. A relative URL can be provided in -// urlStr, in which case it is resolved relative to the UploadURL of the Client. -// Relative URLs should always be specified without a preceding slash. -func (c *Client) NewUploadRequest(urlStr string, reader io.Reader, size int64, mediaType string) (*http.Request, error) { - if !strings.HasSuffix(c.UploadURL.Path, "/") { - return nil, fmt.Errorf("UploadURL must have a trailing slash, but %q does not", c.UploadURL) - } - u, err := c.UploadURL.Parse(urlStr) - if err != nil { - return nil, err - } - - req, err := http.NewRequest("POST", u.String(), reader) - if err != nil { - return nil, err - } - req.ContentLength = size - - if mediaType == "" { - mediaType = defaultMediaType - } - req.Header.Set("Content-Type", mediaType) - req.Header.Set("Accept", mediaTypeV3) - req.Header.Set("User-Agent", c.UserAgent) - return req, nil -} - -// Response is a GitHub API response. This wraps the standard http.Response -// returned from GitHub and provides convenient access to things like -// pagination links. -type Response struct { - *http.Response - - // These fields provide the page values for paginating through a set of - // results. Any or all of these may be set to the zero value for - // responses that are not part of a paginated set, or for which there - // are no additional pages. - - NextPage int - PrevPage int - FirstPage int - LastPage int - - Rate -} - -// newResponse creates a new Response for the provided http.Response. -// r must not be nil. -func newResponse(r *http.Response) *Response { - response := &Response{Response: r} - response.populatePageValues() - response.Rate = parseRate(r) - return response -} - -// populatePageValues parses the HTTP Link response headers and populates the -// various pagination link values in the Response. -func (r *Response) populatePageValues() { - if links, ok := r.Response.Header["Link"]; ok && len(links) > 0 { - for _, link := range strings.Split(links[0], ",") { - segments := strings.Split(strings.TrimSpace(link), ";") - - // link must at least have href and rel - if len(segments) < 2 { - continue - } - - // ensure href is properly formatted - if !strings.HasPrefix(segments[0], "<") || !strings.HasSuffix(segments[0], ">") { - continue - } - - // try to pull out page parameter - url, err := url.Parse(segments[0][1 : len(segments[0])-1]) - if err != nil { - continue - } - page := url.Query().Get("page") - if page == "" { - continue - } - - for _, segment := range segments[1:] { - switch strings.TrimSpace(segment) { - case `rel="next"`: - r.NextPage, _ = strconv.Atoi(page) - case `rel="prev"`: - r.PrevPage, _ = strconv.Atoi(page) - case `rel="first"`: - r.FirstPage, _ = strconv.Atoi(page) - case `rel="last"`: - r.LastPage, _ = strconv.Atoi(page) - } - - } - } - } -} - -// parseRate parses the rate related headers. -func parseRate(r *http.Response) Rate { - var rate Rate - if limit := r.Header.Get(headerRateLimit); limit != "" { - rate.Limit, _ = strconv.Atoi(limit) - } - if remaining := r.Header.Get(headerRateRemaining); remaining != "" { - rate.Remaining, _ = strconv.Atoi(remaining) - } - if reset := r.Header.Get(headerRateReset); reset != "" { - if v, _ := strconv.ParseInt(reset, 10, 64); v != 0 { - rate.Reset = Timestamp{time.Unix(v, 0)} - } - } - return rate -} - -// Do sends an API request and returns the API response. The API response is -// JSON decoded and stored in the value pointed to by v, or returned as an -// error if an API error has occurred. If v implements the io.Writer -// interface, the raw response body will be written to v, without attempting to -// first decode it. If rate limit is exceeded and reset time is in the future, -// Do returns *RateLimitError immediately without making a network API call. -// -// The provided ctx must be non-nil. If it is canceled or times out, -// ctx.Err() will be returned. -func (c *Client) Do(ctx context.Context, req *http.Request, v interface{}) (*Response, error) { - req = withContext(ctx, req) - - rateLimitCategory := category(req.URL.Path) - - // If we've hit rate limit, don't make further requests before Reset time. - if err := c.checkRateLimitBeforeDo(req, rateLimitCategory); err != nil { - return &Response{ - Response: err.Response, - Rate: err.Rate, - }, err - } - - resp, err := c.client.Do(req) - if err != nil { - // If we got an error, and the context has been canceled, - // the context's error is probably more useful. - select { - case <-ctx.Done(): - return nil, ctx.Err() - default: - } - - // If the error type is *url.Error, sanitize its URL before returning. - if e, ok := err.(*url.Error); ok { - if url, err := url.Parse(e.URL); err == nil { - e.URL = sanitizeURL(url).String() - return nil, e - } - } - - return nil, err - } - - defer func() { - // Drain up to 512 bytes and close the body to let the Transport reuse the connection - io.CopyN(ioutil.Discard, resp.Body, 512) - resp.Body.Close() - }() - - response := newResponse(resp) - - c.rateMu.Lock() - c.rateLimits[rateLimitCategory] = response.Rate - c.rateMu.Unlock() - - err = CheckResponse(resp) - if err != nil { - // even though there was an error, we still return the response - // in case the caller wants to inspect it further - return response, err - } - - if v != nil { - if w, ok := v.(io.Writer); ok { - io.Copy(w, resp.Body) - } else { - err = json.NewDecoder(resp.Body).Decode(v) - if err == io.EOF { - err = nil // ignore EOF errors caused by empty response body - } - } - } - - return response, err -} - -// checkRateLimitBeforeDo does not make any network calls, but uses existing knowledge from -// current client state in order to quickly check if *RateLimitError can be immediately returned -// from Client.Do, and if so, returns it so that Client.Do can skip making a network API call unnecessarily. -// Otherwise it returns nil, and Client.Do should proceed normally. -func (c *Client) checkRateLimitBeforeDo(req *http.Request, rateLimitCategory rateLimitCategory) *RateLimitError { - c.rateMu.Lock() - rate := c.rateLimits[rateLimitCategory] - c.rateMu.Unlock() - if !rate.Reset.Time.IsZero() && rate.Remaining == 0 && time.Now().Before(rate.Reset.Time) { - // Create a fake response. - resp := &http.Response{ - Status: http.StatusText(http.StatusForbidden), - StatusCode: http.StatusForbidden, - Request: req, - Header: make(http.Header), - Body: ioutil.NopCloser(strings.NewReader("")), - } - return &RateLimitError{ - Rate: rate, - Response: resp, - Message: fmt.Sprintf("API rate limit of %v still exceeded until %v, not making remote request.", rate.Limit, rate.Reset.Time), - } - } - - return nil -} - -/* -An ErrorResponse reports one or more errors caused by an API request. - -GitHub API docs: https://developer.github.com/v3/#client-errors -*/ -type ErrorResponse struct { - Response *http.Response // HTTP response that caused this error - Message string `json:"message"` // error message - Errors []Error `json:"errors"` // more detail on individual errors - // Block is only populated on certain types of errors such as code 451. - // See https://developer.github.com/changes/2016-03-17-the-451-status-code-is-now-supported/ - // for more information. - Block *struct { - Reason string `json:"reason,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - } `json:"block,omitempty"` - // Most errors will also include a documentation_url field pointing - // to some content that might help you resolve the error, see - // https://developer.github.com/v3/#client-errors - DocumentationURL string `json:"documentation_url,omitempty"` -} - -func (r *ErrorResponse) Error() string { - return fmt.Sprintf("%v %v: %d %v %+v", - r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), - r.Response.StatusCode, r.Message, r.Errors) -} - -// TwoFactorAuthError occurs when using HTTP Basic Authentication for a user -// that has two-factor authentication enabled. The request can be reattempted -// by providing a one-time password in the request. -type TwoFactorAuthError ErrorResponse - -func (r *TwoFactorAuthError) Error() string { return (*ErrorResponse)(r).Error() } - -// RateLimitError occurs when GitHub returns 403 Forbidden response with a rate limit -// remaining value of 0, and error message starts with "API rate limit exceeded for ". -type RateLimitError struct { - Rate Rate // Rate specifies last known rate limit for the client - Response *http.Response // HTTP response that caused this error - Message string `json:"message"` // error message -} - -func (r *RateLimitError) Error() string { - return fmt.Sprintf("%v %v: %d %v %v", - r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), - r.Response.StatusCode, r.Message, formatRateReset(r.Rate.Reset.Time.Sub(time.Now()))) -} - -// AcceptedError occurs when GitHub returns 202 Accepted response with an -// empty body, which means a job was scheduled on the GitHub side to process -// the information needed and cache it. -// Technically, 202 Accepted is not a real error, it's just used to -// indicate that results are not ready yet, but should be available soon. -// The request can be repeated after some time. -type AcceptedError struct{} - -func (*AcceptedError) Error() string { - return "job scheduled on GitHub side; try again later" -} - -// AbuseRateLimitError occurs when GitHub returns 403 Forbidden response with the -// "documentation_url" field value equal to "https://developer.github.com/v3/#abuse-rate-limits". -type AbuseRateLimitError struct { - Response *http.Response // HTTP response that caused this error - Message string `json:"message"` // error message - - // RetryAfter is provided with some abuse rate limit errors. If present, - // it is the amount of time that the client should wait before retrying. - // Otherwise, the client should try again later (after an unspecified amount of time). - RetryAfter *time.Duration -} - -func (r *AbuseRateLimitError) Error() string { - return fmt.Sprintf("%v %v: %d %v", - r.Response.Request.Method, sanitizeURL(r.Response.Request.URL), - r.Response.StatusCode, r.Message) -} - -// sanitizeURL redacts the client_secret parameter from the URL which may be -// exposed to the user. -func sanitizeURL(uri *url.URL) *url.URL { - if uri == nil { - return nil - } - params := uri.Query() - if len(params.Get("client_secret")) > 0 { - params.Set("client_secret", "REDACTED") - uri.RawQuery = params.Encode() - } - return uri -} - -/* -An Error reports more details on an individual error in an ErrorResponse. -These are the possible validation error codes: - - missing: - resource does not exist - missing_field: - a required field on a resource has not been set - invalid: - the formatting of a field is invalid - already_exists: - another resource has the same valid as this field - custom: - some resources return this (e.g. github.User.CreateKey()), additional - information is set in the Message field of the Error - -GitHub API docs: https://developer.github.com/v3/#client-errors -*/ -type Error struct { - Resource string `json:"resource"` // resource on which the error occurred - Field string `json:"field"` // field on which the error occurred - Code string `json:"code"` // validation error code - Message string `json:"message"` // Message describing the error. Errors with Code == "custom" will always have this set. -} - -func (e *Error) Error() string { - return fmt.Sprintf("%v error caused by %v field on %v resource", - e.Code, e.Field, e.Resource) -} - -// CheckResponse checks the API response for errors, and returns them if -// present. A response is considered an error if it has a status code outside -// the 200 range or equal to 202 Accepted. -// API error responses are expected to have either no response -// body, or a JSON response body that maps to ErrorResponse. Any other -// response body will be silently ignored. -// -// The error type will be *RateLimitError for rate limit exceeded errors, -// *AcceptedError for 202 Accepted status codes, -// and *TwoFactorAuthError for two-factor authentication errors. -func CheckResponse(r *http.Response) error { - if r.StatusCode == http.StatusAccepted { - return &AcceptedError{} - } - if c := r.StatusCode; 200 <= c && c <= 299 { - return nil - } - errorResponse := &ErrorResponse{Response: r} - data, err := ioutil.ReadAll(r.Body) - if err == nil && data != nil { - json.Unmarshal(data, errorResponse) - } - switch { - case r.StatusCode == http.StatusUnauthorized && strings.HasPrefix(r.Header.Get(headerOTP), "required"): - return (*TwoFactorAuthError)(errorResponse) - case r.StatusCode == http.StatusForbidden && r.Header.Get(headerRateRemaining) == "0" && strings.HasPrefix(errorResponse.Message, "API rate limit exceeded for "): - return &RateLimitError{ - Rate: parseRate(r), - Response: errorResponse.Response, - Message: errorResponse.Message, - } - case r.StatusCode == http.StatusForbidden && errorResponse.DocumentationURL == "https://developer.github.com/v3/#abuse-rate-limits": - abuseRateLimitError := &AbuseRateLimitError{ - Response: errorResponse.Response, - Message: errorResponse.Message, - } - if v := r.Header["Retry-After"]; len(v) > 0 { - // According to GitHub support, the "Retry-After" header value will be - // an integer which represents the number of seconds that one should - // wait before resuming making requests. - retryAfterSeconds, _ := strconv.ParseInt(v[0], 10, 64) // Error handling is noop. - retryAfter := time.Duration(retryAfterSeconds) * time.Second - abuseRateLimitError.RetryAfter = &retryAfter - } - return abuseRateLimitError - default: - return errorResponse - } -} - -// parseBoolResponse determines the boolean result from a GitHub API response. -// Several GitHub API methods return boolean responses indicated by the HTTP -// status code in the response (true indicated by a 204, false indicated by a -// 404). This helper function will determine that result and hide the 404 -// error if present. Any other error will be returned through as-is. -func parseBoolResponse(err error) (bool, error) { - if err == nil { - return true, nil - } - - if err, ok := err.(*ErrorResponse); ok && err.Response.StatusCode == http.StatusNotFound { - // Simply false. In this one case, we do not pass the error through. - return false, nil - } - - // some other real error occurred - return false, err -} - -// Rate represents the rate limit for the current client. -type Rate struct { - // The number of requests per hour the client is currently limited to. - Limit int `json:"limit"` - - // The number of remaining requests the client can make this hour. - Remaining int `json:"remaining"` - - // The time at which the current rate limit will reset. - Reset Timestamp `json:"reset"` -} - -func (r Rate) String() string { - return Stringify(r) -} - -// RateLimits represents the rate limits for the current client. -type RateLimits struct { - // The rate limit for non-search API requests. Unauthenticated - // requests are limited to 60 per hour. Authenticated requests are - // limited to 5,000 per hour. - // - // GitHub API docs: https://developer.github.com/v3/#rate-limiting - Core *Rate `json:"core"` - - // The rate limit for search API requests. Unauthenticated requests - // are limited to 10 requests per minutes. Authenticated requests are - // limited to 30 per minute. - // - // GitHub API docs: https://developer.github.com/v3/search/#rate-limit - Search *Rate `json:"search"` -} - -func (r RateLimits) String() string { - return Stringify(r) -} - -type rateLimitCategory uint8 - -const ( - coreCategory rateLimitCategory = iota - searchCategory - - categories // An array of this length will be able to contain all rate limit categories. -) - -// category returns the rate limit category of the endpoint, determined by Request.URL.Path. -func category(path string) rateLimitCategory { - switch { - default: - return coreCategory - case strings.HasPrefix(path, "/search/"): - return searchCategory - } -} - -// RateLimits returns the rate limits for the current client. -func (c *Client) RateLimits(ctx context.Context) (*RateLimits, *Response, error) { - req, err := c.NewRequest("GET", "rate_limit", nil) - if err != nil { - return nil, nil, err - } - - response := new(struct { - Resources *RateLimits `json:"resources"` - }) - resp, err := c.Do(ctx, req, response) - if err != nil { - return nil, nil, err - } - - if response.Resources != nil { - c.rateMu.Lock() - if response.Resources.Core != nil { - c.rateLimits[coreCategory] = *response.Resources.Core - } - if response.Resources.Search != nil { - c.rateLimits[searchCategory] = *response.Resources.Search - } - c.rateMu.Unlock() - } - - return response.Resources, resp, nil -} - -/* -UnauthenticatedRateLimitedTransport allows you to make unauthenticated calls -that need to use a higher rate limit associated with your OAuth application. - - t := &github.UnauthenticatedRateLimitedTransport{ - ClientID: "your app's client ID", - ClientSecret: "your app's client secret", - } - client := github.NewClient(t.Client()) - -This will append the querystring params client_id=xxx&client_secret=yyy to all -requests. - -See https://developer.github.com/v3/#unauthenticated-rate-limited-requests for -more information. -*/ -type UnauthenticatedRateLimitedTransport struct { - // ClientID is the GitHub OAuth client ID of the current application, which - // can be found by selecting its entry in the list at - // https://github.com/settings/applications. - ClientID string - - // ClientSecret is the GitHub OAuth client secret of the current - // application. - ClientSecret string - - // Transport is the underlying HTTP transport to use when making requests. - // It will default to http.DefaultTransport if nil. - Transport http.RoundTripper -} - -// RoundTrip implements the RoundTripper interface. -func (t *UnauthenticatedRateLimitedTransport) RoundTrip(req *http.Request) (*http.Response, error) { - if t.ClientID == "" { - return nil, errors.New("t.ClientID is empty") - } - if t.ClientSecret == "" { - return nil, errors.New("t.ClientSecret is empty") - } - - // To set extra querystring params, we must make a copy of the Request so - // that we don't modify the Request we were given. This is required by the - // specification of http.RoundTripper. - // - // Since we are going to modify only req.URL here, we only need a deep copy - // of req.URL. - req2 := new(http.Request) - *req2 = *req - req2.URL = new(url.URL) - *req2.URL = *req.URL - - q := req2.URL.Query() - q.Set("client_id", t.ClientID) - q.Set("client_secret", t.ClientSecret) - req2.URL.RawQuery = q.Encode() - - // Make the HTTP request. - return t.transport().RoundTrip(req2) -} - -// Client returns an *http.Client that makes requests which are subject to the -// rate limit of your OAuth application. -func (t *UnauthenticatedRateLimitedTransport) Client() *http.Client { - return &http.Client{Transport: t} -} - -func (t *UnauthenticatedRateLimitedTransport) transport() http.RoundTripper { - if t.Transport != nil { - return t.Transport - } - return http.DefaultTransport -} - -// BasicAuthTransport is an http.RoundTripper that authenticates all requests -// using HTTP Basic Authentication with the provided username and password. It -// additionally supports users who have two-factor authentication enabled on -// their GitHub account. -type BasicAuthTransport struct { - Username string // GitHub username - Password string // GitHub password - OTP string // one-time password for users with two-factor auth enabled - - // Transport is the underlying HTTP transport to use when making requests. - // It will default to http.DefaultTransport if nil. - Transport http.RoundTripper -} - -// RoundTrip implements the RoundTripper interface. -func (t *BasicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) { - // To set extra headers, we must make a copy of the Request so - // that we don't modify the Request we were given. This is required by the - // specification of http.RoundTripper. - // - // Since we are going to modify only req.Header here, we only need a deep copy - // of req.Header. - req2 := new(http.Request) - *req2 = *req - req2.Header = make(http.Header, len(req.Header)) - for k, s := range req.Header { - req2.Header[k] = append([]string(nil), s...) - } - - req2.SetBasicAuth(t.Username, t.Password) - if t.OTP != "" { - req2.Header.Set(headerOTP, t.OTP) - } - return t.transport().RoundTrip(req2) -} - -// Client returns an *http.Client that makes requests that are authenticated -// using HTTP Basic Authentication. -func (t *BasicAuthTransport) Client() *http.Client { - return &http.Client{Transport: t} -} - -func (t *BasicAuthTransport) transport() http.RoundTripper { - if t.Transport != nil { - return t.Transport - } - return http.DefaultTransport -} - -// formatRateReset formats d to look like "[rate reset in 2s]" or -// "[rate reset in 87m02s]" for the positive durations. And like "[rate limit was reset 87m02s ago]" -// for the negative cases. -func formatRateReset(d time.Duration) string { - isNegative := d < 0 - if isNegative { - d *= -1 - } - secondsTotal := int(0.5 + d.Seconds()) - minutes := secondsTotal / 60 - seconds := secondsTotal - minutes*60 - - var timeString string - if minutes > 0 { - timeString = fmt.Sprintf("%dm%02ds", minutes, seconds) - } else { - timeString = fmt.Sprintf("%ds", seconds) - } - - if isNegative { - return fmt.Sprintf("[rate limit was reset %v ago]", timeString) - } - return fmt.Sprintf("[rate reset in %v]", timeString) -} - -// Bool is a helper routine that allocates a new bool value -// to store v and returns a pointer to it. -func Bool(v bool) *bool { return &v } - -// Int is a helper routine that allocates a new int value -// to store v and returns a pointer to it. -func Int(v int) *int { return &v } - -// Int64 is a helper routine that allocates a new int64 value -// to store v and returns a pointer to it. -func Int64(v int64) *int64 { return &v } - -// String is a helper routine that allocates a new string value -// to store v and returns a pointer to it. -func String(v string) *string { return &v } diff --git a/vendor/github.com/google/go-github/github/gitignore.go b/vendor/github.com/google/go-github/github/gitignore.go deleted file mode 100644 index 2f691bc323e..00000000000 --- a/vendor/github.com/google/go-github/github/gitignore.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// GitignoresService provides access to the gitignore related functions in the -// GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/gitignore/ -type GitignoresService service - -// Gitignore represents a .gitignore file as returned by the GitHub API. -type Gitignore struct { - Name *string `json:"name,omitempty"` - Source *string `json:"source,omitempty"` -} - -func (g Gitignore) String() string { - return Stringify(g) -} - -// List all available Gitignore templates. -// -// GitHub API docs: https://developer.github.com/v3/gitignore/#listing-available-templates -func (s GitignoresService) List(ctx context.Context) ([]string, *Response, error) { - req, err := s.client.NewRequest("GET", "gitignore/templates", nil) - if err != nil { - return nil, nil, err - } - - var availableTemplates []string - resp, err := s.client.Do(ctx, req, &availableTemplates) - if err != nil { - return nil, resp, err - } - - return availableTemplates, resp, nil -} - -// Get a Gitignore by name. -// -// GitHub API docs: https://developer.github.com/v3/gitignore/#get-a-single-template -func (s GitignoresService) Get(ctx context.Context, name string) (*Gitignore, *Response, error) { - u := fmt.Sprintf("gitignore/templates/%v", name) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - gitignore := new(Gitignore) - resp, err := s.client.Do(ctx, req, gitignore) - if err != nil { - return nil, resp, err - } - - return gitignore, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/issues.go b/vendor/github.com/google/go-github/github/issues.go deleted file mode 100644 index f865ea20e87..00000000000 --- a/vendor/github.com/google/go-github/github/issues.go +++ /dev/null @@ -1,330 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" - "strings" - "time" -) - -// IssuesService handles communication with the issue related -// methods of the GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/issues/ -type IssuesService service - -// Issue represents a GitHub issue on a repository. -// -// Note: As far as the GitHub API is concerned, every pull request is an issue, -// but not every issue is a pull request. Some endpoints, events, and webhooks -// may also return pull requests via this struct. If PullRequestLinks is nil, -// this is an issue, and if PullRequestLinks is not nil, this is a pull request. -// The IsPullRequest helper method can be used to check that. -type Issue struct { - ID *int64 `json:"id,omitempty"` - Number *int `json:"number,omitempty"` - State *string `json:"state,omitempty"` - Locked *bool `json:"locked,omitempty"` - Title *string `json:"title,omitempty"` - Body *string `json:"body,omitempty"` - User *User `json:"user,omitempty"` - Labels []Label `json:"labels,omitempty"` - Assignee *User `json:"assignee,omitempty"` - Comments *int `json:"comments,omitempty"` - ClosedAt *time.Time `json:"closed_at,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - ClosedBy *User `json:"closed_by,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - CommentsURL *string `json:"comments_url,omitempty"` - EventsURL *string `json:"events_url,omitempty"` - LabelsURL *string `json:"labels_url,omitempty"` - RepositoryURL *string `json:"repository_url,omitempty"` - Milestone *Milestone `json:"milestone,omitempty"` - PullRequestLinks *PullRequestLinks `json:"pull_request,omitempty"` - Repository *Repository `json:"repository,omitempty"` - Reactions *Reactions `json:"reactions,omitempty"` - Assignees []*User `json:"assignees,omitempty"` - NodeID *string `json:"node_id,omitempty"` - - // TextMatches is only populated from search results that request text matches - // See: search.go and https://developer.github.com/v3/search/#text-match-metadata - TextMatches []TextMatch `json:"text_matches,omitempty"` -} - -func (i Issue) String() string { - return Stringify(i) -} - -// IsPullRequest reports whether the issue is also a pull request. It uses the -// method recommended by GitHub's API documentation, which is to check whether -// PullRequestLinks is non-nil. -func (i Issue) IsPullRequest() bool { - return i.PullRequestLinks != nil -} - -// IssueRequest represents a request to create/edit an issue. -// It is separate from Issue above because otherwise Labels -// and Assignee fail to serialize to the correct JSON. -type IssueRequest struct { - Title *string `json:"title,omitempty"` - Body *string `json:"body,omitempty"` - Labels *[]string `json:"labels,omitempty"` - Assignee *string `json:"assignee,omitempty"` - State *string `json:"state,omitempty"` - Milestone *int `json:"milestone,omitempty"` - Assignees *[]string `json:"assignees,omitempty"` -} - -// IssueListOptions specifies the optional parameters to the IssuesService.List -// and IssuesService.ListByOrg methods. -type IssueListOptions struct { - // Filter specifies which issues to list. Possible values are: assigned, - // created, mentioned, subscribed, all. Default is "assigned". - Filter string `url:"filter,omitempty"` - - // State filters issues based on their state. Possible values are: open, - // closed, all. Default is "open". - State string `url:"state,omitempty"` - - // Labels filters issues based on their label. - Labels []string `url:"labels,comma,omitempty"` - - // Sort specifies how to sort issues. Possible values are: created, updated, - // and comments. Default value is "created". - Sort string `url:"sort,omitempty"` - - // Direction in which to sort issues. Possible values are: asc, desc. - // Default is "desc". - Direction string `url:"direction,omitempty"` - - // Since filters issues by time. - Since time.Time `url:"since,omitempty"` - - ListOptions -} - -// PullRequestLinks object is added to the Issue object when it's an issue included -// in the IssueCommentEvent webhook payload, if the webhook is fired by a comment on a PR. -type PullRequestLinks struct { - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - DiffURL *string `json:"diff_url,omitempty"` - PatchURL *string `json:"patch_url,omitempty"` -} - -// List the issues for the authenticated user. If all is true, list issues -// across all the user's visible repositories including owned, member, and -// organization repositories; if false, list only owned and member -// repositories. -// -// GitHub API docs: https://developer.github.com/v3/issues/#list-issues -func (s *IssuesService) List(ctx context.Context, all bool, opt *IssueListOptions) ([]*Issue, *Response, error) { - var u string - if all { - u = "issues" - } else { - u = "user/issues" - } - return s.listIssues(ctx, u, opt) -} - -// ListByOrg fetches the issues in the specified organization for the -// authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/issues/#list-issues -func (s *IssuesService) ListByOrg(ctx context.Context, org string, opt *IssueListOptions) ([]*Issue, *Response, error) { - u := fmt.Sprintf("orgs/%v/issues", org) - return s.listIssues(ctx, u, opt) -} - -func (s *IssuesService) listIssues(ctx context.Context, u string, opt *IssueListOptions) ([]*Issue, *Response, error) { - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeGraphQLNodeIDPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - var issues []*Issue - resp, err := s.client.Do(ctx, req, &issues) - if err != nil { - return nil, resp, err - } - - return issues, resp, nil -} - -// IssueListByRepoOptions specifies the optional parameters to the -// IssuesService.ListByRepo method. -type IssueListByRepoOptions struct { - // Milestone limits issues for the specified milestone. Possible values are - // a milestone number, "none" for issues with no milestone, "*" for issues - // with any milestone. - Milestone string `url:"milestone,omitempty"` - - // State filters issues based on their state. Possible values are: open, - // closed, all. Default is "open". - State string `url:"state,omitempty"` - - // Assignee filters issues based on their assignee. Possible values are a - // user name, "none" for issues that are not assigned, "*" for issues with - // any assigned user. - Assignee string `url:"assignee,omitempty"` - - // Creator filters issues based on their creator. - Creator string `url:"creator,omitempty"` - - // Mentioned filters issues to those mentioned a specific user. - Mentioned string `url:"mentioned,omitempty"` - - // Labels filters issues based on their label. - Labels []string `url:"labels,omitempty,comma"` - - // Sort specifies how to sort issues. Possible values are: created, updated, - // and comments. Default value is "created". - Sort string `url:"sort,omitempty"` - - // Direction in which to sort issues. Possible values are: asc, desc. - // Default is "desc". - Direction string `url:"direction,omitempty"` - - // Since filters issues by time. - Since time.Time `url:"since,omitempty"` - - ListOptions -} - -// ListByRepo lists the issues for the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/issues/#list-issues-for-a-repository -func (s *IssuesService) ListByRepo(ctx context.Context, owner string, repo string, opt *IssueListByRepoOptions) ([]*Issue, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeGraphQLNodeIDPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - var issues []*Issue - resp, err := s.client.Do(ctx, req, &issues) - if err != nil { - return nil, resp, err - } - - return issues, resp, nil -} - -// Get a single issue. -// -// GitHub API docs: https://developer.github.com/v3/issues/#get-a-single-issue -func (s *IssuesService) Get(ctx context.Context, owner string, repo string, number int) (*Issue, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeReactionsPreview, mediaTypeGraphQLNodeIDPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - issue := new(Issue) - resp, err := s.client.Do(ctx, req, issue) - if err != nil { - return nil, resp, err - } - - return issue, resp, nil -} - -// Create a new issue on the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/issues/#create-an-issue -func (s *IssuesService) Create(ctx context.Context, owner string, repo string, issue *IssueRequest) (*Issue, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues", owner, repo) - req, err := s.client.NewRequest("POST", u, issue) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - i := new(Issue) - resp, err := s.client.Do(ctx, req, i) - if err != nil { - return nil, resp, err - } - - return i, resp, nil -} - -// Edit an issue. -// -// GitHub API docs: https://developer.github.com/v3/issues/#edit-an-issue -func (s *IssuesService) Edit(ctx context.Context, owner string, repo string, number int, issue *IssueRequest) (*Issue, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d", owner, repo, number) - req, err := s.client.NewRequest("PATCH", u, issue) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - i := new(Issue) - resp, err := s.client.Do(ctx, req, i) - if err != nil { - return nil, resp, err - } - - return i, resp, nil -} - -// Lock an issue's conversation. -// -// GitHub API docs: https://developer.github.com/v3/issues/#lock-an-issue -func (s *IssuesService) Lock(ctx context.Context, owner string, repo string, number int) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number) - req, err := s.client.NewRequest("PUT", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// Unlock an issue's conversation. -// -// GitHub API docs: https://developer.github.com/v3/issues/#unlock-an-issue -func (s *IssuesService) Unlock(ctx context.Context, owner string, repo string, number int) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/lock", owner, repo, number) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/issues_assignees.go b/vendor/github.com/google/go-github/github/issues_assignees.go deleted file mode 100644 index 9cb366f50a3..00000000000 --- a/vendor/github.com/google/go-github/github/issues_assignees.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// ListAssignees fetches all available assignees (owners and collaborators) to -// which issues may be assigned. -// -// GitHub API docs: https://developer.github.com/v3/issues/assignees/#list-assignees -func (s *IssuesService) ListAssignees(ctx context.Context, owner, repo string, opt *ListOptions) ([]*User, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/assignees", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - var assignees []*User - resp, err := s.client.Do(ctx, req, &assignees) - if err != nil { - return nil, resp, err - } - - return assignees, resp, nil -} - -// IsAssignee checks if a user is an assignee for the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/issues/assignees/#check-assignee -func (s *IssuesService) IsAssignee(ctx context.Context, owner, repo, user string) (bool, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/assignees/%v", owner, repo, user) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return false, nil, err - } - resp, err := s.client.Do(ctx, req, nil) - assignee, err := parseBoolResponse(err) - return assignee, resp, err -} - -// AddAssignees adds the provided GitHub users as assignees to the issue. -// -// GitHub API docs: https://developer.github.com/v3/issues/assignees/#add-assignees-to-an-issue -func (s *IssuesService) AddAssignees(ctx context.Context, owner, repo string, number int, assignees []string) (*Issue, *Response, error) { - users := &struct { - Assignees []string `json:"assignees,omitempty"` - }{Assignees: assignees} - u := fmt.Sprintf("repos/%v/%v/issues/%v/assignees", owner, repo, number) - req, err := s.client.NewRequest("POST", u, users) - if err != nil { - return nil, nil, err - } - - issue := &Issue{} - resp, err := s.client.Do(ctx, req, issue) - return issue, resp, err -} - -// RemoveAssignees removes the provided GitHub users as assignees from the issue. -// -// GitHub API docs: https://developer.github.com/v3/issues/assignees/#remove-assignees-from-an-issue -func (s *IssuesService) RemoveAssignees(ctx context.Context, owner, repo string, number int, assignees []string) (*Issue, *Response, error) { - users := &struct { - Assignees []string `json:"assignees,omitempty"` - }{Assignees: assignees} - u := fmt.Sprintf("repos/%v/%v/issues/%v/assignees", owner, repo, number) - req, err := s.client.NewRequest("DELETE", u, users) - if err != nil { - return nil, nil, err - } - - issue := &Issue{} - resp, err := s.client.Do(ctx, req, issue) - return issue, resp, err -} diff --git a/vendor/github.com/google/go-github/github/issues_comments.go b/vendor/github.com/google/go-github/github/issues_comments.go deleted file mode 100644 index 70047453add..00000000000 --- a/vendor/github.com/google/go-github/github/issues_comments.go +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// IssueComment represents a comment left on an issue. -type IssueComment struct { - ID *int64 `json:"id,omitempty"` - Body *string `json:"body,omitempty"` - User *User `json:"user,omitempty"` - Reactions *Reactions `json:"reactions,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - IssueURL *string `json:"issue_url,omitempty"` -} - -func (i IssueComment) String() string { - return Stringify(i) -} - -// IssueListCommentsOptions specifies the optional parameters to the -// IssuesService.ListComments method. -type IssueListCommentsOptions struct { - // Sort specifies how to sort comments. Possible values are: created, updated. - Sort string `url:"sort,omitempty"` - - // Direction in which to sort comments. Possible values are: asc, desc. - Direction string `url:"direction,omitempty"` - - // Since filters comments by time. - Since time.Time `url:"since,omitempty"` - - ListOptions -} - -// ListComments lists all comments on the specified issue. Specifying an issue -// number of 0 will return all comments on all issues for the repository. -// -// GitHub API docs: https://developer.github.com/v3/issues/comments/#list-comments-on-an-issue -func (s *IssuesService) ListComments(ctx context.Context, owner string, repo string, number int, opt *IssueListCommentsOptions) ([]*IssueComment, *Response, error) { - var u string - if number == 0 { - u = fmt.Sprintf("repos/%v/%v/issues/comments", owner, repo) - } else { - u = fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number) - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - var comments []*IssueComment - resp, err := s.client.Do(ctx, req, &comments) - if err != nil { - return nil, resp, err - } - - return comments, resp, nil -} - -// GetComment fetches the specified issue comment. -// -// GitHub API docs: https://developer.github.com/v3/issues/comments/#get-a-single-comment -func (s *IssuesService) GetComment(ctx context.Context, owner string, repo string, id int) (*IssueComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - comment := new(IssueComment) - resp, err := s.client.Do(ctx, req, comment) - if err != nil { - return nil, resp, err - } - - return comment, resp, nil -} - -// CreateComment creates a new comment on the specified issue. -// -// GitHub API docs: https://developer.github.com/v3/issues/comments/#create-a-comment -func (s *IssuesService) CreateComment(ctx context.Context, owner string, repo string, number int, comment *IssueComment) (*IssueComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/comments", owner, repo, number) - req, err := s.client.NewRequest("POST", u, comment) - if err != nil { - return nil, nil, err - } - c := new(IssueComment) - resp, err := s.client.Do(ctx, req, c) - if err != nil { - return nil, resp, err - } - - return c, resp, nil -} - -// EditComment updates an issue comment. -// -// GitHub API docs: https://developer.github.com/v3/issues/comments/#edit-a-comment -func (s *IssuesService) EditComment(ctx context.Context, owner string, repo string, id int, comment *IssueComment) (*IssueComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id) - req, err := s.client.NewRequest("PATCH", u, comment) - if err != nil { - return nil, nil, err - } - c := new(IssueComment) - resp, err := s.client.Do(ctx, req, c) - if err != nil { - return nil, resp, err - } - - return c, resp, nil -} - -// DeleteComment deletes an issue comment. -// -// GitHub API docs: https://developer.github.com/v3/issues/comments/#delete-a-comment -func (s *IssuesService) DeleteComment(ctx context.Context, owner string, repo string, id int) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/comments/%d", owner, repo, id) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/issues_events.go b/vendor/github.com/google/go-github/github/issues_events.go deleted file mode 100644 index 55e6d431b30..00000000000 --- a/vendor/github.com/google/go-github/github/issues_events.go +++ /dev/null @@ -1,151 +0,0 @@ -// Copyright 2014 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// IssueEvent represents an event that occurred around an Issue or Pull Request. -type IssueEvent struct { - ID *int64 `json:"id,omitempty"` - URL *string `json:"url,omitempty"` - - // The User that generated this event. - Actor *User `json:"actor,omitempty"` - - // Event identifies the actual type of Event that occurred. Possible - // values are: - // - // closed - // The Actor closed the issue. - // If the issue was closed by commit message, CommitID holds the SHA1 hash of the commit. - // - // merged - // The Actor merged into master a branch containing a commit mentioning the issue. - // CommitID holds the SHA1 of the merge commit. - // - // referenced - // The Actor committed to master a commit mentioning the issue in its commit message. - // CommitID holds the SHA1 of the commit. - // - // reopened, locked, unlocked - // The Actor did that to the issue. - // - // renamed - // The Actor changed the issue title from Rename.From to Rename.To. - // - // mentioned - // Someone unspecified @mentioned the Actor [sic] in an issue comment body. - // - // assigned, unassigned - // The Assigner assigned the issue to or removed the assignment from the Assignee. - // - // labeled, unlabeled - // The Actor added or removed the Label from the issue. - // - // milestoned, demilestoned - // The Actor added or removed the issue from the Milestone. - // - // subscribed, unsubscribed - // The Actor subscribed to or unsubscribed from notifications for an issue. - // - // head_ref_deleted, head_ref_restored - // The pull request’s branch was deleted or restored. - // - Event *string `json:"event,omitempty"` - - CreatedAt *time.Time `json:"created_at,omitempty"` - Issue *Issue `json:"issue,omitempty"` - - // Only present on certain events; see above. - Assignee *User `json:"assignee,omitempty"` - Assigner *User `json:"assigner,omitempty"` - CommitID *string `json:"commit_id,omitempty"` - Milestone *Milestone `json:"milestone,omitempty"` - Label *Label `json:"label,omitempty"` - Rename *Rename `json:"rename,omitempty"` -} - -// ListIssueEvents lists events for the specified issue. -// -// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-an-issue -func (s *IssuesService) ListIssueEvents(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*IssueEvent, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%v/events", owner, repo, number) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var events []*IssueEvent - resp, err := s.client.Do(ctx, req, &events) - if err != nil { - return nil, resp, err - } - - return events, resp, nil -} - -// ListRepositoryEvents lists events for the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/issues/events/#list-events-for-a-repository -func (s *IssuesService) ListRepositoryEvents(ctx context.Context, owner, repo string, opt *ListOptions) ([]*IssueEvent, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/events", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var events []*IssueEvent - resp, err := s.client.Do(ctx, req, &events) - if err != nil { - return nil, resp, err - } - - return events, resp, nil -} - -// GetEvent returns the specified issue event. -// -// GitHub API docs: https://developer.github.com/v3/issues/events/#get-a-single-event -func (s *IssuesService) GetEvent(ctx context.Context, owner, repo string, id int64) (*IssueEvent, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/events/%v", owner, repo, id) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - event := new(IssueEvent) - resp, err := s.client.Do(ctx, req, event) - if err != nil { - return nil, resp, err - } - - return event, resp, nil -} - -// Rename contains details for 'renamed' events. -type Rename struct { - From *string `json:"from,omitempty"` - To *string `json:"to,omitempty"` -} - -func (r Rename) String() string { - return Stringify(r) -} diff --git a/vendor/github.com/google/go-github/github/issues_labels.go b/vendor/github.com/google/go-github/github/issues_labels.go deleted file mode 100644 index aacf7d7c21c..00000000000 --- a/vendor/github.com/google/go-github/github/issues_labels.go +++ /dev/null @@ -1,251 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// Label represents a GitHub label on an Issue -type Label struct { - ID *int64 `json:"id,omitempty"` - URL *string `json:"url,omitempty"` - Name *string `json:"name,omitempty"` - Color *string `json:"color,omitempty"` - NodeID *string `json:"node_id,omitempty"` -} - -func (l Label) String() string { - return Stringify(l) -} - -// ListLabels lists all labels for a repository. -// -// GitHub API docs: https://developer.github.com/v3/issues/labels/#list-all-labels-for-this-repository -func (s *IssuesService) ListLabels(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Label, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/labels", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var labels []*Label - resp, err := s.client.Do(ctx, req, &labels) - if err != nil { - return nil, resp, err - } - - return labels, resp, nil -} - -// GetLabel gets a single label. -// -// GitHub API docs: https://developer.github.com/v3/issues/labels/#get-a-single-label -func (s *IssuesService) GetLabel(ctx context.Context, owner string, repo string, name string) (*Label, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - label := new(Label) - resp, err := s.client.Do(ctx, req, label) - if err != nil { - return nil, resp, err - } - - return label, resp, nil -} - -// CreateLabel creates a new label on the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/issues/labels/#create-a-label -func (s *IssuesService) CreateLabel(ctx context.Context, owner string, repo string, label *Label) (*Label, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/labels", owner, repo) - req, err := s.client.NewRequest("POST", u, label) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - l := new(Label) - resp, err := s.client.Do(ctx, req, l) - if err != nil { - return nil, resp, err - } - - return l, resp, nil -} - -// EditLabel edits a label. -// -// GitHub API docs: https://developer.github.com/v3/issues/labels/#update-a-label -func (s *IssuesService) EditLabel(ctx context.Context, owner string, repo string, name string, label *Label) (*Label, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) - req, err := s.client.NewRequest("PATCH", u, label) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - l := new(Label) - resp, err := s.client.Do(ctx, req, l) - if err != nil { - return nil, resp, err - } - - return l, resp, nil -} - -// DeleteLabel deletes a label. -// -// GitHub API docs: https://developer.github.com/v3/issues/labels/#delete-a-label -func (s *IssuesService) DeleteLabel(ctx context.Context, owner string, repo string, name string) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/labels/%v", owner, repo, name) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} - -// ListLabelsByIssue lists all labels for an issue. -// -// GitHub API docs: https://developer.github.com/v3/issues/labels/#list-labels-on-an-issue -func (s *IssuesService) ListLabelsByIssue(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*Label, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var labels []*Label - resp, err := s.client.Do(ctx, req, &labels) - if err != nil { - return nil, resp, err - } - - return labels, resp, nil -} - -// AddLabelsToIssue adds labels to an issue. -// -// GitHub API docs: https://developer.github.com/v3/issues/labels/#add-labels-to-an-issue -func (s *IssuesService) AddLabelsToIssue(ctx context.Context, owner string, repo string, number int, labels []string) ([]*Label, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) - req, err := s.client.NewRequest("POST", u, labels) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var l []*Label - resp, err := s.client.Do(ctx, req, &l) - if err != nil { - return nil, resp, err - } - - return l, resp, nil -} - -// RemoveLabelForIssue removes a label for an issue. -// -// GitHub API docs: https://developer.github.com/v3/issues/labels/#remove-a-label-from-an-issue -func (s *IssuesService) RemoveLabelForIssue(ctx context.Context, owner string, repo string, number int, label string) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/labels/%v", owner, repo, number, label) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} - -// ReplaceLabelsForIssue replaces all labels for an issue. -// -// GitHub API docs: https://developer.github.com/v3/issues/labels/#replace-all-labels-for-an-issue -func (s *IssuesService) ReplaceLabelsForIssue(ctx context.Context, owner string, repo string, number int, labels []string) ([]*Label, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) - req, err := s.client.NewRequest("PUT", u, labels) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var l []*Label - resp, err := s.client.Do(ctx, req, &l) - if err != nil { - return nil, resp, err - } - - return l, resp, nil -} - -// RemoveLabelsForIssue removes all labels for an issue. -// -// GitHub API docs: https://developer.github.com/v3/issues/labels/#remove-all-labels-from-an-issue -func (s *IssuesService) RemoveLabelsForIssue(ctx context.Context, owner string, repo string, number int) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%d/labels", owner, repo, number) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} - -// ListLabelsForMilestone lists labels for every issue in a milestone. -// -// GitHub API docs: https://developer.github.com/v3/issues/labels/#get-labels-for-every-issue-in-a-milestone -func (s *IssuesService) ListLabelsForMilestone(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*Label, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/milestones/%d/labels", owner, repo, number) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var labels []*Label - resp, err := s.client.Do(ctx, req, &labels) - if err != nil { - return nil, resp, err - } - - return labels, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/issues_milestones.go b/vendor/github.com/google/go-github/github/issues_milestones.go deleted file mode 100644 index 6af1cc03c4a..00000000000 --- a/vendor/github.com/google/go-github/github/issues_milestones.go +++ /dev/null @@ -1,160 +0,0 @@ -// Copyright 2014 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// Milestone represents a GitHub repository milestone. -type Milestone struct { - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - LabelsURL *string `json:"labels_url,omitempty"` - ID *int64 `json:"id,omitempty"` - Number *int `json:"number,omitempty"` - State *string `json:"state,omitempty"` - Title *string `json:"title,omitempty"` - Description *string `json:"description,omitempty"` - Creator *User `json:"creator,omitempty"` - OpenIssues *int `json:"open_issues,omitempty"` - ClosedIssues *int `json:"closed_issues,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - ClosedAt *time.Time `json:"closed_at,omitempty"` - DueOn *time.Time `json:"due_on,omitempty"` - NodeID *string `json:"node_id,omitempty"` -} - -func (m Milestone) String() string { - return Stringify(m) -} - -// MilestoneListOptions specifies the optional parameters to the -// IssuesService.ListMilestones method. -type MilestoneListOptions struct { - // State filters milestones based on their state. Possible values are: - // open, closed, all. Default is "open". - State string `url:"state,omitempty"` - - // Sort specifies how to sort milestones. Possible values are: due_on, completeness. - // Default value is "due_on". - Sort string `url:"sort,omitempty"` - - // Direction in which to sort milestones. Possible values are: asc, desc. - // Default is "asc". - Direction string `url:"direction,omitempty"` - - ListOptions -} - -// ListMilestones lists all milestones for a repository. -// -// GitHub API docs: https://developer.github.com/v3/issues/milestones/#list-milestones-for-a-repository -func (s *IssuesService) ListMilestones(ctx context.Context, owner string, repo string, opt *MilestoneListOptions) ([]*Milestone, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var milestones []*Milestone - resp, err := s.client.Do(ctx, req, &milestones) - if err != nil { - return nil, resp, err - } - - return milestones, resp, nil -} - -// GetMilestone gets a single milestone. -// -// GitHub API docs: https://developer.github.com/v3/issues/milestones/#get-a-single-milestone -func (s *IssuesService) GetMilestone(ctx context.Context, owner string, repo string, number int) (*Milestone, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - milestone := new(Milestone) - resp, err := s.client.Do(ctx, req, milestone) - if err != nil { - return nil, resp, err - } - - return milestone, resp, nil -} - -// CreateMilestone creates a new milestone on the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/issues/milestones/#create-a-milestone -func (s *IssuesService) CreateMilestone(ctx context.Context, owner string, repo string, milestone *Milestone) (*Milestone, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/milestones", owner, repo) - req, err := s.client.NewRequest("POST", u, milestone) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - m := new(Milestone) - resp, err := s.client.Do(ctx, req, m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// EditMilestone edits a milestone. -// -// GitHub API docs: https://developer.github.com/v3/issues/milestones/#update-a-milestone -func (s *IssuesService) EditMilestone(ctx context.Context, owner string, repo string, number int, milestone *Milestone) (*Milestone, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) - req, err := s.client.NewRequest("PATCH", u, milestone) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - m := new(Milestone) - resp, err := s.client.Do(ctx, req, m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// DeleteMilestone deletes a milestone. -// -// GitHub API docs: https://developer.github.com/v3/issues/milestones/#delete-a-milestone -func (s *IssuesService) DeleteMilestone(ctx context.Context, owner string, repo string, number int) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/milestones/%d", owner, repo, number) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/issues_timeline.go b/vendor/github.com/google/go-github/github/issues_timeline.go deleted file mode 100644 index 9cfda832026..00000000000 --- a/vendor/github.com/google/go-github/github/issues_timeline.go +++ /dev/null @@ -1,149 +0,0 @@ -// Copyright 2016 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// Timeline represents an event that occurred around an Issue or Pull Request. -// -// It is similar to an IssueEvent but may contain more information. -// GitHub API docs: https://developer.github.com/v3/issues/timeline/ -type Timeline struct { - ID *int64 `json:"id,omitempty"` - URL *string `json:"url,omitempty"` - CommitURL *string `json:"commit_url,omitempty"` - - // The User object that generated the event. - Actor *User `json:"actor,omitempty"` - - // Event identifies the actual type of Event that occurred. Possible values - // are: - // - // assigned - // The issue was assigned to the assignee. - // - // closed - // The issue was closed by the actor. When the commit_id is present, it - // identifies the commit that closed the issue using "closes / fixes #NN" - // syntax. - // - // commented - // A comment was added to the issue. - // - // committed - // A commit was added to the pull request's 'HEAD' branch. Only provided - // for pull requests. - // - // cross-referenced - // The issue was referenced from another issue. The 'source' attribute - // contains the 'id', 'actor', and 'url' of the reference's source. - // - // demilestoned - // The issue was removed from a milestone. - // - // head_ref_deleted - // The pull request's branch was deleted. - // - // head_ref_restored - // The pull request's branch was restored. - // - // labeled - // A label was added to the issue. - // - // locked - // The issue was locked by the actor. - // - // mentioned - // The actor was @mentioned in an issue body. - // - // merged - // The issue was merged by the actor. The 'commit_id' attribute is the - // SHA1 of the HEAD commit that was merged. - // - // milestoned - // The issue was added to a milestone. - // - // referenced - // The issue was referenced from a commit message. The 'commit_id' - // attribute is the commit SHA1 of where that happened. - // - // renamed - // The issue title was changed. - // - // reopened - // The issue was reopened by the actor. - // - // subscribed - // The actor subscribed to receive notifications for an issue. - // - // unassigned - // The assignee was unassigned from the issue. - // - // unlabeled - // A label was removed from the issue. - // - // unlocked - // The issue was unlocked by the actor. - // - // unsubscribed - // The actor unsubscribed to stop receiving notifications for an issue. - // - Event *string `json:"event,omitempty"` - - // The string SHA of a commit that referenced this Issue or Pull Request. - CommitID *string `json:"commit_id,omitempty"` - // The timestamp indicating when the event occurred. - CreatedAt *time.Time `json:"created_at,omitempty"` - // The Label object including `name` and `color` attributes. Only provided for - // 'labeled' and 'unlabeled' events. - Label *Label `json:"label,omitempty"` - // The User object which was assigned to (or unassigned from) this Issue or - // Pull Request. Only provided for 'assigned' and 'unassigned' events. - Assignee *User `json:"assignee,omitempty"` - // The Milestone object including a 'title' attribute. - // Only provided for 'milestoned' and 'demilestoned' events. - Milestone *Milestone `json:"milestone,omitempty"` - // The 'id', 'actor', and 'url' for the source of a reference from another issue. - // Only provided for 'cross-referenced' events. - Source *Source `json:"source,omitempty"` - // An object containing rename details including 'from' and 'to' attributes. - // Only provided for 'renamed' events. - Rename *Rename `json:"rename,omitempty"` -} - -// Source represents a reference's source. -type Source struct { - ID *int64 `json:"id,omitempty"` - URL *string `json:"url,omitempty"` - Actor *User `json:"actor,omitempty"` -} - -// ListIssueTimeline lists events for the specified issue. -// -// GitHub API docs: https://developer.github.com/v3/issues/timeline/#list-events-for-an-issue -func (s *IssuesService) ListIssueTimeline(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*Timeline, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%v/timeline", owner, repo, number) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTimelinePreview) - - var events []*Timeline - resp, err := s.client.Do(ctx, req, &events) - return events, resp, err -} diff --git a/vendor/github.com/google/go-github/github/licenses.go b/vendor/github.com/google/go-github/github/licenses.go deleted file mode 100644 index e9cd1777afb..00000000000 --- a/vendor/github.com/google/go-github/github/licenses.go +++ /dev/null @@ -1,103 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// LicensesService handles communication with the license related -// methods of the GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/licenses/ -type LicensesService service - -// RepositoryLicense represents the license for a repository. -type RepositoryLicense struct { - Name *string `json:"name,omitempty"` - Path *string `json:"path,omitempty"` - - SHA *string `json:"sha,omitempty"` - Size *int `json:"size,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - GitURL *string `json:"git_url,omitempty"` - DownloadURL *string `json:"download_url,omitempty"` - Type *string `json:"type,omitempty"` - Content *string `json:"content,omitempty"` - Encoding *string `json:"encoding,omitempty"` - License *License `json:"license,omitempty"` -} - -func (l RepositoryLicense) String() string { - return Stringify(l) -} - -// License represents an open source license. -type License struct { - Key *string `json:"key,omitempty"` - Name *string `json:"name,omitempty"` - URL *string `json:"url,omitempty"` - - SPDXID *string `json:"spdx_id,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - Featured *bool `json:"featured,omitempty"` - Description *string `json:"description,omitempty"` - Implementation *string `json:"implementation,omitempty"` - Permissions *[]string `json:"permissions,omitempty"` - Conditions *[]string `json:"conditions,omitempty"` - Limitations *[]string `json:"limitations,omitempty"` - Body *string `json:"body,omitempty"` -} - -func (l License) String() string { - return Stringify(l) -} - -// List popular open source licenses. -// -// GitHub API docs: https://developer.github.com/v3/licenses/#list-all-licenses -func (s *LicensesService) List(ctx context.Context) ([]*License, *Response, error) { - req, err := s.client.NewRequest("GET", "licenses", nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeLicensesPreview) - - var licenses []*License - resp, err := s.client.Do(ctx, req, &licenses) - if err != nil { - return nil, resp, err - } - - return licenses, resp, nil -} - -// Get extended metadata for one license. -// -// GitHub API docs: https://developer.github.com/v3/licenses/#get-an-individual-license -func (s *LicensesService) Get(ctx context.Context, licenseName string) (*License, *Response, error) { - u := fmt.Sprintf("licenses/%s", licenseName) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeLicensesPreview) - - license := new(License) - resp, err := s.client.Do(ctx, req, license) - if err != nil { - return nil, resp, err - } - - return license, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/messages.go b/vendor/github.com/google/go-github/github/messages.go deleted file mode 100644 index 2396fd4314e..00000000000 --- a/vendor/github.com/google/go-github/github/messages.go +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright 2016 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// This file provides functions for validating payloads from GitHub Webhooks. -// GitHub API docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github - -package github - -import ( - "crypto/hmac" - "crypto/sha1" - "crypto/sha256" - "crypto/sha512" - "encoding/hex" - "encoding/json" - "errors" - "fmt" - "hash" - "io/ioutil" - "net/http" - "net/url" - "strings" -) - -const ( - // sha1Prefix is the prefix used by GitHub before the HMAC hexdigest. - sha1Prefix = "sha1" - // sha256Prefix and sha512Prefix are provided for future compatibility. - sha256Prefix = "sha256" - sha512Prefix = "sha512" - // signatureHeader is the GitHub header key used to pass the HMAC hexdigest. - signatureHeader = "X-Hub-Signature" - // eventTypeHeader is the GitHub header key used to pass the event type. - eventTypeHeader = "X-Github-Event" - // deliveryIDHeader is the GitHub header key used to pass the unique ID for the webhook event. - deliveryIDHeader = "X-Github-Delivery" -) - -var ( - // eventTypeMapping maps webhooks types to their corresponding go-github struct types. - eventTypeMapping = map[string]string{ - "commit_comment": "CommitCommentEvent", - "create": "CreateEvent", - "delete": "DeleteEvent", - "deployment": "DeploymentEvent", - "deployment_status": "DeploymentStatusEvent", - "fork": "ForkEvent", - "gollum": "GollumEvent", - "installation": "InstallationEvent", - "installation_repositories": "InstallationRepositoriesEvent", - "issue_comment": "IssueCommentEvent", - "issues": "IssuesEvent", - "label": "LabelEvent", - "marketplace_purchase": "MarketplacePurchaseEvent", - "member": "MemberEvent", - "membership": "MembershipEvent", - "milestone": "MilestoneEvent", - "organization": "OrganizationEvent", - "org_block": "OrgBlockEvent", - "page_build": "PageBuildEvent", - "ping": "PingEvent", - "project": "ProjectEvent", - "project_card": "ProjectCardEvent", - "project_column": "ProjectColumnEvent", - "public": "PublicEvent", - "pull_request_review": "PullRequestReviewEvent", - "pull_request_review_comment": "PullRequestReviewCommentEvent", - "pull_request": "PullRequestEvent", - "push": "PushEvent", - "repository": "RepositoryEvent", - "release": "ReleaseEvent", - "status": "StatusEvent", - "team": "TeamEvent", - "team_add": "TeamAddEvent", - "watch": "WatchEvent", - } -) - -// genMAC generates the HMAC signature for a message provided the secret key -// and hashFunc. -func genMAC(message, key []byte, hashFunc func() hash.Hash) []byte { - mac := hmac.New(hashFunc, key) - mac.Write(message) - return mac.Sum(nil) -} - -// checkMAC reports whether messageMAC is a valid HMAC tag for message. -func checkMAC(message, messageMAC, key []byte, hashFunc func() hash.Hash) bool { - expectedMAC := genMAC(message, key, hashFunc) - return hmac.Equal(messageMAC, expectedMAC) -} - -// messageMAC returns the hex-decoded HMAC tag from the signature and its -// corresponding hash function. -func messageMAC(signature string) ([]byte, func() hash.Hash, error) { - if signature == "" { - return nil, nil, errors.New("missing signature") - } - sigParts := strings.SplitN(signature, "=", 2) - if len(sigParts) != 2 { - return nil, nil, fmt.Errorf("error parsing signature %q", signature) - } - - var hashFunc func() hash.Hash - switch sigParts[0] { - case sha1Prefix: - hashFunc = sha1.New - case sha256Prefix: - hashFunc = sha256.New - case sha512Prefix: - hashFunc = sha512.New - default: - return nil, nil, fmt.Errorf("unknown hash type prefix: %q", sigParts[0]) - } - - buf, err := hex.DecodeString(sigParts[1]) - if err != nil { - return nil, nil, fmt.Errorf("error decoding signature %q: %v", signature, err) - } - return buf, hashFunc, nil -} - -// ValidatePayload validates an incoming GitHub Webhook event request -// and returns the (JSON) payload. -// The Content-Type header of the payload can be "application/json" or "application/x-www-form-urlencoded". -// If the Content-Type is neither then an error is returned. -// secretKey is the GitHub Webhook secret message. -// -// Example usage: -// -// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { -// payload, err := github.ValidatePayload(r, s.webhookSecretKey) -// if err != nil { ... } -// // Process payload... -// } -// -func ValidatePayload(r *http.Request, secretKey []byte) (payload []byte, err error) { - var body []byte // Raw body that GitHub uses to calculate the signature. - - switch ct := r.Header.Get("Content-Type"); ct { - case "application/json": - var err error - if body, err = ioutil.ReadAll(r.Body); err != nil { - return nil, err - } - - // If the content type is application/json, - // the JSON payload is just the original body. - payload = body - - case "application/x-www-form-urlencoded": - // payloadFormParam is the name of the form parameter that the JSON payload - // will be in if a webhook has its content type set to application/x-www-form-urlencoded. - const payloadFormParam = "payload" - - var err error - if body, err = ioutil.ReadAll(r.Body); err != nil { - return nil, err - } - - // If the content type is application/x-www-form-urlencoded, - // the JSON payload will be under the "payload" form param. - form, err := url.ParseQuery(string(body)) - if err != nil { - return nil, err - } - payload = []byte(form.Get(payloadFormParam)) - - default: - return nil, fmt.Errorf("Webhook request has unsupported Content-Type %q", ct) - } - - sig := r.Header.Get(signatureHeader) - if err := validateSignature(sig, body, secretKey); err != nil { - return nil, err - } - return payload, nil -} - -// validateSignature validates the signature for the given payload. -// signature is the GitHub hash signature delivered in the X-Hub-Signature header. -// payload is the JSON payload sent by GitHub Webhooks. -// secretKey is the GitHub Webhook secret message. -// -// GitHub API docs: https://developer.github.com/webhooks/securing/#validating-payloads-from-github -func validateSignature(signature string, payload, secretKey []byte) error { - messageMAC, hashFunc, err := messageMAC(signature) - if err != nil { - return err - } - if !checkMAC(payload, messageMAC, secretKey, hashFunc) { - return errors.New("payload signature check failed") - } - return nil -} - -// WebHookType returns the event type of webhook request r. -// -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#webhook-headers -func WebHookType(r *http.Request) string { - return r.Header.Get(eventTypeHeader) -} - -// DeliveryID returns the unique delivery ID of webhook request r. -// -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#webhook-headers -func DeliveryID(r *http.Request) string { - return r.Header.Get(deliveryIDHeader) -} - -// ParseWebHook parses the event payload. For recognized event types, a -// value of the corresponding struct type will be returned (as returned -// by Event.ParsePayload()). An error will be returned for unrecognized event -// types. -// -// Example usage: -// -// func (s *GitHubEventMonitor) ServeHTTP(w http.ResponseWriter, r *http.Request) { -// payload, err := github.ValidatePayload(r, s.webhookSecretKey) -// if err != nil { ... } -// event, err := github.ParseWebHook(github.WebHookType(r), payload) -// if err != nil { ... } -// switch event := event.(type) { -// case *github.CommitCommentEvent: -// processCommitCommentEvent(event) -// case *github.CreateEvent: -// processCreateEvent(event) -// ... -// } -// } -// -func ParseWebHook(messageType string, payload []byte) (interface{}, error) { - eventType, ok := eventTypeMapping[messageType] - if !ok { - return nil, fmt.Errorf("unknown X-Github-Event in message: %v", messageType) - } - - event := Event{ - Type: &eventType, - RawPayload: (*json.RawMessage)(&payload), - } - return event.ParsePayload() -} diff --git a/vendor/github.com/google/go-github/github/migrations.go b/vendor/github.com/google/go-github/github/migrations.go deleted file mode 100644 index 90cc1fae85f..00000000000 --- a/vendor/github.com/google/go-github/github/migrations.go +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2016 The go-github 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 github - -import ( - "context" - "errors" - "fmt" - "net/http" - "strings" -) - -// MigrationService provides access to the migration related functions -// in the GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/migration/ -type MigrationService service - -// Migration represents a GitHub migration (archival). -type Migration struct { - ID *int64 `json:"id,omitempty"` - GUID *string `json:"guid,omitempty"` - // State is the current state of a migration. - // Possible values are: - // "pending" which means the migration hasn't started yet, - // "exporting" which means the migration is in progress, - // "exported" which means the migration finished successfully, or - // "failed" which means the migration failed. - State *string `json:"state,omitempty"` - // LockRepositories indicates whether repositories are locked (to prevent - // manipulation) while migrating data. - LockRepositories *bool `json:"lock_repositories,omitempty"` - // ExcludeAttachments indicates whether attachments should be excluded from - // the migration (to reduce migration archive file size). - ExcludeAttachments *bool `json:"exclude_attachments,omitempty"` - URL *string `json:"url,omitempty"` - CreatedAt *string `json:"created_at,omitempty"` - UpdatedAt *string `json:"updated_at,omitempty"` - Repositories []*Repository `json:"repositories,omitempty"` -} - -func (m Migration) String() string { - return Stringify(m) -} - -// MigrationOptions specifies the optional parameters to Migration methods. -type MigrationOptions struct { - // LockRepositories indicates whether repositories should be locked (to prevent - // manipulation) while migrating data. - LockRepositories bool - - // ExcludeAttachments indicates whether attachments should be excluded from - // the migration (to reduce migration archive file size). - ExcludeAttachments bool -} - -// startMigration represents the body of a StartMigration request. -type startMigration struct { - // Repositories is a slice of repository names to migrate. - Repositories []string `json:"repositories,omitempty"` - - // LockRepositories indicates whether repositories should be locked (to prevent - // manipulation) while migrating data. - LockRepositories *bool `json:"lock_repositories,omitempty"` - - // ExcludeAttachments indicates whether attachments should be excluded from - // the migration (to reduce migration archive file size). - ExcludeAttachments *bool `json:"exclude_attachments,omitempty"` -} - -// StartMigration starts the generation of a migration archive. -// repos is a slice of repository names to migrate. -// -// GitHub API docs: https://developer.github.com/v3/migration/migrations/#start-a-migration -func (s *MigrationService) StartMigration(ctx context.Context, org string, repos []string, opt *MigrationOptions) (*Migration, *Response, error) { - u := fmt.Sprintf("orgs/%v/migrations", org) - - body := &startMigration{Repositories: repos} - if opt != nil { - body.LockRepositories = Bool(opt.LockRepositories) - body.ExcludeAttachments = Bool(opt.ExcludeAttachments) - } - - req, err := s.client.NewRequest("POST", u, body) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeMigrationsPreview) - - m := &Migration{} - resp, err := s.client.Do(ctx, req, m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// ListMigrations lists the most recent migrations. -// -// GitHub API docs: https://developer.github.com/v3/migration/migrations/#get-a-list-of-migrations -func (s *MigrationService) ListMigrations(ctx context.Context, org string) ([]*Migration, *Response, error) { - u := fmt.Sprintf("orgs/%v/migrations", org) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeMigrationsPreview) - - var m []*Migration - resp, err := s.client.Do(ctx, req, &m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// MigrationStatus gets the status of a specific migration archive. -// id is the migration ID. -// -// GitHub API docs: https://developer.github.com/v3/migration/migrations/#get-the-status-of-a-migration -func (s *MigrationService) MigrationStatus(ctx context.Context, org string, id int64) (*Migration, *Response, error) { - u := fmt.Sprintf("orgs/%v/migrations/%v", org, id) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeMigrationsPreview) - - m := &Migration{} - resp, err := s.client.Do(ctx, req, m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// MigrationArchiveURL fetches a migration archive URL. -// id is the migration ID. -// -// GitHub API docs: https://developer.github.com/v3/migration/migrations/#download-a-migration-archive -func (s *MigrationService) MigrationArchiveURL(ctx context.Context, org string, id int64) (url string, err error) { - u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, id) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return "", err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeMigrationsPreview) - - s.client.clientMu.Lock() - defer s.client.clientMu.Unlock() - - // Disable the redirect mechanism because AWS fails if the GitHub auth token is provided. - var loc string - saveRedirect := s.client.client.CheckRedirect - s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error { - loc = req.URL.String() - return errors.New("disable redirect") - } - defer func() { s.client.client.CheckRedirect = saveRedirect }() - - _, err = s.client.Do(ctx, req, nil) // expect error from disable redirect - if err == nil { - return "", errors.New("expected redirect, none provided") - } - if !strings.Contains(err.Error(), "disable redirect") { - return "", err - } - return loc, nil -} - -// DeleteMigration deletes a previous migration archive. -// id is the migration ID. -// -// GitHub API docs: https://developer.github.com/v3/migration/migrations/#delete-a-migration-archive -func (s *MigrationService) DeleteMigration(ctx context.Context, org string, id int64) (*Response, error) { - u := fmt.Sprintf("orgs/%v/migrations/%v/archive", org, id) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeMigrationsPreview) - - return s.client.Do(ctx, req, nil) -} - -// UnlockRepo unlocks a repository that was locked for migration. -// id is the migration ID. -// You should unlock each migrated repository and delete them when the migration -// is complete and you no longer need the source data. -// -// GitHub API docs: https://developer.github.com/v3/migration/migrations/#unlock-a-repository -func (s *MigrationService) UnlockRepo(ctx context.Context, org string, id int64, repo string) (*Response, error) { - u := fmt.Sprintf("orgs/%v/migrations/%v/repos/%v/lock", org, id, repo) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeMigrationsPreview) - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/migrations_source_import.go b/vendor/github.com/google/go-github/github/migrations_source_import.go deleted file mode 100644 index fd45e780065..00000000000 --- a/vendor/github.com/google/go-github/github/migrations_source_import.go +++ /dev/null @@ -1,329 +0,0 @@ -// Copyright 2016 The go-github 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 github - -import ( - "context" - "fmt" -) - -// Import represents a repository import request. -type Import struct { - // The URL of the originating repository. - VCSURL *string `json:"vcs_url,omitempty"` - // The originating VCS type. Can be one of 'subversion', 'git', - // 'mercurial', or 'tfvc'. Without this parameter, the import job will - // take additional time to detect the VCS type before beginning the - // import. This detection step will be reflected in the response. - VCS *string `json:"vcs,omitempty"` - // VCSUsername and VCSPassword are only used for StartImport calls that - // are importing a password-protected repository. - VCSUsername *string `json:"vcs_username,omitempty"` - VCSPassword *string `json:"vcs_password,omitempty"` - // For a tfvc import, the name of the project that is being imported. - TFVCProject *string `json:"tfvc_project,omitempty"` - - // LFS related fields that may be preset in the Import Progress response - - // Describes whether the import has been opted in or out of using Git - // LFS. The value can be 'opt_in', 'opt_out', or 'undecided' if no - // action has been taken. - UseLFS *string `json:"use_lfs,omitempty"` - // Describes whether files larger than 100MB were found during the - // importing step. - HasLargeFiles *bool `json:"has_large_files,omitempty"` - // The total size in gigabytes of files larger than 100MB found in the - // originating repository. - LargeFilesSize *int `json:"large_files_size,omitempty"` - // The total number of files larger than 100MB found in the originating - // repository. To see a list of these files, call LargeFiles. - LargeFilesCount *int `json:"large_files_count,omitempty"` - - // Identifies the current status of an import. An import that does not - // have errors will progress through these steps: - // - // detecting - the "detection" step of the import is in progress - // because the request did not include a VCS parameter. The - // import is identifying the type of source control present at - // the URL. - // importing - the "raw" step of the import is in progress. This is - // where commit data is fetched from the original repository. - // The import progress response will include CommitCount (the - // total number of raw commits that will be imported) and - // Percent (0 - 100, the current progress through the import). - // mapping - the "rewrite" step of the import is in progress. This - // is where SVN branches are converted to Git branches, and - // where author updates are applied. The import progress - // response does not include progress information. - // pushing - the "push" step of the import is in progress. This is - // where the importer updates the repository on GitHub. The - // import progress response will include PushPercent, which is - // the percent value reported by git push when it is "Writing - // objects". - // complete - the import is complete, and the repository is ready - // on GitHub. - // - // If there are problems, you will see one of these in the status field: - // - // auth_failed - the import requires authentication in order to - // connect to the original repository. Make an UpdateImport - // request, and include VCSUsername and VCSPassword. - // error - the import encountered an error. The import progress - // response will include the FailedStep and an error message. - // Contact GitHub support for more information. - // detection_needs_auth - the importer requires authentication for - // the originating repository to continue detection. Make an - // UpdatImport request, and include VCSUsername and - // VCSPassword. - // detection_found_nothing - the importer didn't recognize any - // source control at the URL. - // detection_found_multiple - the importer found several projects - // or repositories at the provided URL. When this is the case, - // the Import Progress response will also include a - // ProjectChoices field with the possible project choices as - // values. Make an UpdateImport request, and include VCS and - // (if applicable) TFVCProject. - Status *string `json:"status,omitempty"` - CommitCount *int `json:"commit_count,omitempty"` - StatusText *string `json:"status_text,omitempty"` - AuthorsCount *int `json:"authors_count,omitempty"` - Percent *int `json:"percent,omitempty"` - PushPercent *int `json:"push_percent,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - AuthorsURL *string `json:"authors_url,omitempty"` - RepositoryURL *string `json:"repository_url,omitempty"` - Message *string `json:"message,omitempty"` - FailedStep *string `json:"failed_step,omitempty"` - - // Human readable display name, provided when the Import appears as - // part of ProjectChoices. - HumanName *string `json:"human_name,omitempty"` - - // When the importer finds several projects or repositories at the - // provided URLs, this will identify the available choices. Call - // UpdateImport with the selected Import value. - ProjectChoices []Import `json:"project_choices,omitempty"` -} - -func (i Import) String() string { - return Stringify(i) -} - -// SourceImportAuthor identifies an author imported from a source repository. -// -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-commit-authors -type SourceImportAuthor struct { - ID *int64 `json:"id,omitempty"` - RemoteID *string `json:"remote_id,omitempty"` - RemoteName *string `json:"remote_name,omitempty"` - Email *string `json:"email,omitempty"` - Name *string `json:"name,omitempty"` - URL *string `json:"url,omitempty"` - ImportURL *string `json:"import_url,omitempty"` -} - -func (a SourceImportAuthor) String() string { - return Stringify(a) -} - -// LargeFile identifies a file larger than 100MB found during a repository import. -// -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-large-files -type LargeFile struct { - RefName *string `json:"ref_name,omitempty"` - Path *string `json:"path,omitempty"` - OID *string `json:"oid,omitempty"` - Size *int `json:"size,omitempty"` -} - -func (f LargeFile) String() string { - return Stringify(f) -} - -// StartImport initiates a repository import. -// -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#start-an-import -func (s *MigrationService) StartImport(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/import", owner, repo) - req, err := s.client.NewRequest("PUT", u, in) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - out := new(Import) - resp, err := s.client.Do(ctx, req, out) - if err != nil { - return nil, resp, err - } - - return out, resp, nil -} - -// ImportProgress queries for the status and progress of an ongoing repository import. -// -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-import-progress -func (s *MigrationService) ImportProgress(ctx context.Context, owner, repo string) (*Import, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/import", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - out := new(Import) - resp, err := s.client.Do(ctx, req, out) - if err != nil { - return nil, resp, err - } - - return out, resp, nil -} - -// UpdateImport initiates a repository import. -// -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#update-existing-import -func (s *MigrationService) UpdateImport(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/import", owner, repo) - req, err := s.client.NewRequest("PATCH", u, in) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - out := new(Import) - resp, err := s.client.Do(ctx, req, out) - if err != nil { - return nil, resp, err - } - - return out, resp, nil -} - -// CommitAuthors gets the authors mapped from the original repository. -// -// Each type of source control system represents authors in a different way. -// For example, a Git commit author has a display name and an email address, -// but a Subversion commit author just has a username. The GitHub Importer will -// make the author information valid, but the author might not be correct. For -// example, it will change the bare Subversion username "hubot" into something -// like "hubot ". -// -// This method and MapCommitAuthor allow you to provide correct Git author -// information. -// -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-commit-authors -func (s *MigrationService) CommitAuthors(ctx context.Context, owner, repo string) ([]*SourceImportAuthor, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/import/authors", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - var authors []*SourceImportAuthor - resp, err := s.client.Do(ctx, req, &authors) - if err != nil { - return nil, resp, err - } - - return authors, resp, nil -} - -// MapCommitAuthor updates an author's identity for the import. Your -// application can continue updating authors any time before you push new -// commits to the repository. -// -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#map-a-commit-author -func (s *MigrationService) MapCommitAuthor(ctx context.Context, owner, repo string, id int64, author *SourceImportAuthor) (*SourceImportAuthor, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/import/authors/%v", owner, repo, id) - req, err := s.client.NewRequest("PATCH", u, author) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - out := new(SourceImportAuthor) - resp, err := s.client.Do(ctx, req, out) - if err != nil { - return nil, resp, err - } - - return out, resp, nil -} - -// SetLFSPreference sets whether imported repositories should use Git LFS for -// files larger than 100MB. Only the UseLFS field on the provided Import is -// used. -// -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#set-git-lfs-preference -func (s *MigrationService) SetLFSPreference(ctx context.Context, owner, repo string, in *Import) (*Import, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/import/lfs", owner, repo) - req, err := s.client.NewRequest("PATCH", u, in) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - out := new(Import) - resp, err := s.client.Do(ctx, req, out) - if err != nil { - return nil, resp, err - } - - return out, resp, nil -} - -// LargeFiles lists files larger than 100MB found during the import. -// -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#get-large-files -func (s *MigrationService) LargeFiles(ctx context.Context, owner, repo string) ([]*LargeFile, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/import/large_files", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - var files []*LargeFile - resp, err := s.client.Do(ctx, req, &files) - if err != nil { - return nil, resp, err - } - - return files, resp, nil -} - -// CancelImport stops an import for a repository. -// -// GitHub API docs: https://developer.github.com/v3/migration/source_imports/#cancel-an-import -func (s *MigrationService) CancelImport(ctx context.Context, owner, repo string) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/import", owner, repo) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeImportPreview) - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/misc.go b/vendor/github.com/google/go-github/github/misc.go deleted file mode 100644 index 5b8082d3ce5..00000000000 --- a/vendor/github.com/google/go-github/github/misc.go +++ /dev/null @@ -1,253 +0,0 @@ -// Copyright 2014 The go-github 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 github - -import ( - "bytes" - "context" - "fmt" - "net/url" -) - -// MarkdownOptions specifies optional parameters to the Markdown method. -type MarkdownOptions struct { - // Mode identifies the rendering mode. Possible values are: - // markdown - render a document as plain Markdown, just like - // README files are rendered. - // - // gfm - to render a document as user-content, e.g. like user - // comments or issues are rendered. In GFM mode, hard line breaks are - // always taken into account, and issue and user mentions are linked - // accordingly. - // - // Default is "markdown". - Mode string - - // Context identifies the repository context. Only taken into account - // when rendering as "gfm". - Context string -} - -type markdownRequest struct { - Text *string `json:"text,omitempty"` - Mode *string `json:"mode,omitempty"` - Context *string `json:"context,omitempty"` -} - -// Markdown renders an arbitrary Markdown document. -// -// GitHub API docs: https://developer.github.com/v3/markdown/ -func (c *Client) Markdown(ctx context.Context, text string, opt *MarkdownOptions) (string, *Response, error) { - request := &markdownRequest{Text: String(text)} - if opt != nil { - if opt.Mode != "" { - request.Mode = String(opt.Mode) - } - if opt.Context != "" { - request.Context = String(opt.Context) - } - } - - req, err := c.NewRequest("POST", "markdown", request) - if err != nil { - return "", nil, err - } - - buf := new(bytes.Buffer) - resp, err := c.Do(ctx, req, buf) - if err != nil { - return "", resp, err - } - - return buf.String(), resp, nil -} - -// ListEmojis returns the emojis available to use on GitHub. -// -// GitHub API docs: https://developer.github.com/v3/emojis/ -func (c *Client) ListEmojis(ctx context.Context) (map[string]string, *Response, error) { - req, err := c.NewRequest("GET", "emojis", nil) - if err != nil { - return nil, nil, err - } - - var emoji map[string]string - resp, err := c.Do(ctx, req, &emoji) - if err != nil { - return nil, resp, err - } - - return emoji, resp, nil -} - -// CodeOfConduct represents a code of conduct. -type CodeOfConduct struct { - Name *string `json:"name,omitempty"` - Key *string `json:"key,omitempty"` - URL *string `json:"url,omitempty"` - Body *string `json:"body,omitempty"` -} - -func (c *CodeOfConduct) String() string { - return Stringify(c) -} - -// ListCodesOfConduct returns all codes of conduct. -// -// GitHub API docs: https://developer.github.com/v3/codes_of_conduct/#list-all-codes-of-conduct -func (c *Client) ListCodesOfConduct(ctx context.Context) ([]*CodeOfConduct, *Response, error) { - req, err := c.NewRequest("GET", "codes_of_conduct", nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeCodesOfConductPreview) - - var cs []*CodeOfConduct - resp, err := c.Do(ctx, req, &cs) - if err != nil { - return nil, resp, err - } - - return cs, resp, nil -} - -// GetCodeOfConduct returns an individual code of conduct. -// -// https://developer.github.com/v3/codes_of_conduct/#get-an-individual-code-of-conduct -func (c *Client) GetCodeOfConduct(ctx context.Context, key string) (*CodeOfConduct, *Response, error) { - u := fmt.Sprintf("codes_of_conduct/%s", key) - req, err := c.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeCodesOfConductPreview) - - coc := new(CodeOfConduct) - resp, err := c.Do(ctx, req, coc) - if err != nil { - return nil, resp, err - } - - return coc, resp, nil -} - -// APIMeta represents metadata about the GitHub API. -type APIMeta struct { - // An Array of IP addresses in CIDR format specifying the addresses - // that incoming service hooks will originate from on GitHub.com. - Hooks []string `json:"hooks,omitempty"` - - // An Array of IP addresses in CIDR format specifying the Git servers - // for GitHub.com. - Git []string `json:"git,omitempty"` - - // Whether authentication with username and password is supported. - // (GitHub Enterprise instances using CAS or OAuth for authentication - // will return false. Features like Basic Authentication with a - // username and password, sudo mode, and two-factor authentication are - // not supported on these servers.) - VerifiablePasswordAuthentication *bool `json:"verifiable_password_authentication,omitempty"` - - // An array of IP addresses in CIDR format specifying the addresses - // which serve GitHub Pages websites. - Pages []string `json:"pages,omitempty"` -} - -// APIMeta returns information about GitHub.com, the service. Or, if you access -// this endpoint on your organization’s GitHub Enterprise installation, this -// endpoint provides information about that installation. -// -// GitHub API docs: https://developer.github.com/v3/meta/ -func (c *Client) APIMeta(ctx context.Context) (*APIMeta, *Response, error) { - req, err := c.NewRequest("GET", "meta", nil) - if err != nil { - return nil, nil, err - } - - meta := new(APIMeta) - resp, err := c.Do(ctx, req, meta) - if err != nil { - return nil, resp, err - } - - return meta, resp, nil -} - -// Octocat returns an ASCII art octocat with the specified message in a speech -// bubble. If message is empty, a random zen phrase is used. -func (c *Client) Octocat(ctx context.Context, message string) (string, *Response, error) { - u := "octocat" - if message != "" { - u = fmt.Sprintf("%s?s=%s", u, url.QueryEscape(message)) - } - - req, err := c.NewRequest("GET", u, nil) - if err != nil { - return "", nil, err - } - - buf := new(bytes.Buffer) - resp, err := c.Do(ctx, req, buf) - if err != nil { - return "", resp, err - } - - return buf.String(), resp, nil -} - -// Zen returns a random line from The Zen of GitHub. -// -// see also: http://warpspire.com/posts/taste/ -func (c *Client) Zen(ctx context.Context) (string, *Response, error) { - req, err := c.NewRequest("GET", "zen", nil) - if err != nil { - return "", nil, err - } - - buf := new(bytes.Buffer) - resp, err := c.Do(ctx, req, buf) - if err != nil { - return "", resp, err - } - - return buf.String(), resp, nil -} - -// ServiceHook represents a hook that has configuration settings, a list of -// available events, and default events. -type ServiceHook struct { - Name *string `json:"name,omitempty"` - Events []string `json:"events,omitempty"` - SupportedEvents []string `json:"supported_events,omitempty"` - Schema [][]string `json:"schema,omitempty"` -} - -func (s *ServiceHook) String() string { - return Stringify(s) -} - -// ListServiceHooks lists all of the available service hooks. -// -// GitHub API docs: https://developer.github.com/webhooks/#services -func (c *Client) ListServiceHooks(ctx context.Context) ([]*ServiceHook, *Response, error) { - u := "hooks" - req, err := c.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var hooks []*ServiceHook - resp, err := c.Do(ctx, req, &hooks) - if err != nil { - return nil, resp, err - } - - return hooks, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/orgs.go b/vendor/github.com/google/go-github/github/orgs.go deleted file mode 100644 index 976a5fca2f4..00000000000 --- a/vendor/github.com/google/go-github/github/orgs.go +++ /dev/null @@ -1,209 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// OrganizationsService provides access to the organization related functions -// in the GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/orgs/ -type OrganizationsService service - -// Organization represents a GitHub organization account. -type Organization struct { - Login *string `json:"login,omitempty"` - ID *int64 `json:"id,omitempty"` - AvatarURL *string `json:"avatar_url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - Name *string `json:"name,omitempty"` - Company *string `json:"company,omitempty"` - Blog *string `json:"blog,omitempty"` - Location *string `json:"location,omitempty"` - Email *string `json:"email,omitempty"` - Description *string `json:"description,omitempty"` - PublicRepos *int `json:"public_repos,omitempty"` - PublicGists *int `json:"public_gists,omitempty"` - Followers *int `json:"followers,omitempty"` - Following *int `json:"following,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - TotalPrivateRepos *int `json:"total_private_repos,omitempty"` - OwnedPrivateRepos *int `json:"owned_private_repos,omitempty"` - PrivateGists *int `json:"private_gists,omitempty"` - DiskUsage *int `json:"disk_usage,omitempty"` - Collaborators *int `json:"collaborators,omitempty"` - BillingEmail *string `json:"billing_email,omitempty"` - Type *string `json:"type,omitempty"` - Plan *Plan `json:"plan,omitempty"` - NodeID *string `json:"node_id,omitempty"` - - // API URLs - URL *string `json:"url,omitempty"` - EventsURL *string `json:"events_url,omitempty"` - HooksURL *string `json:"hooks_url,omitempty"` - IssuesURL *string `json:"issues_url,omitempty"` - MembersURL *string `json:"members_url,omitempty"` - PublicMembersURL *string `json:"public_members_url,omitempty"` - ReposURL *string `json:"repos_url,omitempty"` -} - -func (o Organization) String() string { - return Stringify(o) -} - -// Plan represents the payment plan for an account. See plans at https://github.com/plans. -type Plan struct { - Name *string `json:"name,omitempty"` - Space *int `json:"space,omitempty"` - Collaborators *int `json:"collaborators,omitempty"` - PrivateRepos *int `json:"private_repos,omitempty"` -} - -func (p Plan) String() string { - return Stringify(p) -} - -// OrganizationsListOptions specifies the optional parameters to the -// OrganizationsService.ListAll method. -type OrganizationsListOptions struct { - // Since filters Organizations by ID. - Since int `url:"since,omitempty"` - - ListOptions -} - -// ListAll lists all organizations, in the order that they were created on GitHub. -// -// Note: Pagination is powered exclusively by the since parameter. To continue -// listing the next set of organizations, use the ID of the last-returned organization -// as the opts.Since parameter for the next call. -// -// GitHub API docs: https://developer.github.com/v3/orgs/#list-all-organizations -func (s *OrganizationsService) ListAll(ctx context.Context, opt *OrganizationsListOptions) ([]*Organization, *Response, error) { - u, err := addOptions("organizations", opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - orgs := []*Organization{} - resp, err := s.client.Do(ctx, req, &orgs) - if err != nil { - return nil, resp, err - } - return orgs, resp, nil -} - -// List the organizations for a user. Passing the empty string will list -// organizations for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/orgs/#list-user-organizations -func (s *OrganizationsService) List(ctx context.Context, user string, opt *ListOptions) ([]*Organization, *Response, error) { - var u string - if user != "" { - u = fmt.Sprintf("users/%v/orgs", user) - } else { - u = "user/orgs" - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var orgs []*Organization - resp, err := s.client.Do(ctx, req, &orgs) - if err != nil { - return nil, resp, err - } - - return orgs, resp, nil -} - -// Get fetches an organization by name. -// -// GitHub API docs: https://developer.github.com/v3/orgs/#get-an-organization -func (s *OrganizationsService) Get(ctx context.Context, org string) (*Organization, *Response, error) { - u := fmt.Sprintf("orgs/%v", org) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - organization := new(Organization) - resp, err := s.client.Do(ctx, req, organization) - if err != nil { - return nil, resp, err - } - - return organization, resp, nil -} - -// GetByID fetches an organization. -// -// Note: GetByID uses the undocumented GitHub API endpoint /organizations/:id. -func (s *OrganizationsService) GetByID(ctx context.Context, id int64) (*Organization, *Response, error) { - u := fmt.Sprintf("organizations/%d", id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - organization := new(Organization) - resp, err := s.client.Do(ctx, req, organization) - if err != nil { - return nil, resp, err - } - - return organization, resp, nil -} - -// Edit an organization. -// -// GitHub API docs: https://developer.github.com/v3/orgs/#edit-an-organization -func (s *OrganizationsService) Edit(ctx context.Context, name string, org *Organization) (*Organization, *Response, error) { - u := fmt.Sprintf("orgs/%v", name) - req, err := s.client.NewRequest("PATCH", u, org) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - o := new(Organization) - resp, err := s.client.Do(ctx, req, o) - if err != nil { - return nil, resp, err - } - - return o, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/orgs_hooks.go b/vendor/github.com/google/go-github/github/orgs_hooks.go deleted file mode 100644 index 4fc692e0f63..00000000000 --- a/vendor/github.com/google/go-github/github/orgs_hooks.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2015 The go-github 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 github - -import ( - "context" - "fmt" -) - -// ListHooks lists all Hooks for the specified organization. -// -// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#list-hooks -func (s *OrganizationsService) ListHooks(ctx context.Context, org string, opt *ListOptions) ([]*Hook, *Response, error) { - u := fmt.Sprintf("orgs/%v/hooks", org) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var hooks []*Hook - resp, err := s.client.Do(ctx, req, &hooks) - if err != nil { - return nil, resp, err - } - - return hooks, resp, nil -} - -// GetHook returns a single specified Hook. -// -// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#get-single-hook -func (s *OrganizationsService) GetHook(ctx context.Context, org string, id int) (*Hook, *Response, error) { - u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - hook := new(Hook) - resp, err := s.client.Do(ctx, req, hook) - return hook, resp, err -} - -// CreateHook creates a Hook for the specified org. -// Name and Config are required fields. -// -// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#create-a-hook -func (s *OrganizationsService) CreateHook(ctx context.Context, org string, hook *Hook) (*Hook, *Response, error) { - u := fmt.Sprintf("orgs/%v/hooks", org) - req, err := s.client.NewRequest("POST", u, hook) - if err != nil { - return nil, nil, err - } - - h := new(Hook) - resp, err := s.client.Do(ctx, req, h) - if err != nil { - return nil, resp, err - } - - return h, resp, nil -} - -// EditHook updates a specified Hook. -// -// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#edit-a-hook -func (s *OrganizationsService) EditHook(ctx context.Context, org string, id int, hook *Hook) (*Hook, *Response, error) { - u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) - req, err := s.client.NewRequest("PATCH", u, hook) - if err != nil { - return nil, nil, err - } - h := new(Hook) - resp, err := s.client.Do(ctx, req, h) - return h, resp, err -} - -// PingHook triggers a 'ping' event to be sent to the Hook. -// -// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#ping-a-hook -func (s *OrganizationsService) PingHook(ctx context.Context, org string, id int) (*Response, error) { - u := fmt.Sprintf("orgs/%v/hooks/%d/pings", org, id) - req, err := s.client.NewRequest("POST", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} - -// DeleteHook deletes a specified Hook. -// -// GitHub API docs: https://developer.github.com/v3/orgs/hooks/#delete-a-hook -func (s *OrganizationsService) DeleteHook(ctx context.Context, org string, id int) (*Response, error) { - u := fmt.Sprintf("orgs/%v/hooks/%d", org, id) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/orgs_members.go b/vendor/github.com/google/go-github/github/orgs_members.go deleted file mode 100644 index d0ea6a985e1..00000000000 --- a/vendor/github.com/google/go-github/github/orgs_members.go +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// Membership represents the status of a user's membership in an organization or team. -type Membership struct { - URL *string `json:"url,omitempty"` - - // State is the user's status within the organization or team. - // Possible values are: "active", "pending" - State *string `json:"state,omitempty"` - - // Role identifies the user's role within the organization or team. - // Possible values for organization membership: - // member - non-owner organization member - // admin - organization owner - // - // Possible values for team membership are: - // member - a normal member of the team - // maintainer - a team maintainer. Able to add/remove other team - // members, promote other team members to team - // maintainer, and edit the team’s name and description - Role *string `json:"role,omitempty"` - - // For organization membership, the API URL of the organization. - OrganizationURL *string `json:"organization_url,omitempty"` - - // For organization membership, the organization the membership is for. - Organization *Organization `json:"organization,omitempty"` - - // For organization membership, the user the membership is for. - User *User `json:"user,omitempty"` -} - -func (m Membership) String() string { - return Stringify(m) -} - -// ListMembersOptions specifies optional parameters to the -// OrganizationsService.ListMembers method. -type ListMembersOptions struct { - // If true (or if the authenticated user is not an owner of the - // organization), list only publicly visible members. - PublicOnly bool `url:"-"` - - // Filter members returned in the list. Possible values are: - // 2fa_disabled, all. Default is "all". - Filter string `url:"filter,omitempty"` - - // Role filters members returned by their role in the organization. - // Possible values are: - // all - all members of the organization, regardless of role - // admin - organization owners - // member - non-organization members - // - // Default is "all". - Role string `url:"role,omitempty"` - - ListOptions -} - -// ListMembers lists the members for an organization. If the authenticated -// user is an owner of the organization, this will return both concealed and -// public members, otherwise it will only return public members. -// -// GitHub API docs: https://developer.github.com/v3/orgs/members/#members-list -func (s *OrganizationsService) ListMembers(ctx context.Context, org string, opt *ListMembersOptions) ([]*User, *Response, error) { - var u string - if opt != nil && opt.PublicOnly { - u = fmt.Sprintf("orgs/%v/public_members", org) - } else { - u = fmt.Sprintf("orgs/%v/members", org) - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var members []*User - resp, err := s.client.Do(ctx, req, &members) - if err != nil { - return nil, resp, err - } - - return members, resp, nil -} - -// IsMember checks if a user is a member of an organization. -// -// GitHub API docs: https://developer.github.com/v3/orgs/members/#check-membership -func (s *OrganizationsService) IsMember(ctx context.Context, org, user string) (bool, *Response, error) { - u := fmt.Sprintf("orgs/%v/members/%v", org, user) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return false, nil, err - } - - resp, err := s.client.Do(ctx, req, nil) - member, err := parseBoolResponse(err) - return member, resp, err -} - -// IsPublicMember checks if a user is a public member of an organization. -// -// GitHub API docs: https://developer.github.com/v3/orgs/members/#check-public-membership -func (s *OrganizationsService) IsPublicMember(ctx context.Context, org, user string) (bool, *Response, error) { - u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return false, nil, err - } - - resp, err := s.client.Do(ctx, req, nil) - member, err := parseBoolResponse(err) - return member, resp, err -} - -// RemoveMember removes a user from all teams of an organization. -// -// GitHub API docs: https://developer.github.com/v3/orgs/members/#remove-a-member -func (s *OrganizationsService) RemoveMember(ctx context.Context, org, user string) (*Response, error) { - u := fmt.Sprintf("orgs/%v/members/%v", org, user) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// PublicizeMembership publicizes a user's membership in an organization. (A -// user cannot publicize the membership for another user.) -// -// GitHub API docs: https://developer.github.com/v3/orgs/members/#publicize-a-users-membership -func (s *OrganizationsService) PublicizeMembership(ctx context.Context, org, user string) (*Response, error) { - u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) - req, err := s.client.NewRequest("PUT", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// ConcealMembership conceals a user's membership in an organization. -// -// GitHub API docs: https://developer.github.com/v3/orgs/members/#conceal-a-users-membership -func (s *OrganizationsService) ConcealMembership(ctx context.Context, org, user string) (*Response, error) { - u := fmt.Sprintf("orgs/%v/public_members/%v", org, user) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// ListOrgMembershipsOptions specifies optional parameters to the -// OrganizationsService.ListOrgMemberships method. -type ListOrgMembershipsOptions struct { - // Filter memberships to include only those with the specified state. - // Possible values are: "active", "pending". - State string `url:"state,omitempty"` - - ListOptions -} - -// ListOrgMemberships lists the organization memberships for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-your-organization-memberships -func (s *OrganizationsService) ListOrgMemberships(ctx context.Context, opt *ListOrgMembershipsOptions) ([]*Membership, *Response, error) { - u := "user/memberships/orgs" - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var memberships []*Membership - resp, err := s.client.Do(ctx, req, &memberships) - if err != nil { - return nil, resp, err - } - - return memberships, resp, nil -} - -// GetOrgMembership gets the membership for a user in a specified organization. -// Passing an empty string for user will get the membership for the -// authenticated user. -// -// GitHub API docs: -// https://developer.github.com/v3/orgs/members/#get-organization-membership -// https://developer.github.com/v3/orgs/members/#get-your-organization-membership -func (s *OrganizationsService) GetOrgMembership(ctx context.Context, user, org string) (*Membership, *Response, error) { - var u string - if user != "" { - u = fmt.Sprintf("orgs/%v/memberships/%v", org, user) - } else { - u = fmt.Sprintf("user/memberships/orgs/%v", org) - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - membership := new(Membership) - resp, err := s.client.Do(ctx, req, membership) - if err != nil { - return nil, resp, err - } - - return membership, resp, nil -} - -// EditOrgMembership edits the membership for user in specified organization. -// Passing an empty string for user will edit the membership for the -// authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/orgs/members/#add-or-update-organization-membership -// GitHub API docs: https://developer.github.com/v3/orgs/members/#edit-your-organization-membership -func (s *OrganizationsService) EditOrgMembership(ctx context.Context, user, org string, membership *Membership) (*Membership, *Response, error) { - var u, method string - if user != "" { - u = fmt.Sprintf("orgs/%v/memberships/%v", org, user) - method = "PUT" - } else { - u = fmt.Sprintf("user/memberships/orgs/%v", org) - method = "PATCH" - } - - req, err := s.client.NewRequest(method, u, membership) - if err != nil { - return nil, nil, err - } - - m := new(Membership) - resp, err := s.client.Do(ctx, req, m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// RemoveOrgMembership removes user from the specified organization. If the -// user has been invited to the organization, this will cancel their invitation. -// -// GitHub API docs: https://developer.github.com/v3/orgs/members/#remove-organization-membership -func (s *OrganizationsService) RemoveOrgMembership(ctx context.Context, user, org string) (*Response, error) { - u := fmt.Sprintf("orgs/%v/memberships/%v", org, user) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// ListPendingOrgInvitations returns a list of pending invitations. -// -// GitHub API docs: https://developer.github.com/v3/orgs/members/#list-pending-organization-invitations -func (s *OrganizationsService) ListPendingOrgInvitations(ctx context.Context, org string, opt *ListOptions) ([]*Invitation, *Response, error) { - u := fmt.Sprintf("orgs/%v/invitations", org) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var pendingInvitations []*Invitation - resp, err := s.client.Do(ctx, req, &pendingInvitations) - if err != nil { - return nil, resp, err - } - return pendingInvitations, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/orgs_outside_collaborators.go b/vendor/github.com/google/go-github/github/orgs_outside_collaborators.go deleted file mode 100644 index 85ffd05f61a..00000000000 --- a/vendor/github.com/google/go-github/github/orgs_outside_collaborators.go +++ /dev/null @@ -1,81 +0,0 @@ -// Copyright 2017 The go-github 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 github - -import ( - "context" - "fmt" -) - -// ListOutsideCollaboratorsOptions specifies optional parameters to the -// OrganizationsService.ListOutsideCollaborators method. -type ListOutsideCollaboratorsOptions struct { - // Filter outside collaborators returned in the list. Possible values are: - // 2fa_disabled, all. Default is "all". - Filter string `url:"filter,omitempty"` - - ListOptions -} - -// ListOutsideCollaborators lists outside collaborators of organization's repositories. -// This will only work if the authenticated -// user is an owner of the organization. -// -// Warning: The API may change without advance notice during the preview period. -// Preview features are not supported for production use. -// -// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#list-outside-collaborators -func (s *OrganizationsService) ListOutsideCollaborators(ctx context.Context, org string, opt *ListOutsideCollaboratorsOptions) ([]*User, *Response, error) { - u := fmt.Sprintf("orgs/%v/outside_collaborators", org) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var members []*User - resp, err := s.client.Do(ctx, req, &members) - if err != nil { - return nil, resp, err - } - - return members, resp, nil -} - -// RemoveOutsideCollaborator removes a user from the list of outside collaborators; -// consequently, removing them from all the organization's repositories. -// -// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#remove-outside-collaborator -func (s *OrganizationsService) RemoveOutsideCollaborator(ctx context.Context, org string, user string) (*Response, error) { - u := fmt.Sprintf("orgs/%v/outside_collaborators/%v", org, user) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// ConvertMemberToOutsideCollaborator reduces the permission level of a member of the -// organization to that of an outside collaborator. Therefore, they will only -// have access to the repositories that their current team membership allows. -// Responses for converting a non-member or the last owner to an outside collaborator -// are listed in GitHub API docs. -// -// GitHub API docs: https://developer.github.com/v3/orgs/outside_collaborators/#convert-member-to-outside-collaborator -func (s *OrganizationsService) ConvertMemberToOutsideCollaborator(ctx context.Context, org string, user string) (*Response, error) { - u := fmt.Sprintf("orgs/%v/outside_collaborators/%v", org, user) - req, err := s.client.NewRequest("PUT", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/orgs_projects.go b/vendor/github.com/google/go-github/github/orgs_projects.go deleted file mode 100644 index e57cba97829..00000000000 --- a/vendor/github.com/google/go-github/github/orgs_projects.go +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2017 The go-github 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 github - -import ( - "context" - "fmt" -) - -// ListProjects lists the projects for an organization. -// -// GitHub API docs: https://developer.github.com/v3/projects/#list-organization-projects -func (s *OrganizationsService) ListProjects(ctx context.Context, org string, opt *ProjectListOptions) ([]*Project, *Response, error) { - u := fmt.Sprintf("orgs/%v/projects", org) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - var projects []*Project - resp, err := s.client.Do(ctx, req, &projects) - if err != nil { - return nil, resp, err - } - - return projects, resp, nil -} - -// CreateProject creates a GitHub Project for the specified organization. -// -// GitHub API docs: https://developer.github.com/v3/projects/#create-an-organization-project -func (s *OrganizationsService) CreateProject(ctx context.Context, org string, opt *ProjectOptions) (*Project, *Response, error) { - u := fmt.Sprintf("orgs/%v/projects", org) - req, err := s.client.NewRequest("POST", u, opt) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - project := &Project{} - resp, err := s.client.Do(ctx, req, project) - if err != nil { - return nil, resp, err - } - - return project, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/orgs_teams.go b/vendor/github.com/google/go-github/github/orgs_teams.go deleted file mode 100644 index c145710881a..00000000000 --- a/vendor/github.com/google/go-github/github/orgs_teams.go +++ /dev/null @@ -1,512 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" - "strings" - "time" -) - -// Team represents a team within a GitHub organization. Teams are used to -// manage access to an organization's repositories. -type Team struct { - ID *int64 `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - Description *string `json:"description,omitempty"` - URL *string `json:"url,omitempty"` - Slug *string `json:"slug,omitempty"` - - // Permission specifies the default permission for repositories owned by the team. - Permission *string `json:"permission,omitempty"` - - // Privacy identifies the level of privacy this team should have. - // Possible values are: - // secret - only visible to organization owners and members of this team - // closed - visible to all members of this organization - // Default is "secret". - Privacy *string `json:"privacy,omitempty"` - - MembersCount *int `json:"members_count,omitempty"` - ReposCount *int `json:"repos_count,omitempty"` - Organization *Organization `json:"organization,omitempty"` - MembersURL *string `json:"members_url,omitempty"` - RepositoriesURL *string `json:"repositories_url,omitempty"` - Parent *Team `json:"parent,omitempty"` - - // LDAPDN is only available in GitHub Enterprise and when the team - // membership is synchronized with LDAP. - LDAPDN *string `json:"ldap_dn,omitempty"` -} - -func (t Team) String() string { - return Stringify(t) -} - -// Invitation represents a team member's invitation status. -type Invitation struct { - ID *int64 `json:"id,omitempty"` - Login *string `json:"login,omitempty"` - Email *string `json:"email,omitempty"` - // Role can be one of the values - 'direct_member', 'admin', 'billing_manager', 'hiring_manager', or 'reinstate'. - Role *string `json:"role,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - Inviter *User `json:"inviter,omitempty"` -} - -func (i Invitation) String() string { - return Stringify(i) -} - -// ListTeams lists all of the teams for an organization. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-teams -func (s *OrganizationsService) ListTeams(ctx context.Context, org string, opt *ListOptions) ([]*Team, *Response, error) { - u := fmt.Sprintf("orgs/%v/teams", org) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - var teams []*Team - resp, err := s.client.Do(ctx, req, &teams) - if err != nil { - return nil, resp, err - } - - return teams, resp, nil -} - -// GetTeam fetches a team by ID. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team -func (s *OrganizationsService) GetTeam(ctx context.Context, team int64) (*Team, *Response, error) { - u := fmt.Sprintf("teams/%v", team) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - t := new(Team) - resp, err := s.client.Do(ctx, req, t) - if err != nil { - return nil, resp, err - } - - return t, resp, nil -} - -// NewTeam represents a team to be created or modified. -type NewTeam struct { - Name string `json:"name"` // Name of the team. (Required.) - Description *string `json:"description,omitempty"` - Maintainers []string `json:"maintainers,omitempty"` - RepoNames []string `json:"repo_names,omitempty"` - ParentTeamID *int64 `json:"parent_team_id,omitempty"` - - // Deprecated: Permission is deprecated when creating or editing a team in an org - // using the new GitHub permission model. It no longer identifies the - // permission a team has on its repos, but only specifies the default - // permission a repo is initially added with. Avoid confusion by - // specifying a permission value when calling AddTeamRepo. - Permission *string `json:"permission,omitempty"` - - // Privacy identifies the level of privacy this team should have. - // Possible values are: - // secret - only visible to organization owners and members of this team - // closed - visible to all members of this organization - // Default is "secret". - Privacy *string `json:"privacy,omitempty"` - - // LDAPDN may be used in GitHub Enterprise when the team membership - // is synchronized with LDAP. - LDAPDN *string `json:"ldap_dn,omitempty"` -} - -func (s NewTeam) String() string { - return Stringify(s) -} - -// CreateTeam creates a new team within an organization. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#create-team -func (s *OrganizationsService) CreateTeam(ctx context.Context, org string, team *NewTeam) (*Team, *Response, error) { - u := fmt.Sprintf("orgs/%v/teams", org) - req, err := s.client.NewRequest("POST", u, team) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - t := new(Team) - resp, err := s.client.Do(ctx, req, t) - if err != nil { - return nil, resp, err - } - - return t, resp, nil -} - -// EditTeam edits a team. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#edit-team -func (s *OrganizationsService) EditTeam(ctx context.Context, id int64, team *NewTeam) (*Team, *Response, error) { - u := fmt.Sprintf("teams/%v", id) - req, err := s.client.NewRequest("PATCH", u, team) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - t := new(Team) - resp, err := s.client.Do(ctx, req, t) - if err != nil { - return nil, resp, err - } - - return t, resp, nil -} - -// DeleteTeam deletes a team. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#delete-team -func (s *OrganizationsService) DeleteTeam(ctx context.Context, team int64) (*Response, error) { - u := fmt.Sprintf("teams/%v", team) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - return s.client.Do(ctx, req, nil) -} - -// OrganizationListTeamMembersOptions specifies the optional parameters to the -// OrganizationsService.ListTeamMembers method. -type OrganizationListTeamMembersOptions struct { - // Role filters members returned by their role in the team. Possible - // values are "all", "member", "maintainer". Default is "all". - Role string `url:"role,omitempty"` - - ListOptions -} - -// ListChildTeams lists child teams for a team. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-child-teams -func (s *OrganizationsService) ListChildTeams(ctx context.Context, teamID int64, opt *ListOptions) ([]*Team, *Response, error) { - u := fmt.Sprintf("teams/%v/teams", teamID) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - var teams []*Team - resp, err := s.client.Do(ctx, req, &teams) - if err != nil { - return nil, resp, err - } - - return teams, resp, nil -} - -// ListTeamMembers lists all of the users who are members of the specified -// team. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-team-members -func (s *OrganizationsService) ListTeamMembers(ctx context.Context, team int64, opt *OrganizationListTeamMembersOptions) ([]*User, *Response, error) { - u := fmt.Sprintf("teams/%v/members", team) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - var members []*User - resp, err := s.client.Do(ctx, req, &members) - if err != nil { - return nil, resp, err - } - - return members, resp, nil -} - -// IsTeamMember checks if a user is a member of the specified team. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team-member -// -// Deprecated: This API has been marked as deprecated in the Github API docs, -// OrganizationsService.GetTeamMembership method should be used instead. -func (s *OrganizationsService) IsTeamMember(ctx context.Context, team int64, user string) (bool, *Response, error) { - u := fmt.Sprintf("teams/%v/members/%v", team, user) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return false, nil, err - } - - resp, err := s.client.Do(ctx, req, nil) - member, err := parseBoolResponse(err) - return member, resp, err -} - -// ListTeamRepos lists the repositories that the specified team has access to. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-team-repos -func (s *OrganizationsService) ListTeamRepos(ctx context.Context, team int64, opt *ListOptions) ([]*Repository, *Response, error) { - u := fmt.Sprintf("teams/%v/repos", team) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when topics API fully launches. - headers := []string{mediaTypeTopicsPreview, mediaTypeNestedTeamsPreview} - req.Header.Set("Accept", strings.Join(headers, ", ")) - - var repos []*Repository - resp, err := s.client.Do(ctx, req, &repos) - if err != nil { - return nil, resp, err - } - - return repos, resp, nil -} - -// IsTeamRepo checks if a team manages the specified repository. If the -// repository is managed by team, a Repository is returned which includes the -// permissions team has for that repo. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#check-if-a-team-manages-a-repository -func (s *OrganizationsService) IsTeamRepo(ctx context.Context, team int64, owner string, repo string) (*Repository, *Response, error) { - u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - headers := []string{mediaTypeOrgPermissionRepo, mediaTypeNestedTeamsPreview} - req.Header.Set("Accept", strings.Join(headers, ", ")) - - repository := new(Repository) - resp, err := s.client.Do(ctx, req, repository) - if err != nil { - return nil, resp, err - } - - return repository, resp, nil -} - -// OrganizationAddTeamRepoOptions specifies the optional parameters to the -// OrganizationsService.AddTeamRepo method. -type OrganizationAddTeamRepoOptions struct { - // Permission specifies the permission to grant the team on this repository. - // Possible values are: - // pull - team members can pull, but not push to or administer this repository - // push - team members can pull and push, but not administer this repository - // admin - team members can pull, push and administer this repository - // - // If not specified, the team's permission attribute will be used. - Permission string `json:"permission,omitempty"` -} - -// AddTeamRepo adds a repository to be managed by the specified team. The -// specified repository must be owned by the organization to which the team -// belongs, or a direct fork of a repository owned by the organization. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#add-team-repo -func (s *OrganizationsService) AddTeamRepo(ctx context.Context, team int64, owner string, repo string, opt *OrganizationAddTeamRepoOptions) (*Response, error) { - u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) - req, err := s.client.NewRequest("PUT", u, opt) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// RemoveTeamRepo removes a repository from being managed by the specified -// team. Note that this does not delete the repository, it just removes it -// from the team. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#remove-team-repo -func (s *OrganizationsService) RemoveTeamRepo(ctx context.Context, team int64, owner string, repo string) (*Response, error) { - u := fmt.Sprintf("teams/%v/repos/%v/%v", team, owner, repo) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// ListUserTeams lists a user's teams -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-user-teams -func (s *OrganizationsService) ListUserTeams(ctx context.Context, opt *ListOptions) ([]*Team, *Response, error) { - u := "user/teams" - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - var teams []*Team - resp, err := s.client.Do(ctx, req, &teams) - if err != nil { - return nil, resp, err - } - - return teams, resp, nil -} - -// GetTeamMembership returns the membership status for a user in a team. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#get-team-membership -func (s *OrganizationsService) GetTeamMembership(ctx context.Context, team int64, user string) (*Membership, *Response, error) { - u := fmt.Sprintf("teams/%v/memberships/%v", team, user) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - t := new(Membership) - resp, err := s.client.Do(ctx, req, t) - if err != nil { - return nil, resp, err - } - - return t, resp, nil -} - -// OrganizationAddTeamMembershipOptions does stuff specifies the optional -// parameters to the OrganizationsService.AddTeamMembership method. -type OrganizationAddTeamMembershipOptions struct { - // Role specifies the role the user should have in the team. Possible - // values are: - // member - a normal member of the team - // maintainer - a team maintainer. Able to add/remove other team - // members, promote other team members to team - // maintainer, and edit the team’s name and description - // - // Default value is "member". - Role string `json:"role,omitempty"` -} - -// AddTeamMembership adds or invites a user to a team. -// -// In order to add a membership between a user and a team, the authenticated -// user must have 'admin' permissions to the team or be an owner of the -// organization that the team is associated with. -// -// If the user is already a part of the team's organization (meaning they're on -// at least one other team in the organization), this endpoint will add the -// user to the team. -// -// If the user is completely unaffiliated with the team's organization (meaning -// they're on none of the organization's teams), this endpoint will send an -// invitation to the user via email. This newly-created membership will be in -// the "pending" state until the user accepts the invitation, at which point -// the membership will transition to the "active" state and the user will be -// added as a member of the team. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#add-team-membership -func (s *OrganizationsService) AddTeamMembership(ctx context.Context, team int64, user string, opt *OrganizationAddTeamMembershipOptions) (*Membership, *Response, error) { - u := fmt.Sprintf("teams/%v/memberships/%v", team, user) - req, err := s.client.NewRequest("PUT", u, opt) - if err != nil { - return nil, nil, err - } - - t := new(Membership) - resp, err := s.client.Do(ctx, req, t) - if err != nil { - return nil, resp, err - } - - return t, resp, nil -} - -// RemoveTeamMembership removes a user from a team. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#remove-team-membership -func (s *OrganizationsService) RemoveTeamMembership(ctx context.Context, team int64, user string) (*Response, error) { - u := fmt.Sprintf("teams/%v/memberships/%v", team, user) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// ListPendingTeamInvitations get pending invitaion list in team. -// Warning: The API may change without advance notice during the preview period. -// Preview features are not supported for production use. -// -// GitHub API docs: https://developer.github.com/v3/orgs/teams/#list-pending-team-invitations -func (s *OrganizationsService) ListPendingTeamInvitations(ctx context.Context, team int64, opt *ListOptions) ([]*Invitation, *Response, error) { - u := fmt.Sprintf("teams/%v/invitations", team) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var pendingInvitations []*Invitation - resp, err := s.client.Do(ctx, req, &pendingInvitations) - if err != nil { - return nil, resp, err - } - - return pendingInvitations, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/orgs_users_blocking.go b/vendor/github.com/google/go-github/github/orgs_users_blocking.go deleted file mode 100644 index b1aecf44532..00000000000 --- a/vendor/github.com/google/go-github/github/orgs_users_blocking.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2017 The go-github 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 github - -import ( - "context" - "fmt" -) - -// ListBlockedUsers lists all the users blocked by an organization. -// -// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#list-blocked-users -func (s *OrganizationsService) ListBlockedUsers(ctx context.Context, org string, opt *ListOptions) ([]*User, *Response, error) { - u := fmt.Sprintf("orgs/%v/blocks", org) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeBlockUsersPreview) - - var blockedUsers []*User - resp, err := s.client.Do(ctx, req, &blockedUsers) - if err != nil { - return nil, resp, err - } - - return blockedUsers, resp, nil -} - -// IsBlocked reports whether specified user is blocked from an organization. -// -// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#check-whether-a-user-is-blocked-from-an-organization -func (s *OrganizationsService) IsBlocked(ctx context.Context, org string, user string) (bool, *Response, error) { - u := fmt.Sprintf("orgs/%v/blocks/%v", org, user) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return false, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeBlockUsersPreview) - - resp, err := s.client.Do(ctx, req, nil) - isBlocked, err := parseBoolResponse(err) - return isBlocked, resp, err -} - -// BlockUser blocks specified user from an organization. -// -// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#block-a-user -func (s *OrganizationsService) BlockUser(ctx context.Context, org string, user string) (*Response, error) { - u := fmt.Sprintf("orgs/%v/blocks/%v", org, user) - - req, err := s.client.NewRequest("PUT", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeBlockUsersPreview) - - return s.client.Do(ctx, req, nil) -} - -// UnblockUser unblocks specified user from an organization. -// -// GitHub API docs: https://developer.github.com/v3/orgs/blocking/#unblock-a-user -func (s *OrganizationsService) UnblockUser(ctx context.Context, org string, user string) (*Response, error) { - u := fmt.Sprintf("orgs/%v/blocks/%v", org, user) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeBlockUsersPreview) - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/projects.go b/vendor/github.com/google/go-github/github/projects.go deleted file mode 100644 index 22061363291..00000000000 --- a/vendor/github.com/google/go-github/github/projects.go +++ /dev/null @@ -1,431 +0,0 @@ -// Copyright 2016 The go-github 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 github - -import ( - "context" - "fmt" -) - -// ProjectsService provides access to the projects functions in the -// GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/projects/ -type ProjectsService service - -// Project represents a GitHub Project. -type Project struct { - ID *int64 `json:"id,omitempty"` - URL *string `json:"url,omitempty"` - OwnerURL *string `json:"owner_url,omitempty"` - Name *string `json:"name,omitempty"` - Body *string `json:"body,omitempty"` - Number *int `json:"number,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - - // The User object that generated the project. - Creator *User `json:"creator,omitempty"` -} - -func (p Project) String() string { - return Stringify(p) -} - -// GetProject gets a GitHub Project for a repo. -// -// GitHub API docs: https://developer.github.com/v3/projects/#get-a-project -func (s *ProjectsService) GetProject(ctx context.Context, id int64) (*Project, *Response, error) { - u := fmt.Sprintf("projects/%v", id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - project := &Project{} - resp, err := s.client.Do(ctx, req, project) - if err != nil { - return nil, resp, err - } - - return project, resp, nil -} - -// ProjectOptions specifies the parameters to the -// RepositoriesService.CreateProject and -// ProjectsService.UpdateProject methods. -type ProjectOptions struct { - // The name of the project. (Required for creation; optional for update.) - Name string `json:"name,omitempty"` - // The body of the project. (Optional.) - Body string `json:"body,omitempty"` - - // The following field(s) are only applicable for update. - // They should be left with zero values for creation. - - // State of the project. Either "open" or "closed". (Optional.) - State string `json:"state,omitempty"` -} - -// UpdateProject updates a repository project. -// -// GitHub API docs: https://developer.github.com/v3/projects/#update-a-project -func (s *ProjectsService) UpdateProject(ctx context.Context, id int64, opt *ProjectOptions) (*Project, *Response, error) { - u := fmt.Sprintf("projects/%v", id) - req, err := s.client.NewRequest("PATCH", u, opt) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - project := &Project{} - resp, err := s.client.Do(ctx, req, project) - if err != nil { - return nil, resp, err - } - - return project, resp, nil -} - -// DeleteProject deletes a GitHub Project from a repository. -// -// GitHub API docs: https://developer.github.com/v3/projects/#delete-a-project -func (s *ProjectsService) DeleteProject(ctx context.Context, id int64) (*Response, error) { - u := fmt.Sprintf("projects/%v", id) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - return s.client.Do(ctx, req, nil) -} - -// ProjectColumn represents a column of a GitHub Project. -// -// GitHub API docs: https://developer.github.com/v3/repos/projects/ -type ProjectColumn struct { - ID *int64 `json:"id,omitempty"` - Name *string `json:"name,omitempty"` - ProjectURL *string `json:"project_url,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` -} - -// ListProjectColumns lists the columns of a GitHub Project for a repo. -// -// GitHub API docs: https://developer.github.com/v3/projects/columns/#list-project-columns -func (s *ProjectsService) ListProjectColumns(ctx context.Context, projectID int64, opt *ListOptions) ([]*ProjectColumn, *Response, error) { - u := fmt.Sprintf("projects/%v/columns", projectID) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - columns := []*ProjectColumn{} - resp, err := s.client.Do(ctx, req, &columns) - if err != nil { - return nil, resp, err - } - - return columns, resp, nil -} - -// GetProjectColumn gets a column of a GitHub Project for a repo. -// -// GitHub API docs: https://developer.github.com/v3/projects/columns/#get-a-project-column -func (s *ProjectsService) GetProjectColumn(ctx context.Context, id int64) (*ProjectColumn, *Response, error) { - u := fmt.Sprintf("projects/columns/%v", id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - column := &ProjectColumn{} - resp, err := s.client.Do(ctx, req, column) - if err != nil { - return nil, resp, err - } - - return column, resp, nil -} - -// ProjectColumnOptions specifies the parameters to the -// ProjectsService.CreateProjectColumn and -// ProjectsService.UpdateProjectColumn methods. -type ProjectColumnOptions struct { - // The name of the project column. (Required for creation and update.) - Name string `json:"name"` -} - -// CreateProjectColumn creates a column for the specified (by number) project. -// -// GitHub API docs: https://developer.github.com/v3/projects/columns/#create-a-project-column -func (s *ProjectsService) CreateProjectColumn(ctx context.Context, projectID int64, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) { - u := fmt.Sprintf("projects/%v/columns", projectID) - req, err := s.client.NewRequest("POST", u, opt) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - column := &ProjectColumn{} - resp, err := s.client.Do(ctx, req, column) - if err != nil { - return nil, resp, err - } - - return column, resp, nil -} - -// UpdateProjectColumn updates a column of a GitHub Project. -// -// GitHub API docs: https://developer.github.com/v3/projects/columns/#update-a-project-column -func (s *ProjectsService) UpdateProjectColumn(ctx context.Context, columnID int64, opt *ProjectColumnOptions) (*ProjectColumn, *Response, error) { - u := fmt.Sprintf("projects/columns/%v", columnID) - req, err := s.client.NewRequest("PATCH", u, opt) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - column := &ProjectColumn{} - resp, err := s.client.Do(ctx, req, column) - if err != nil { - return nil, resp, err - } - - return column, resp, nil -} - -// DeleteProjectColumn deletes a column from a GitHub Project. -// -// GitHub API docs: https://developer.github.com/v3/projects/columns/#delete-a-project-column -func (s *ProjectsService) DeleteProjectColumn(ctx context.Context, columnID int64) (*Response, error) { - u := fmt.Sprintf("projects/columns/%v", columnID) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - return s.client.Do(ctx, req, nil) -} - -// ProjectColumnMoveOptions specifies the parameters to the -// ProjectsService.MoveProjectColumn method. -type ProjectColumnMoveOptions struct { - // Position can be one of "first", "last", or "after:", where - // is the ID of a column in the same project. (Required.) - Position string `json:"position"` -} - -// MoveProjectColumn moves a column within a GitHub Project. -// -// GitHub API docs: https://developer.github.com/v3/projects/columns/#move-a-project-column -func (s *ProjectsService) MoveProjectColumn(ctx context.Context, columnID int64, opt *ProjectColumnMoveOptions) (*Response, error) { - u := fmt.Sprintf("projects/columns/%v/moves", columnID) - req, err := s.client.NewRequest("POST", u, opt) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - return s.client.Do(ctx, req, nil) -} - -// ProjectCard represents a card in a column of a GitHub Project. -// -// GitHub API docs: https://developer.github.com/v3/projects/cards/#get-a-project-card -type ProjectCard struct { - URL *string `json:"url,omitempty"` - ColumnURL *string `json:"column_url,omitempty"` - ContentURL *string `json:"content_url,omitempty"` - ID *int64 `json:"id,omitempty"` - Note *string `json:"note,omitempty"` - Creator *User `json:"creator,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - - // The following fields are only populated by Webhook events. - ColumnID *int64 `json:"column_id,omitempty"` -} - -// ListProjectCards lists the cards in a column of a GitHub Project. -// -// GitHub API docs: https://developer.github.com/v3/projects/cards/#list-project-cards -func (s *ProjectsService) ListProjectCards(ctx context.Context, columnID int64, opt *ListOptions) ([]*ProjectCard, *Response, error) { - u := fmt.Sprintf("projects/columns/%v/cards", columnID) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - cards := []*ProjectCard{} - resp, err := s.client.Do(ctx, req, &cards) - if err != nil { - return nil, resp, err - } - - return cards, resp, nil -} - -// GetProjectCard gets a card in a column of a GitHub Project. -// -// GitHub API docs: https://developer.github.com/v3/projects/cards/#get-a-project-card -func (s *ProjectsService) GetProjectCard(ctx context.Context, columnID int64) (*ProjectCard, *Response, error) { - u := fmt.Sprintf("projects/columns/cards/%v", columnID) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - card := &ProjectCard{} - resp, err := s.client.Do(ctx, req, card) - if err != nil { - return nil, resp, err - } - - return card, resp, nil -} - -// ProjectCardOptions specifies the parameters to the -// ProjectsService.CreateProjectCard and -// ProjectsService.UpdateProjectCard methods. -type ProjectCardOptions struct { - // The note of the card. Note and ContentID are mutually exclusive. - Note string `json:"note,omitempty"` - // The ID (not Number) of the Issue to associate with this card. - // Note and ContentID are mutually exclusive. - ContentID int64 `json:"content_id,omitempty"` - // The type of content to associate with this card. Possible values are: "Issue". - ContentType string `json:"content_type,omitempty"` -} - -// CreateProjectCard creates a card in the specified column of a GitHub Project. -// -// GitHub API docs: https://developer.github.com/v3/projects/cards/#create-a-project-card -func (s *ProjectsService) CreateProjectCard(ctx context.Context, columnID int64, opt *ProjectCardOptions) (*ProjectCard, *Response, error) { - u := fmt.Sprintf("projects/columns/%v/cards", columnID) - req, err := s.client.NewRequest("POST", u, opt) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - card := &ProjectCard{} - resp, err := s.client.Do(ctx, req, card) - if err != nil { - return nil, resp, err - } - - return card, resp, nil -} - -// UpdateProjectCard updates a card of a GitHub Project. -// -// GitHub API docs: https://developer.github.com/v3/projects/cards/#update-a-project-card -func (s *ProjectsService) UpdateProjectCard(ctx context.Context, cardID int64, opt *ProjectCardOptions) (*ProjectCard, *Response, error) { - u := fmt.Sprintf("projects/columns/cards/%v", cardID) - req, err := s.client.NewRequest("PATCH", u, opt) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - card := &ProjectCard{} - resp, err := s.client.Do(ctx, req, card) - if err != nil { - return nil, resp, err - } - - return card, resp, nil -} - -// DeleteProjectCard deletes a card from a GitHub Project. -// -// GitHub API docs: https://developer.github.com/v3/projects/cards/#delete-a-project-card -func (s *ProjectsService) DeleteProjectCard(ctx context.Context, cardID int64) (*Response, error) { - u := fmt.Sprintf("projects/columns/cards/%v", cardID) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - return s.client.Do(ctx, req, nil) -} - -// ProjectCardMoveOptions specifies the parameters to the -// ProjectsService.MoveProjectCard method. -type ProjectCardMoveOptions struct { - // Position can be one of "top", "bottom", or "after:", where - // is the ID of a card in the same project. - Position string `json:"position"` - // ColumnID is the ID of a column in the same project. Note that ColumnID - // is required when using Position "after:" when that card is in - // another column; otherwise it is optional. - ColumnID int64 `json:"column_id,omitempty"` -} - -// MoveProjectCard moves a card within a GitHub Project. -// -// GitHub API docs: https://developer.github.com/v3/projects/cards/#move-a-project-card -func (s *ProjectsService) MoveProjectCard(ctx context.Context, cardID int64, opt *ProjectCardMoveOptions) (*Response, error) { - u := fmt.Sprintf("projects/columns/cards/%v/moves", cardID) - req, err := s.client.NewRequest("POST", u, opt) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/pulls.go b/vendor/github.com/google/go-github/github/pulls.go deleted file mode 100644 index 31d492eea75..00000000000 --- a/vendor/github.com/google/go-github/github/pulls.go +++ /dev/null @@ -1,371 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "bytes" - "context" - "fmt" - "time" -) - -// PullRequestsService handles communication with the pull request related -// methods of the GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/pulls/ -type PullRequestsService service - -// PullRequest represents a GitHub pull request on a repository. -type PullRequest struct { - ID *int64 `json:"id,omitempty"` - Number *int `json:"number,omitempty"` - State *string `json:"state,omitempty"` - Title *string `json:"title,omitempty"` - Body *string `json:"body,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - ClosedAt *time.Time `json:"closed_at,omitempty"` - MergedAt *time.Time `json:"merged_at,omitempty"` - User *User `json:"user,omitempty"` - Merged *bool `json:"merged,omitempty"` - Mergeable *bool `json:"mergeable,omitempty"` - MergeableState *string `json:"mergeable_state,omitempty"` - MergedBy *User `json:"merged_by,omitempty"` - MergeCommitSHA *string `json:"merge_commit_sha,omitempty"` - Comments *int `json:"comments,omitempty"` - Commits *int `json:"commits,omitempty"` - Additions *int `json:"additions,omitempty"` - Deletions *int `json:"deletions,omitempty"` - ChangedFiles *int `json:"changed_files,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - IssueURL *string `json:"issue_url,omitempty"` - StatusesURL *string `json:"statuses_url,omitempty"` - DiffURL *string `json:"diff_url,omitempty"` - PatchURL *string `json:"patch_url,omitempty"` - ReviewCommentsURL *string `json:"review_comments_url,omitempty"` - ReviewCommentURL *string `json:"review_comment_url,omitempty"` - Assignee *User `json:"assignee,omitempty"` - Assignees []*User `json:"assignees,omitempty"` - Milestone *Milestone `json:"milestone,omitempty"` - MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` - AuthorAssociation *string `json:"author_association,omitempty"` - NodeID *string `json:"node_id,omitempty"` - - Head *PullRequestBranch `json:"head,omitempty"` - Base *PullRequestBranch `json:"base,omitempty"` -} - -func (p PullRequest) String() string { - return Stringify(p) -} - -// PullRequestBranch represents a base or head branch in a GitHub pull request. -type PullRequestBranch struct { - Label *string `json:"label,omitempty"` - Ref *string `json:"ref,omitempty"` - SHA *string `json:"sha,omitempty"` - Repo *Repository `json:"repo,omitempty"` - User *User `json:"user,omitempty"` -} - -// PullRequestListOptions specifies the optional parameters to the -// PullRequestsService.List method. -type PullRequestListOptions struct { - // State filters pull requests based on their state. Possible values are: - // open, closed. Default is "open". - State string `url:"state,omitempty"` - - // Head filters pull requests by head user and branch name in the format of: - // "user:ref-name". - Head string `url:"head,omitempty"` - - // Base filters pull requests by base branch name. - Base string `url:"base,omitempty"` - - // Sort specifies how to sort pull requests. Possible values are: created, - // updated, popularity, long-running. Default is "created". - Sort string `url:"sort,omitempty"` - - // Direction in which to sort pull requests. Possible values are: asc, desc. - // If Sort is "created" or not specified, Default is "desc", otherwise Default - // is "asc" - Direction string `url:"direction,omitempty"` - - ListOptions -} - -// List the pull requests for the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/pulls/#list-pull-requests -func (s *PullRequestsService) List(ctx context.Context, owner string, repo string, opt *PullRequestListOptions) ([]*PullRequest, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var pulls []*PullRequest - resp, err := s.client.Do(ctx, req, &pulls) - if err != nil { - return nil, resp, err - } - - return pulls, resp, nil -} - -// Get a single pull request. -// -// GitHub API docs: https://developer.github.com/v3/pulls/#get-a-single-pull-request -func (s *PullRequestsService) Get(ctx context.Context, owner string, repo string, number int) (*PullRequest, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - pull := new(PullRequest) - resp, err := s.client.Do(ctx, req, pull) - if err != nil { - return nil, resp, err - } - - return pull, resp, nil -} - -// GetRaw gets a single pull request in raw (diff or patch) format. -func (s *PullRequestsService) GetRaw(ctx context.Context, owner string, repo string, number int, opt RawOptions) (string, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return "", nil, err - } - - switch opt.Type { - case Diff: - req.Header.Set("Accept", mediaTypeV3Diff) - case Patch: - req.Header.Set("Accept", mediaTypeV3Patch) - default: - return "", nil, fmt.Errorf("unsupported raw type %d", opt.Type) - } - - var buf bytes.Buffer - resp, err := s.client.Do(ctx, req, &buf) - if err != nil { - return "", resp, err - } - - return buf.String(), resp, nil -} - -// NewPullRequest represents a new pull request to be created. -type NewPullRequest struct { - Title *string `json:"title,omitempty"` - Head *string `json:"head,omitempty"` - Base *string `json:"base,omitempty"` - Body *string `json:"body,omitempty"` - Issue *int `json:"issue,omitempty"` - MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` -} - -// Create a new pull request on the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/pulls/#create-a-pull-request -func (s *PullRequestsService) Create(ctx context.Context, owner string, repo string, pull *NewPullRequest) (*PullRequest, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls", owner, repo) - req, err := s.client.NewRequest("POST", u, pull) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - p := new(PullRequest) - resp, err := s.client.Do(ctx, req, p) - if err != nil { - return nil, resp, err - } - - return p, resp, nil -} - -type pullRequestUpdate struct { - Title *string `json:"title,omitempty"` - Body *string `json:"body,omitempty"` - State *string `json:"state,omitempty"` - Base *string `json:"base,omitempty"` - MaintainerCanModify *bool `json:"maintainer_can_modify,omitempty"` -} - -// Edit a pull request. -// pull must not be nil. -// -// The following fields are editable: Title, Body, State, Base.Ref and MaintainerCanModify. -// Base.Ref updates the base branch of the pull request. -// -// GitHub API docs: https://developer.github.com/v3/pulls/#update-a-pull-request -func (s *PullRequestsService) Edit(ctx context.Context, owner string, repo string, number int, pull *PullRequest) (*PullRequest, *Response, error) { - if pull == nil { - return nil, nil, fmt.Errorf("pull must be provided") - } - - u := fmt.Sprintf("repos/%v/%v/pulls/%d", owner, repo, number) - - update := &pullRequestUpdate{ - Title: pull.Title, - Body: pull.Body, - State: pull.State, - MaintainerCanModify: pull.MaintainerCanModify, - } - if pull.Base != nil { - update.Base = pull.Base.Ref - } - - req, err := s.client.NewRequest("PATCH", u, update) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - p := new(PullRequest) - resp, err := s.client.Do(ctx, req, p) - if err != nil { - return nil, resp, err - } - - return p, resp, nil -} - -// ListCommits lists the commits in a pull request. -// -// GitHub API docs: https://developer.github.com/v3/pulls/#list-commits-on-a-pull-request -func (s *PullRequestsService) ListCommits(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*RepositoryCommit, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/commits", owner, repo, number) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGitSigningPreview) - - var commits []*RepositoryCommit - resp, err := s.client.Do(ctx, req, &commits) - if err != nil { - return nil, resp, err - } - - return commits, resp, nil -} - -// ListFiles lists the files in a pull request. -// -// GitHub API docs: https://developer.github.com/v3/pulls/#list-pull-requests-files -func (s *PullRequestsService) ListFiles(ctx context.Context, owner string, repo string, number int, opt *ListOptions) ([]*CommitFile, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/files", owner, repo, number) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var commitFiles []*CommitFile - resp, err := s.client.Do(ctx, req, &commitFiles) - if err != nil { - return nil, resp, err - } - - return commitFiles, resp, nil -} - -// IsMerged checks if a pull request has been merged. -// -// GitHub API docs: https://developer.github.com/v3/pulls/#get-if-a-pull-request-has-been-merged -func (s *PullRequestsService) IsMerged(ctx context.Context, owner string, repo string, number int) (bool, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return false, nil, err - } - - resp, err := s.client.Do(ctx, req, nil) - merged, err := parseBoolResponse(err) - return merged, resp, err -} - -// PullRequestMergeResult represents the result of merging a pull request. -type PullRequestMergeResult struct { - SHA *string `json:"sha,omitempty"` - Merged *bool `json:"merged,omitempty"` - Message *string `json:"message,omitempty"` -} - -// PullRequestOptions lets you define how a pull request will be merged. -type PullRequestOptions struct { - CommitTitle string // Extra detail to append to automatic commit message. (Optional.) - SHA string // SHA that pull request head must match to allow merge. (Optional.) - - // The merge method to use. Possible values include: "merge", "squash", and "rebase" with the default being merge. (Optional.) - MergeMethod string -} - -type pullRequestMergeRequest struct { - CommitMessage string `json:"commit_message"` - CommitTitle string `json:"commit_title,omitempty"` - MergeMethod string `json:"merge_method,omitempty"` - SHA string `json:"sha,omitempty"` -} - -// Merge a pull request (Merge Button™). -// commitMessage is the title for the automatic commit message. -// -// GitHub API docs: https://developer.github.com/v3/pulls/#merge-a-pull-request-merge-buttontrade -func (s *PullRequestsService) Merge(ctx context.Context, owner string, repo string, number int, commitMessage string, options *PullRequestOptions) (*PullRequestMergeResult, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/merge", owner, repo, number) - - pullRequestBody := &pullRequestMergeRequest{CommitMessage: commitMessage} - if options != nil { - pullRequestBody.CommitTitle = options.CommitTitle - pullRequestBody.MergeMethod = options.MergeMethod - pullRequestBody.SHA = options.SHA - } - req, err := s.client.NewRequest("PUT", u, pullRequestBody) - if err != nil { - return nil, nil, err - } - - mergeResult := new(PullRequestMergeResult) - resp, err := s.client.Do(ctx, req, mergeResult) - if err != nil { - return nil, resp, err - } - - return mergeResult, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/pulls_comments.go b/vendor/github.com/google/go-github/github/pulls_comments.go deleted file mode 100644 index ff892279e44..00000000000 --- a/vendor/github.com/google/go-github/github/pulls_comments.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// PullRequestComment represents a comment left on a pull request. -type PullRequestComment struct { - ID *int64 `json:"id,omitempty"` - InReplyTo *int64 `json:"in_reply_to,omitempty"` - Body *string `json:"body,omitempty"` - Path *string `json:"path,omitempty"` - DiffHunk *string `json:"diff_hunk,omitempty"` - Position *int `json:"position,omitempty"` - OriginalPosition *int `json:"original_position,omitempty"` - CommitID *string `json:"commit_id,omitempty"` - OriginalCommitID *string `json:"original_commit_id,omitempty"` - User *User `json:"user,omitempty"` - Reactions *Reactions `json:"reactions,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - PullRequestURL *string `json:"pull_request_url,omitempty"` -} - -func (p PullRequestComment) String() string { - return Stringify(p) -} - -// PullRequestListCommentsOptions specifies the optional parameters to the -// PullRequestsService.ListComments method. -type PullRequestListCommentsOptions struct { - // Sort specifies how to sort comments. Possible values are: created, updated. - Sort string `url:"sort,omitempty"` - - // Direction in which to sort comments. Possible values are: asc, desc. - Direction string `url:"direction,omitempty"` - - // Since filters comments by time. - Since time.Time `url:"since,omitempty"` - - ListOptions -} - -// ListComments lists all comments on the specified pull request. Specifying a -// pull request number of 0 will return all comments on all pull requests for -// the repository. -// -// GitHub API docs: https://developer.github.com/v3/pulls/comments/#list-comments-on-a-pull-request -func (s *PullRequestsService) ListComments(ctx context.Context, owner string, repo string, number int, opt *PullRequestListCommentsOptions) ([]*PullRequestComment, *Response, error) { - var u string - if number == 0 { - u = fmt.Sprintf("repos/%v/%v/pulls/comments", owner, repo) - } else { - u = fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number) - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - var comments []*PullRequestComment - resp, err := s.client.Do(ctx, req, &comments) - if err != nil { - return nil, resp, err - } - - return comments, resp, nil -} - -// GetComment fetches the specified pull request comment. -// -// GitHub API docs: https://developer.github.com/v3/pulls/comments/#get-a-single-comment -func (s *PullRequestsService) GetComment(ctx context.Context, owner string, repo string, number int) (*PullRequestComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - comment := new(PullRequestComment) - resp, err := s.client.Do(ctx, req, comment) - if err != nil { - return nil, resp, err - } - - return comment, resp, nil -} - -// CreateComment creates a new comment on the specified pull request. -// -// GitHub API docs: https://developer.github.com/v3/pulls/comments/#create-a-comment -func (s *PullRequestsService) CreateComment(ctx context.Context, owner string, repo string, number int, comment *PullRequestComment) (*PullRequestComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/comments", owner, repo, number) - req, err := s.client.NewRequest("POST", u, comment) - if err != nil { - return nil, nil, err - } - - c := new(PullRequestComment) - resp, err := s.client.Do(ctx, req, c) - if err != nil { - return nil, resp, err - } - - return c, resp, nil -} - -// EditComment updates a pull request comment. -// -// GitHub API docs: https://developer.github.com/v3/pulls/comments/#edit-a-comment -func (s *PullRequestsService) EditComment(ctx context.Context, owner string, repo string, number int, comment *PullRequestComment) (*PullRequestComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number) - req, err := s.client.NewRequest("PATCH", u, comment) - if err != nil { - return nil, nil, err - } - - c := new(PullRequestComment) - resp, err := s.client.Do(ctx, req, c) - if err != nil { - return nil, resp, err - } - - return c, resp, nil -} - -// DeleteComment deletes a pull request comment. -// -// GitHub API docs: https://developer.github.com/v3/pulls/comments/#delete-a-comment -func (s *PullRequestsService) DeleteComment(ctx context.Context, owner string, repo string, number int) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/comments/%d", owner, repo, number) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/pulls_reviewers.go b/vendor/github.com/google/go-github/github/pulls_reviewers.go deleted file mode 100644 index 15b47be31fd..00000000000 --- a/vendor/github.com/google/go-github/github/pulls_reviewers.go +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2017 The go-github 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 github - -import ( - "context" - "fmt" -) - -// ReviewersRequest specifies users and teams for a pull request review request. -type ReviewersRequest struct { - Reviewers []string `json:"reviewers,omitempty"` - TeamReviewers []string `json:"team_reviewers,omitempty"` -} - -// Reviewers represents reviewers of a pull request. -type Reviewers struct { - Users []*User `json:"users,omitempty"` - Teams []*Team `json:"teams,omitempty"` -} - -// RequestReviewers creates a review request for the provided reviewers for the specified pull request. -// -// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#create-a-review-request -func (s *PullRequestsService) RequestReviewers(ctx context.Context, owner, repo string, number int, reviewers ReviewersRequest) (*PullRequest, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, number) - req, err := s.client.NewRequest("POST", u, &reviewers) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamReviewPreview) - - r := new(PullRequest) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, nil -} - -// ListReviewers lists reviewers whose reviews have been requested on the specified pull request. -// -// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#list-review-requests -func (s *PullRequestsService) ListReviewers(ctx context.Context, owner, repo string, number int, opt *ListOptions) (*Reviewers, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/requested_reviewers", owner, repo, number) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamReviewPreview) - - reviewers := new(Reviewers) - resp, err := s.client.Do(ctx, req, reviewers) - if err != nil { - return nil, resp, err - } - - return reviewers, resp, nil -} - -// RemoveReviewers removes the review request for the provided reviewers for the specified pull request. -// -// GitHub API docs: https://developer.github.com/v3/pulls/review_requests/#delete-a-review-request -func (s *PullRequestsService) RemoveReviewers(ctx context.Context, owner, repo string, number int, reviewers ReviewersRequest) (*Response, error) { - u := fmt.Sprintf("repos/%s/%s/pulls/%d/requested_reviewers", owner, repo, number) - req, err := s.client.NewRequest("DELETE", u, &reviewers) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTeamReviewPreview) - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/pulls_reviews.go b/vendor/github.com/google/go-github/github/pulls_reviews.go deleted file mode 100644 index 1aceb0d4ddf..00000000000 --- a/vendor/github.com/google/go-github/github/pulls_reviews.go +++ /dev/null @@ -1,236 +0,0 @@ -// Copyright 2016 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// PullRequestReview represents a review of a pull request. -type PullRequestReview struct { - ID *int64 `json:"id,omitempty"` - User *User `json:"user,omitempty"` - Body *string `json:"body,omitempty"` - SubmittedAt *time.Time `json:"submitted_at,omitempty"` - CommitID *string `json:"commit_id,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - PullRequestURL *string `json:"pull_request_url,omitempty"` - State *string `json:"state,omitempty"` -} - -func (p PullRequestReview) String() string { - return Stringify(p) -} - -// DraftReviewComment represents a comment part of the review. -type DraftReviewComment struct { - Path *string `json:"path,omitempty"` - Position *int `json:"position,omitempty"` - Body *string `json:"body,omitempty"` -} - -func (c DraftReviewComment) String() string { - return Stringify(c) -} - -// PullRequestReviewRequest represents a request to create a review. -type PullRequestReviewRequest struct { - CommitID *string `json:"commit_id,omitempty"` - Body *string `json:"body,omitempty"` - Event *string `json:"event,omitempty"` - Comments []*DraftReviewComment `json:"comments,omitempty"` -} - -func (r PullRequestReviewRequest) String() string { - return Stringify(r) -} - -// PullRequestReviewDismissalRequest represents a request to dismiss a review. -type PullRequestReviewDismissalRequest struct { - Message *string `json:"message,omitempty"` -} - -func (r PullRequestReviewDismissalRequest) String() string { - return Stringify(r) -} - -// ListReviews lists all reviews on the specified pull request. -// -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 -// -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#list-reviews-on-a-pull-request -func (s *PullRequestsService) ListReviews(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*PullRequestReview, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var reviews []*PullRequestReview - resp, err := s.client.Do(ctx, req, &reviews) - if err != nil { - return nil, resp, err - } - - return reviews, resp, nil -} - -// GetReview fetches the specified pull request review. -// -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 -// -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#get-a-single-review -func (s *PullRequestsService) GetReview(ctx context.Context, owner, repo string, number, reviewID int64) (*PullRequestReview, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - review := new(PullRequestReview) - resp, err := s.client.Do(ctx, req, review) - if err != nil { - return nil, resp, err - } - - return review, resp, nil -} - -// DeletePendingReview deletes the specified pull request pending review. -// -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 -// -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#delete-a-pending-review -func (s *PullRequestsService) DeletePendingReview(ctx context.Context, owner, repo string, number, reviewID int64) (*PullRequestReview, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d", owner, repo, number, reviewID) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, nil, err - } - - review := new(PullRequestReview) - resp, err := s.client.Do(ctx, req, review) - if err != nil { - return nil, resp, err - } - - return review, resp, nil -} - -// ListReviewComments lists all the comments for the specified review. -// -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 -// -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#get-comments-for-a-single-review -func (s *PullRequestsService) ListReviewComments(ctx context.Context, owner, repo string, number, reviewID int64, opt *ListOptions) ([]*PullRequestComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/comments", owner, repo, number, reviewID) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var comments []*PullRequestComment - resp, err := s.client.Do(ctx, req, &comments) - if err != nil { - return nil, resp, err - } - - return comments, resp, nil -} - -// CreateReview creates a new review on the specified pull request. -// -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 -// -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#create-a-pull-request-review -func (s *PullRequestsService) CreateReview(ctx context.Context, owner, repo string, number int, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews", owner, repo, number) - - req, err := s.client.NewRequest("POST", u, review) - if err != nil { - return nil, nil, err - } - - r := new(PullRequestReview) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, nil -} - -// SubmitReview submits a specified review on the specified pull request. -// -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 -// -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#submit-a-pull-request-review -func (s *PullRequestsService) SubmitReview(ctx context.Context, owner, repo string, number, reviewID int64, review *PullRequestReviewRequest) (*PullRequestReview, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/events", owner, repo, number, reviewID) - - req, err := s.client.NewRequest("POST", u, review) - if err != nil { - return nil, nil, err - } - - r := new(PullRequestReview) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, nil -} - -// DismissReview dismisses a specified review on the specified pull request. -// -// TODO: Follow up with GitHub support about an issue with this method's -// returned error format and remove this comment once it's fixed. -// Read more about it here - https://github.com/google/go-github/issues/540 -// -// GitHub API docs: https://developer.github.com/v3/pulls/reviews/#dismiss-a-pull-request-review -func (s *PullRequestsService) DismissReview(ctx context.Context, owner, repo string, number, reviewID int64, review *PullRequestReviewDismissalRequest) (*PullRequestReview, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/%d/reviews/%d/dismissals", owner, repo, number, reviewID) - - req, err := s.client.NewRequest("PUT", u, review) - if err != nil { - return nil, nil, err - } - - r := new(PullRequestReview) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/reactions.go b/vendor/github.com/google/go-github/github/reactions.go deleted file mode 100644 index b276ff3e059..00000000000 --- a/vendor/github.com/google/go-github/github/reactions.go +++ /dev/null @@ -1,273 +0,0 @@ -// Copyright 2016 The go-github 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 github - -import ( - "context" - "fmt" -) - -// ReactionsService provides access to the reactions-related functions in the -// GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/reactions/ -type ReactionsService service - -// Reaction represents a GitHub reaction. -type Reaction struct { - // ID is the Reaction ID. - ID *int64 `json:"id,omitempty"` - User *User `json:"user,omitempty"` - // Content is the type of reaction. - // Possible values are: - // "+1", "-1", "laugh", "confused", "heart", "hooray". - Content *string `json:"content,omitempty"` -} - -// Reactions represents a summary of GitHub reactions. -type Reactions struct { - TotalCount *int `json:"total_count,omitempty"` - PlusOne *int `json:"+1,omitempty"` - MinusOne *int `json:"-1,omitempty"` - Laugh *int `json:"laugh,omitempty"` - Confused *int `json:"confused,omitempty"` - Heart *int `json:"heart,omitempty"` - Hooray *int `json:"hooray,omitempty"` - URL *string `json:"url,omitempty"` -} - -func (r Reaction) String() string { - return Stringify(r) -} - -// ListCommentReactions lists the reactions for a commit comment. -// -// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-a-commit-comment -func (s *ReactionsService) ListCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*Reaction, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - var m []*Reaction - resp, err := s.client.Do(ctx, req, &m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// CreateCommentReaction creates a reaction for a commit comment. -// Note that if you have already created a reaction of type content, the -// previously created reaction will be returned with Status: 200 OK. -// -// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-a-commit-comment -func (s ReactionsService) CreateCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/comments/%v/reactions", owner, repo, id) - - body := &Reaction{Content: String(content)} - req, err := s.client.NewRequest("POST", u, body) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - m := &Reaction{} - resp, err := s.client.Do(ctx, req, m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// ListIssueReactions lists the reactions for an issue. -// -// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue -func (s *ReactionsService) ListIssueReactions(ctx context.Context, owner, repo string, number int, opt *ListOptions) ([]*Reaction, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%v/reactions", owner, repo, number) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - var m []*Reaction - resp, err := s.client.Do(ctx, req, &m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// CreateIssueReaction creates a reaction for an issue. -// Note that if you have already created a reaction of type content, the -// previously created reaction will be returned with Status: 200 OK. -// -// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue -func (s ReactionsService) CreateIssueReaction(ctx context.Context, owner, repo string, number int, content string) (*Reaction, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/%v/reactions", owner, repo, number) - - body := &Reaction{Content: String(content)} - req, err := s.client.NewRequest("POST", u, body) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - m := &Reaction{} - resp, err := s.client.Do(ctx, req, m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// ListIssueCommentReactions lists the reactions for an issue comment. -// -// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue-comment -func (s *ReactionsService) ListIssueCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*Reaction, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions", owner, repo, id) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - var m []*Reaction - resp, err := s.client.Do(ctx, req, &m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// CreateIssueCommentReaction creates a reaction for an issue comment. -// Note that if you have already created a reaction of type content, the -// previously created reaction will be returned with Status: 200 OK. -// -// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment -func (s ReactionsService) CreateIssueCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/issues/comments/%v/reactions", owner, repo, id) - - body := &Reaction{Content: String(content)} - req, err := s.client.NewRequest("POST", u, body) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - m := &Reaction{} - resp, err := s.client.Do(ctx, req, m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// ListPullRequestCommentReactions lists the reactions for a pull request review comment. -// -// GitHub API docs: https://developer.github.com/v3/reactions/#list-reactions-for-an-issue-comment -func (s *ReactionsService) ListPullRequestCommentReactions(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*Reaction, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions", owner, repo, id) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - var m []*Reaction - resp, err := s.client.Do(ctx, req, &m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// CreatePullRequestCommentReaction creates a reaction for a pull request review comment. -// Note that if you have already created a reaction of type content, the -// previously created reaction will be returned with Status: 200 OK. -// -// GitHub API docs: https://developer.github.com/v3/reactions/#create-reaction-for-an-issue-comment -func (s ReactionsService) CreatePullRequestCommentReaction(ctx context.Context, owner, repo string, id int64, content string) (*Reaction, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pulls/comments/%v/reactions", owner, repo, id) - - body := &Reaction{Content: String(content)} - req, err := s.client.NewRequest("POST", u, body) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - m := &Reaction{} - resp, err := s.client.Do(ctx, req, m) - if err != nil { - return nil, resp, err - } - - return m, resp, nil -} - -// DeleteReaction deletes a reaction. -// -// GitHub API docs: https://developer.github.com/v3/reaction/reactions/#delete-a-reaction-archive -func (s *ReactionsService) DeleteReaction(ctx context.Context, id int64) (*Response, error) { - u := fmt.Sprintf("reactions/%v", id) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/repos.go b/vendor/github.com/google/go-github/github/repos.go deleted file mode 100644 index 68accf7ff15..00000000000 --- a/vendor/github.com/google/go-github/github/repos.go +++ /dev/null @@ -1,1076 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "encoding/json" - "fmt" - "strings" -) - -// RepositoriesService handles communication with the repository related -// methods of the GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/repos/ -type RepositoriesService service - -// Repository represents a GitHub repository. -type Repository struct { - ID *int64 `json:"id,omitempty"` - Owner *User `json:"owner,omitempty"` - Name *string `json:"name,omitempty"` - FullName *string `json:"full_name,omitempty"` - Description *string `json:"description,omitempty"` - Homepage *string `json:"homepage,omitempty"` - CodeOfConduct *CodeOfConduct `json:"code_of_conduct,omitempty"` - DefaultBranch *string `json:"default_branch,omitempty"` - MasterBranch *string `json:"master_branch,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - PushedAt *Timestamp `json:"pushed_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - CloneURL *string `json:"clone_url,omitempty"` - GitURL *string `json:"git_url,omitempty"` - MirrorURL *string `json:"mirror_url,omitempty"` - SSHURL *string `json:"ssh_url,omitempty"` - SVNURL *string `json:"svn_url,omitempty"` - Language *string `json:"language,omitempty"` - Fork *bool `json:"fork,omitempty"` - ForksCount *int `json:"forks_count,omitempty"` - NetworkCount *int `json:"network_count,omitempty"` - OpenIssuesCount *int `json:"open_issues_count,omitempty"` - StargazersCount *int `json:"stargazers_count,omitempty"` - SubscribersCount *int `json:"subscribers_count,omitempty"` - WatchersCount *int `json:"watchers_count,omitempty"` - Size *int `json:"size,omitempty"` - AutoInit *bool `json:"auto_init,omitempty"` - Parent *Repository `json:"parent,omitempty"` - Source *Repository `json:"source,omitempty"` - Organization *Organization `json:"organization,omitempty"` - Permissions *map[string]bool `json:"permissions,omitempty"` - AllowRebaseMerge *bool `json:"allow_rebase_merge,omitempty"` - AllowSquashMerge *bool `json:"allow_squash_merge,omitempty"` - AllowMergeCommit *bool `json:"allow_merge_commit,omitempty"` - Topics []string `json:"topics,omitempty"` - - // Only provided when using RepositoriesService.Get while in preview - License *License `json:"license,omitempty"` - - // Additional mutable fields when creating and editing a repository - Private *bool `json:"private,omitempty"` - HasIssues *bool `json:"has_issues,omitempty"` - HasWiki *bool `json:"has_wiki,omitempty"` - HasPages *bool `json:"has_pages,omitempty"` - HasProjects *bool `json:"has_projects,omitempty"` - HasDownloads *bool `json:"has_downloads,omitempty"` - LicenseTemplate *string `json:"license_template,omitempty"` - GitignoreTemplate *string `json:"gitignore_template,omitempty"` - Archived *bool `json:"archived,omitempty"` - - // Creating an organization repository. Required for non-owners. - TeamID *int64 `json:"team_id,omitempty"` - - // API URLs - URL *string `json:"url,omitempty"` - ArchiveURL *string `json:"archive_url,omitempty"` - AssigneesURL *string `json:"assignees_url,omitempty"` - BlobsURL *string `json:"blobs_url,omitempty"` - BranchesURL *string `json:"branches_url,omitempty"` - CollaboratorsURL *string `json:"collaborators_url,omitempty"` - CommentsURL *string `json:"comments_url,omitempty"` - CommitsURL *string `json:"commits_url,omitempty"` - CompareURL *string `json:"compare_url,omitempty"` - ContentsURL *string `json:"contents_url,omitempty"` - ContributorsURL *string `json:"contributors_url,omitempty"` - DeploymentsURL *string `json:"deployments_url,omitempty"` - DownloadsURL *string `json:"downloads_url,omitempty"` - EventsURL *string `json:"events_url,omitempty"` - ForksURL *string `json:"forks_url,omitempty"` - GitCommitsURL *string `json:"git_commits_url,omitempty"` - GitRefsURL *string `json:"git_refs_url,omitempty"` - GitTagsURL *string `json:"git_tags_url,omitempty"` - HooksURL *string `json:"hooks_url,omitempty"` - IssueCommentURL *string `json:"issue_comment_url,omitempty"` - IssueEventsURL *string `json:"issue_events_url,omitempty"` - IssuesURL *string `json:"issues_url,omitempty"` - KeysURL *string `json:"keys_url,omitempty"` - LabelsURL *string `json:"labels_url,omitempty"` - LanguagesURL *string `json:"languages_url,omitempty"` - MergesURL *string `json:"merges_url,omitempty"` - MilestonesURL *string `json:"milestones_url,omitempty"` - NotificationsURL *string `json:"notifications_url,omitempty"` - PullsURL *string `json:"pulls_url,omitempty"` - ReleasesURL *string `json:"releases_url,omitempty"` - StargazersURL *string `json:"stargazers_url,omitempty"` - StatusesURL *string `json:"statuses_url,omitempty"` - SubscribersURL *string `json:"subscribers_url,omitempty"` - SubscriptionURL *string `json:"subscription_url,omitempty"` - TagsURL *string `json:"tags_url,omitempty"` - TreesURL *string `json:"trees_url,omitempty"` - TeamsURL *string `json:"teams_url,omitempty"` - - // TextMatches is only populated from search results that request text matches - // See: search.go and https://developer.github.com/v3/search/#text-match-metadata - TextMatches []TextMatch `json:"text_matches,omitempty"` -} - -func (r Repository) String() string { - return Stringify(r) -} - -// RepositoryListOptions specifies the optional parameters to the -// RepositoriesService.List method. -type RepositoryListOptions struct { - // Visibility of repositories to list. Can be one of all, public, or private. - // Default: all - Visibility string `url:"visibility,omitempty"` - - // List repos of given affiliation[s]. - // Comma-separated list of values. Can include: - // * owner: Repositories that are owned by the authenticated user. - // * collaborator: Repositories that the user has been added to as a - // collaborator. - // * organization_member: Repositories that the user has access to through - // being a member of an organization. This includes every repository on - // every team that the user is on. - // Default: owner,collaborator,organization_member - Affiliation string `url:"affiliation,omitempty"` - - // Type of repositories to list. - // Can be one of all, owner, public, private, member. Default: all - // Will cause a 422 error if used in the same request as visibility or - // affiliation. - Type string `url:"type,omitempty"` - - // How to sort the repository list. Can be one of created, updated, pushed, - // full_name. Default: full_name - Sort string `url:"sort,omitempty"` - - // Direction in which to sort repositories. Can be one of asc or desc. - // Default: when using full_name: asc; otherwise desc - Direction string `url:"direction,omitempty"` - - ListOptions -} - -// List the repositories for a user. Passing the empty string will list -// repositories for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/repos/#list-user-repositories -func (s *RepositoriesService) List(ctx context.Context, user string, opt *RepositoryListOptions) ([]*Repository, *Response, error) { - var u string - if user != "" { - u = fmt.Sprintf("users/%v/repos", user) - } else { - u = "user/repos" - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - var repos []*Repository - resp, err := s.client.Do(ctx, req, &repos) - if err != nil { - return nil, resp, err - } - - return repos, resp, nil -} - -// RepositoryListByOrgOptions specifies the optional parameters to the -// RepositoriesService.ListByOrg method. -type RepositoryListByOrgOptions struct { - // Type of repositories to list. Possible values are: all, public, private, - // forks, sources, member. Default is "all". - Type string `url:"type,omitempty"` - - ListOptions -} - -// ListByOrg lists the repositories for an organization. -// -// GitHub API docs: https://developer.github.com/v3/repos/#list-organization-repositories -func (s *RepositoriesService) ListByOrg(ctx context.Context, org string, opt *RepositoryListByOrgOptions) ([]*Repository, *Response, error) { - u := fmt.Sprintf("orgs/%v/repos", org) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - var repos []*Repository - resp, err := s.client.Do(ctx, req, &repos) - if err != nil { - return nil, resp, err - } - - return repos, resp, nil -} - -// RepositoryListAllOptions specifies the optional parameters to the -// RepositoriesService.ListAll method. -type RepositoryListAllOptions struct { - // ID of the last repository seen - Since int64 `url:"since,omitempty"` -} - -// ListAll lists all GitHub repositories in the order that they were created. -// -// GitHub API docs: https://developer.github.com/v3/repos/#list-all-public-repositories -func (s *RepositoriesService) ListAll(ctx context.Context, opt *RepositoryListAllOptions) ([]*Repository, *Response, error) { - u, err := addOptions("repositories", opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var repos []*Repository - resp, err := s.client.Do(ctx, req, &repos) - if err != nil { - return nil, resp, err - } - - return repos, resp, nil -} - -// Create a new repository. If an organization is specified, the new -// repository will be created under that org. If the empty string is -// specified, it will be created for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/repos/#create -func (s *RepositoriesService) Create(ctx context.Context, org string, repo *Repository) (*Repository, *Response, error) { - var u string - if org != "" { - u = fmt.Sprintf("orgs/%v/repos", org) - } else { - u = "user/repos" - } - - req, err := s.client.NewRequest("POST", u, repo) - if err != nil { - return nil, nil, err - } - - r := new(Repository) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, nil -} - -// Get fetches a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/#get -func (s *RepositoriesService) Get(ctx context.Context, owner, repo string) (*Repository, *Response, error) { - u := fmt.Sprintf("repos/%v/%v", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when the license support fully launches - // https://developer.github.com/v3/licenses/#get-a-repositorys-license - acceptHeaders := []string{mediaTypeLicensesPreview, mediaTypeCodesOfConductPreview, mediaTypeTopicsPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - repository := new(Repository) - resp, err := s.client.Do(ctx, req, repository) - if err != nil { - return nil, resp, err - } - - return repository, resp, nil -} - -// GetCodeOfConduct gets the contents of a repository's code of conduct. -// -// GitHub API docs: https://developer.github.com/v3/codes_of_conduct/#get-the-contents-of-a-repositorys-code-of-conduct -func (s *RepositoriesService) GetCodeOfConduct(ctx context.Context, owner, repo string) (*CodeOfConduct, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/community/code_of_conduct", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeCodesOfConductPreview) - - coc := new(CodeOfConduct) - resp, err := s.client.Do(ctx, req, coc) - if err != nil { - return nil, resp, err - } - - return coc, resp, nil -} - -// GetByID fetches a repository. -// -// Note: GetByID uses the undocumented GitHub API endpoint /repositories/:id. -func (s *RepositoriesService) GetByID(ctx context.Context, id int64) (*Repository, *Response, error) { - u := fmt.Sprintf("repositories/%d", id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when the license support fully launches - // https://developer.github.com/v3/licenses/#get-a-repositorys-license - req.Header.Set("Accept", mediaTypeLicensesPreview) - - repository := new(Repository) - resp, err := s.client.Do(ctx, req, repository) - if err != nil { - return nil, resp, err - } - - return repository, resp, nil -} - -// Edit updates a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/#edit -func (s *RepositoriesService) Edit(ctx context.Context, owner, repo string, repository *Repository) (*Repository, *Response, error) { - u := fmt.Sprintf("repos/%v/%v", owner, repo) - req, err := s.client.NewRequest("PATCH", u, repository) - if err != nil { - return nil, nil, err - } - - r := new(Repository) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, nil -} - -// Delete a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/#delete-a-repository -func (s *RepositoriesService) Delete(ctx context.Context, owner, repo string) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v", owner, repo) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// Contributor represents a repository contributor -type Contributor struct { - Login *string `json:"login,omitempty"` - ID *int64 `json:"id,omitempty"` - AvatarURL *string `json:"avatar_url,omitempty"` - GravatarID *string `json:"gravatar_id,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - FollowersURL *string `json:"followers_url,omitempty"` - FollowingURL *string `json:"following_url,omitempty"` - GistsURL *string `json:"gists_url,omitempty"` - StarredURL *string `json:"starred_url,omitempty"` - SubscriptionsURL *string `json:"subscriptions_url,omitempty"` - OrganizationsURL *string `json:"organizations_url,omitempty"` - ReposURL *string `json:"repos_url,omitempty"` - EventsURL *string `json:"events_url,omitempty"` - ReceivedEventsURL *string `json:"received_events_url,omitempty"` - Type *string `json:"type,omitempty"` - SiteAdmin *bool `json:"site_admin,omitempty"` - Contributions *int `json:"contributions,omitempty"` -} - -// ListContributorsOptions specifies the optional parameters to the -// RepositoriesService.ListContributors method. -type ListContributorsOptions struct { - // Include anonymous contributors in results or not - Anon string `url:"anon,omitempty"` - - ListOptions -} - -// ListContributors lists contributors for a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/#list-contributors -func (s *RepositoriesService) ListContributors(ctx context.Context, owner string, repository string, opt *ListContributorsOptions) ([]*Contributor, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/contributors", owner, repository) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var contributor []*Contributor - resp, err := s.client.Do(ctx, req, &contributor) - if err != nil { - return nil, nil, err - } - - return contributor, resp, nil -} - -// ListLanguages lists languages for the specified repository. The returned map -// specifies the languages and the number of bytes of code written in that -// language. For example: -// -// { -// "C": 78769, -// "Python": 7769 -// } -// -// GitHub API docs: https://developer.github.com/v3/repos/#list-languages -func (s *RepositoriesService) ListLanguages(ctx context.Context, owner string, repo string) (map[string]int, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/languages", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - languages := make(map[string]int) - resp, err := s.client.Do(ctx, req, &languages) - if err != nil { - return nil, resp, err - } - - return languages, resp, nil -} - -// ListTeams lists the teams for the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/#list-teams -func (s *RepositoriesService) ListTeams(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Team, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/teams", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - var teams []*Team - resp, err := s.client.Do(ctx, req, &teams) - if err != nil { - return nil, resp, err - } - - return teams, resp, nil -} - -// RepositoryTag represents a repository tag. -type RepositoryTag struct { - Name *string `json:"name,omitempty"` - Commit *Commit `json:"commit,omitempty"` - ZipballURL *string `json:"zipball_url,omitempty"` - TarballURL *string `json:"tarball_url,omitempty"` -} - -// ListTags lists tags for the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/#list-tags -func (s *RepositoriesService) ListTags(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*RepositoryTag, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/tags", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var tags []*RepositoryTag - resp, err := s.client.Do(ctx, req, &tags) - if err != nil { - return nil, resp, err - } - - return tags, resp, nil -} - -// Branch represents a repository branch -type Branch struct { - Name *string `json:"name,omitempty"` - Commit *RepositoryCommit `json:"commit,omitempty"` - Protected *bool `json:"protected,omitempty"` -} - -// Protection represents a repository branch's protection. -type Protection struct { - RequiredStatusChecks *RequiredStatusChecks `json:"required_status_checks"` - RequiredPullRequestReviews *PullRequestReviewsEnforcement `json:"required_pull_request_reviews"` - EnforceAdmins *AdminEnforcement `json:"enforce_admins"` - Restrictions *BranchRestrictions `json:"restrictions"` -} - -// ProtectionRequest represents a request to create/edit a branch's protection. -type ProtectionRequest struct { - RequiredStatusChecks *RequiredStatusChecks `json:"required_status_checks"` - RequiredPullRequestReviews *PullRequestReviewsEnforcementRequest `json:"required_pull_request_reviews"` - EnforceAdmins bool `json:"enforce_admins"` - Restrictions *BranchRestrictionsRequest `json:"restrictions"` -} - -// RequiredStatusChecks represents the protection status of a individual branch. -type RequiredStatusChecks struct { - // Require branches to be up to date before merging. (Required.) - Strict bool `json:"strict"` - // The list of status checks to require in order to merge into this - // branch. (Required; use []string{} instead of nil for empty list.) - Contexts []string `json:"contexts"` -} - -// PullRequestReviewsEnforcement represents the pull request reviews enforcement of a protected branch. -type PullRequestReviewsEnforcement struct { - // Specifies which users and teams can dismiss pull request reviews. - DismissalRestrictions DismissalRestrictions `json:"dismissal_restrictions"` - // Specifies if approved reviews are dismissed automatically, when a new commit is pushed. - DismissStaleReviews bool `json:"dismiss_stale_reviews"` - // RequireCodeOwnerReviews specifies if an approved review is required in pull requests including files with a designated code owner. - RequireCodeOwnerReviews bool `json:"require_code_owner_reviews"` -} - -// PullRequestReviewsEnforcementRequest represents request to set the pull request review -// enforcement of a protected branch. It is separate from PullRequestReviewsEnforcement above -// because the request structure is different from the response structure. -type PullRequestReviewsEnforcementRequest struct { - // Specifies which users and teams should be allowed to dismiss pull request reviews. Can be nil to disable the restrictions. - DismissalRestrictionsRequest *DismissalRestrictionsRequest `json:"dismissal_restrictions"` - // Specifies if approved reviews can be dismissed automatically, when a new commit is pushed. (Required) - DismissStaleReviews bool `json:"dismiss_stale_reviews"` - // RequireCodeOwnerReviews specifies if an approved review is required in pull requests including files with a designated code owner. - RequireCodeOwnerReviews bool `json:"require_code_owner_reviews"` -} - -// MarshalJSON implements the json.Marshaler interface. -// Converts nil value of PullRequestReviewsEnforcementRequest.DismissalRestrictionsRequest to empty array -func (req PullRequestReviewsEnforcementRequest) MarshalJSON() ([]byte, error) { - if req.DismissalRestrictionsRequest == nil { - newReq := struct { - R []interface{} `json:"dismissal_restrictions"` - D bool `json:"dismiss_stale_reviews"` - O bool `json:"require_code_owner_reviews"` - }{ - R: []interface{}{}, - D: req.DismissStaleReviews, - O: req.RequireCodeOwnerReviews, - } - return json.Marshal(newReq) - } - newReq := struct { - R *DismissalRestrictionsRequest `json:"dismissal_restrictions"` - D bool `json:"dismiss_stale_reviews"` - O bool `json:"require_code_owner_reviews"` - }{ - R: req.DismissalRestrictionsRequest, - D: req.DismissStaleReviews, - O: req.RequireCodeOwnerReviews, - } - return json.Marshal(newReq) -} - -// PullRequestReviewsEnforcementUpdate represents request to patch the pull request review -// enforcement of a protected branch. It is separate from PullRequestReviewsEnforcementRequest above -// because the patch request does not require all fields to be initialized. -type PullRequestReviewsEnforcementUpdate struct { - // Specifies which users and teams can dismiss pull request reviews. Can be omitted. - DismissalRestrictionsRequest *DismissalRestrictionsRequest `json:"dismissal_restrictions,omitempty"` - // Specifies if approved reviews can be dismissed automatically, when a new commit is pushed. Can be omitted. - DismissStaleReviews *bool `json:"dismiss_stale_reviews,omitempty"` - // RequireCodeOwnerReviews specifies if an approved review is required in pull requests including files with a designated code owner. - RequireCodeOwnerReviews bool `json:"require_code_owner_reviews,omitempty"` -} - -// AdminEnforcement represents the configuration to enforce required status checks for repository administrators. -type AdminEnforcement struct { - URL *string `json:"url,omitempty"` - Enabled bool `json:"enabled"` -} - -// BranchRestrictions represents the restriction that only certain users or -// teams may push to a branch. -type BranchRestrictions struct { - // The list of user logins with push access. - Users []*User `json:"users"` - // The list of team slugs with push access. - Teams []*Team `json:"teams"` -} - -// BranchRestrictionsRequest represents the request to create/edit the -// restriction that only certain users or teams may push to a branch. It is -// separate from BranchRestrictions above because the request structure is -// different from the response structure. -type BranchRestrictionsRequest struct { - // The list of user logins with push access. (Required; use []string{} instead of nil for empty list.) - Users []string `json:"users"` - // The list of team slugs with push access. (Required; use []string{} instead of nil for empty list.) - Teams []string `json:"teams"` -} - -// DismissalRestrictions specifies which users and teams can dismiss pull request reviews. -type DismissalRestrictions struct { - // The list of users who can dimiss pull request reviews. - Users []*User `json:"users"` - // The list of teams which can dismiss pull request reviews. - Teams []*Team `json:"teams"` -} - -// DismissalRestrictionsRequest represents the request to create/edit the -// restriction to allows only specific users or teams to dimiss pull request reviews. It is -// separate from DismissalRestrictions above because the request structure is -// different from the response structure. -type DismissalRestrictionsRequest struct { - // The list of user logins who can dismiss pull request reviews. (Required; use []string{} instead of nil for empty list.) - Users []string `json:"users"` - // The list of team slugs which can dismiss pull request reviews. (Required; use []string{} instead of nil for empty list.) - Teams []string `json:"teams"` -} - -// ListBranches lists branches for the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/#list-branches -func (s *RepositoriesService) ListBranches(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Branch, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - var branches []*Branch - resp, err := s.client.Do(ctx, req, &branches) - if err != nil { - return nil, resp, err - } - - return branches, resp, nil -} - -// GetBranch gets the specified branch for a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/#get-branch -func (s *RepositoriesService) GetBranch(ctx context.Context, owner, repo, branch string) (*Branch, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v", owner, repo, branch) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - b := new(Branch) - resp, err := s.client.Do(ctx, req, b) - if err != nil { - return nil, resp, err - } - - return b, resp, nil -} - -// GetBranchProtection gets the protection of a given branch. -// -// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-branch-protection -func (s *RepositoriesService) GetBranchProtection(ctx context.Context, owner, repo, branch string) (*Protection, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - p := new(Protection) - resp, err := s.client.Do(ctx, req, p) - if err != nil { - return nil, resp, err - } - - return p, resp, nil -} - -// GetRequiredStatusChecks gets the required status checks for a given protected branch. -// -// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-required-status-checks-of-protected-branch -func (s *RepositoriesService) GetRequiredStatusChecks(ctx context.Context, owner, repo, branch string) (*RequiredStatusChecks, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks", owner, repo, branch) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - p := new(RequiredStatusChecks) - resp, err := s.client.Do(ctx, req, p) - if err != nil { - return nil, resp, err - } - - return p, resp, nil -} - -// ListRequiredStatusChecksContexts lists the required status checks contexts for a given protected branch. -// -// GitHub API docs: https://developer.github.com/v3/repos/branches/#list-required-status-checks-contexts-of-protected-branch -func (s *RepositoriesService) ListRequiredStatusChecksContexts(ctx context.Context, owner, repo, branch string) (contexts []string, resp *Response, err error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_status_checks/contexts", owner, repo, branch) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - resp, err = s.client.Do(ctx, req, &contexts) - if err != nil { - return nil, resp, err - } - - return contexts, resp, nil -} - -// UpdateBranchProtection updates the protection of a given branch. -// -// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-branch-protection -func (s *RepositoriesService) UpdateBranchProtection(ctx context.Context, owner, repo, branch string, preq *ProtectionRequest) (*Protection, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch) - req, err := s.client.NewRequest("PUT", u, preq) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - p := new(Protection) - resp, err := s.client.Do(ctx, req, p) - if err != nil { - return nil, resp, err - } - - return p, resp, nil -} - -// RemoveBranchProtection removes the protection of a given branch. -// -// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-branch-protection -func (s *RepositoriesService) RemoveBranchProtection(ctx context.Context, owner, repo, branch string) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection", owner, repo, branch) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - return s.client.Do(ctx, req, nil) -} - -// License gets the contents of a repository's license if one is detected. -// -// GitHub API docs: https://developer.github.com/v3/licenses/#get-the-contents-of-a-repositorys-license -func (s *RepositoriesService) License(ctx context.Context, owner, repo string) (*RepositoryLicense, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/license", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - r := &RepositoryLicense{} - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, nil -} - -// GetPullRequestReviewEnforcement gets pull request review enforcement of a protected branch. -// -// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-pull-request-review-enforcement-of-protected-branch -func (s *RepositoriesService) GetPullRequestReviewEnforcement(ctx context.Context, owner, repo, branch string) (*PullRequestReviewsEnforcement, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - r := new(PullRequestReviewsEnforcement) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, nil -} - -// UpdatePullRequestReviewEnforcement patches pull request review enforcement of a protected branch. -// It requires admin access and branch protection to be enabled. -// -// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-pull-request-review-enforcement-of-protected-branch -func (s *RepositoriesService) UpdatePullRequestReviewEnforcement(ctx context.Context, owner, repo, branch string, patch *PullRequestReviewsEnforcementUpdate) (*PullRequestReviewsEnforcement, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch) - req, err := s.client.NewRequest("PATCH", u, patch) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - r := new(PullRequestReviewsEnforcement) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, err -} - -// DisableDismissalRestrictions disables dismissal restrictions of a protected branch. -// It requires admin access and branch protection to be enabled. -// -// GitHub API docs: https://developer.github.com/v3/repos/branches/#update-pull-request-review-enforcement-of-protected-branch -func (s *RepositoriesService) DisableDismissalRestrictions(ctx context.Context, owner, repo, branch string) (*PullRequestReviewsEnforcement, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch) - - data := struct { - R []interface{} `json:"dismissal_restrictions"` - }{[]interface{}{}} - - req, err := s.client.NewRequest("PATCH", u, data) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - r := new(PullRequestReviewsEnforcement) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, err -} - -// RemovePullRequestReviewEnforcement removes pull request enforcement of a protected branch. -// -// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-pull-request-review-enforcement-of-protected-branch -func (s *RepositoriesService) RemovePullRequestReviewEnforcement(ctx context.Context, owner, repo, branch string) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/required_pull_request_reviews", owner, repo, branch) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - return s.client.Do(ctx, req, nil) -} - -// GetAdminEnforcement gets admin enforcement information of a protected branch. -// -// GitHub API docs: https://developer.github.com/v3/repos/branches/#get-admin-enforcement-of-protected-branch -func (s *RepositoriesService) GetAdminEnforcement(ctx context.Context, owner, repo, branch string) (*AdminEnforcement, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, branch) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - r := new(AdminEnforcement) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, nil -} - -// AddAdminEnforcement adds admin enforcement to a protected branch. -// It requires admin access and branch protection to be enabled. -// -// GitHub API docs: https://developer.github.com/v3/repos/branches/#add-admin-enforcement-of-protected-branch -func (s *RepositoriesService) AddAdminEnforcement(ctx context.Context, owner, repo, branch string) (*AdminEnforcement, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, branch) - req, err := s.client.NewRequest("POST", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - r := new(AdminEnforcement) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, err -} - -// RemoveAdminEnforcement removes admin enforcement from a protected branch. -// -// GitHub API docs: https://developer.github.com/v3/repos/branches/#remove-admin-enforcement-of-protected-branch -func (s *RepositoriesService) RemoveAdminEnforcement(ctx context.Context, owner, repo, branch string) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/branches/%v/protection/enforce_admins", owner, repo, branch) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches - req.Header.Set("Accept", mediaTypeProtectedBranchesPreview) - - return s.client.Do(ctx, req, nil) -} - -// repositoryTopics represents a collection of repository topics. -type repositoryTopics struct { - Names []string `json:"names"` -} - -// ListAllTopics lists topics for a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/#list-all-topics-for-a-repository -func (s *RepositoriesService) ListAllTopics(ctx context.Context, owner, repo string) ([]string, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/topics", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTopicsPreview) - - topics := new(repositoryTopics) - resp, err := s.client.Do(ctx, req, topics) - if err != nil { - return nil, resp, err - } - - return topics.Names, resp, nil -} - -// ReplaceAllTopics replaces topics for a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/#replace-all-topics-for-a-repository -func (s *RepositoriesService) ReplaceAllTopics(ctx context.Context, owner, repo string, topics []string) ([]string, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/topics", owner, repo) - t := &repositoryTopics{ - Names: topics, - } - if t.Names == nil { - t.Names = []string{} - } - req, err := s.client.NewRequest("PUT", u, t) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTopicsPreview) - - t = new(repositoryTopics) - resp, err := s.client.Do(ctx, req, t) - if err != nil { - return nil, resp, err - } - - return t.Names, resp, nil -} - -// TransferRequest represents a request to transfer a repository. -type TransferRequest struct { - NewOwner string `json:"new_owner"` - TeamID []int64 `json:"team_id,omitempty"` -} - -// Transfer transfers a repository from one account or organization to another. -// -// This method might return an *AcceptedError and a status code of -// 202. This is because this is the status that GitHub returns to signify that -// it has now scheduled the transfer of the repository in a background task. -// A follow up request, after a delay of a second or so, should result -// in a successful request. -// -// GitHub API docs: https://developer.github.com/v3/repos/#transfer-a-repository -func (s *RepositoriesService) Transfer(ctx context.Context, owner, repo string, transfer TransferRequest) (*Repository, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/transfer", owner, repo) - - req, err := s.client.NewRequest("POST", u, &transfer) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeRepositoryTransferPreview) - - r := new(Repository) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - - return r, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/repos_collaborators.go b/vendor/github.com/google/go-github/github/repos_collaborators.go deleted file mode 100644 index 61ee9d39c62..00000000000 --- a/vendor/github.com/google/go-github/github/repos_collaborators.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// ListCollaboratorsOptions specifies the optional parameters to the -// RepositoriesService.ListCollaborators method. -type ListCollaboratorsOptions struct { - // Affiliation specifies how collaborators should be filtered by their affiliation. - // Possible values are: - // outside - All outside collaborators of an organization-owned repository - // direct - All collaborators with permissions to an organization-owned repository, - // regardless of organization membership status - // all - All collaborators the authenticated user can see - // - // Default value is "all". - Affiliation string `url:"affiliation,omitempty"` - - ListOptions -} - -// ListCollaborators lists the GitHub users that have access to the repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#list-collaborators -func (s *RepositoriesService) ListCollaborators(ctx context.Context, owner, repo string, opt *ListCollaboratorsOptions) ([]*User, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/collaborators", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - req.Header.Set("Accept", mediaTypeNestedTeamsPreview) - - var users []*User - resp, err := s.client.Do(ctx, req, &users) - if err != nil { - return nil, resp, err - } - - return users, resp, nil -} - -// IsCollaborator checks whether the specified GitHub user has collaborator -// access to the given repo. -// Note: This will return false if the user is not a collaborator OR the user -// is not a GitHub user. -// -// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#get -func (s *RepositoriesService) IsCollaborator(ctx context.Context, owner, repo, user string) (bool, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return false, nil, err - } - - resp, err := s.client.Do(ctx, req, nil) - isCollab, err := parseBoolResponse(err) - return isCollab, resp, err -} - -// RepositoryPermissionLevel represents the permission level an organization -// member has for a given repository. -type RepositoryPermissionLevel struct { - // Possible values: "admin", "write", "read", "none" - Permission *string `json:"permission,omitempty"` - - User *User `json:"user,omitempty"` -} - -// GetPermissionLevel retrieves the specific permission level a collaborator has for a given repository. -// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#review-a-users-permission-level -func (s *RepositoriesService) GetPermissionLevel(ctx context.Context, owner, repo, user string) (*RepositoryPermissionLevel, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/collaborators/%v/permission", owner, repo, user) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - rpl := new(RepositoryPermissionLevel) - resp, err := s.client.Do(ctx, req, rpl) - if err != nil { - return nil, resp, err - } - return rpl, resp, nil -} - -// RepositoryAddCollaboratorOptions specifies the optional parameters to the -// RepositoriesService.AddCollaborator method. -type RepositoryAddCollaboratorOptions struct { - // Permission specifies the permission to grant the user on this repository. - // Possible values are: - // pull - team members can pull, but not push to or administer this repository - // push - team members can pull and push, but not administer this repository - // admin - team members can pull, push and administer this repository - // - // Default value is "push". This option is only valid for organization-owned repositories. - Permission string `json:"permission,omitempty"` -} - -// AddCollaborator sends an invitation to the specified GitHub user -// to become a collaborator to the given repo. -// -// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#add-user-as-a-collaborator -func (s *RepositoriesService) AddCollaborator(ctx context.Context, owner, repo, user string, opt *RepositoryAddCollaboratorOptions) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) - req, err := s.client.NewRequest("PUT", u, opt) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) - - return s.client.Do(ctx, req, nil) -} - -// RemoveCollaborator removes the specified GitHub user as collaborator from the given repo. -// Note: Does not return error if a valid user that is not a collaborator is removed. -// -// GitHub API docs: https://developer.github.com/v3/repos/collaborators/#remove-collaborator -func (s *RepositoriesService) RemoveCollaborator(ctx context.Context, owner, repo, user string) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/collaborators/%v", owner, repo, user) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/repos_comments.go b/vendor/github.com/google/go-github/github/repos_comments.go deleted file mode 100644 index fa2377d403d..00000000000 --- a/vendor/github.com/google/go-github/github/repos_comments.go +++ /dev/null @@ -1,161 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// RepositoryComment represents a comment for a commit, file, or line in a repository. -type RepositoryComment struct { - HTMLURL *string `json:"html_url,omitempty"` - URL *string `json:"url,omitempty"` - ID *int64 `json:"id,omitempty"` - CommitID *string `json:"commit_id,omitempty"` - User *User `json:"user,omitempty"` - Reactions *Reactions `json:"reactions,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - - // User-mutable fields - Body *string `json:"body"` - // User-initialized fields - Path *string `json:"path,omitempty"` - Position *int `json:"position,omitempty"` -} - -func (r RepositoryComment) String() string { - return Stringify(r) -} - -// ListComments lists all the comments for the repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/comments/#list-commit-comments-for-a-repository -func (s *RepositoriesService) ListComments(ctx context.Context, owner, repo string, opt *ListOptions) ([]*RepositoryComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/comments", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - var comments []*RepositoryComment - resp, err := s.client.Do(ctx, req, &comments) - if err != nil { - return nil, resp, err - } - - return comments, resp, nil -} - -// ListCommitComments lists all the comments for a given commit SHA. -// -// GitHub API docs: https://developer.github.com/v3/repos/comments/#list-comments-for-a-single-commit -func (s *RepositoriesService) ListCommitComments(ctx context.Context, owner, repo, sha string, opt *ListOptions) ([]*RepositoryComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/commits/%v/comments", owner, repo, sha) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - var comments []*RepositoryComment - resp, err := s.client.Do(ctx, req, &comments) - if err != nil { - return nil, resp, err - } - - return comments, resp, nil -} - -// CreateComment creates a comment for the given commit. -// Note: GitHub allows for comments to be created for non-existing files and positions. -// -// GitHub API docs: https://developer.github.com/v3/repos/comments/#create-a-commit-comment -func (s *RepositoriesService) CreateComment(ctx context.Context, owner, repo, sha string, comment *RepositoryComment) (*RepositoryComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/commits/%v/comments", owner, repo, sha) - req, err := s.client.NewRequest("POST", u, comment) - if err != nil { - return nil, nil, err - } - - c := new(RepositoryComment) - resp, err := s.client.Do(ctx, req, c) - if err != nil { - return nil, resp, err - } - - return c, resp, nil -} - -// GetComment gets a single comment from a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/comments/#get-a-single-commit-comment -func (s *RepositoriesService) GetComment(ctx context.Context, owner, repo string, id int64) (*RepositoryComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeReactionsPreview) - - c := new(RepositoryComment) - resp, err := s.client.Do(ctx, req, c) - if err != nil { - return nil, resp, err - } - - return c, resp, nil -} - -// UpdateComment updates the body of a single comment. -// -// GitHub API docs: https://developer.github.com/v3/repos/comments/#update-a-commit-comment -func (s *RepositoriesService) UpdateComment(ctx context.Context, owner, repo string, id int64, comment *RepositoryComment) (*RepositoryComment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) - req, err := s.client.NewRequest("PATCH", u, comment) - if err != nil { - return nil, nil, err - } - - c := new(RepositoryComment) - resp, err := s.client.Do(ctx, req, c) - if err != nil { - return nil, resp, err - } - - return c, resp, nil -} - -// DeleteComment deletes a single comment from a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/comments/#delete-a-commit-comment -func (s *RepositoriesService) DeleteComment(ctx context.Context, owner, repo string, id int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/comments/%v", owner, repo, id) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/repos_commits.go b/vendor/github.com/google/go-github/github/repos_commits.go deleted file mode 100644 index 0484737342c..00000000000 --- a/vendor/github.com/google/go-github/github/repos_commits.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "bytes" - "context" - "fmt" - "time" -) - -// RepositoryCommit represents a commit in a repo. -// Note that it's wrapping a Commit, so author/committer information is in two places, -// but contain different details about them: in RepositoryCommit "github details", in Commit - "git details". -type RepositoryCommit struct { - SHA *string `json:"sha,omitempty"` - Commit *Commit `json:"commit,omitempty"` - Author *User `json:"author,omitempty"` - Committer *User `json:"committer,omitempty"` - Parents []Commit `json:"parents,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - URL *string `json:"url,omitempty"` - CommentsURL *string `json:"comments_url,omitempty"` - - // Details about how many changes were made in this commit. Only filled in during GetCommit! - Stats *CommitStats `json:"stats,omitempty"` - // Details about which files, and how this commit touched. Only filled in during GetCommit! - Files []CommitFile `json:"files,omitempty"` -} - -func (r RepositoryCommit) String() string { - return Stringify(r) -} - -// CommitStats represents the number of additions / deletions from a file in a given RepositoryCommit or GistCommit. -type CommitStats struct { - Additions *int `json:"additions,omitempty"` - Deletions *int `json:"deletions,omitempty"` - Total *int `json:"total,omitempty"` -} - -func (c CommitStats) String() string { - return Stringify(c) -} - -// CommitFile represents a file modified in a commit. -type CommitFile struct { - SHA *string `json:"sha,omitempty"` - Filename *string `json:"filename,omitempty"` - Additions *int `json:"additions,omitempty"` - Deletions *int `json:"deletions,omitempty"` - Changes *int `json:"changes,omitempty"` - Status *string `json:"status,omitempty"` - Patch *string `json:"patch,omitempty"` - BlobURL *string `json:"blob_url,omitempty"` - RawURL *string `json:"raw_url,omitempty"` - ContentsURL *string `json:"contents_url,omitempty"` -} - -func (c CommitFile) String() string { - return Stringify(c) -} - -// CommitsComparison is the result of comparing two commits. -// See CompareCommits() for details. -type CommitsComparison struct { - BaseCommit *RepositoryCommit `json:"base_commit,omitempty"` - MergeBaseCommit *RepositoryCommit `json:"merge_base_commit,omitempty"` - - // Head can be 'behind' or 'ahead' - Status *string `json:"status,omitempty"` - AheadBy *int `json:"ahead_by,omitempty"` - BehindBy *int `json:"behind_by,omitempty"` - TotalCommits *int `json:"total_commits,omitempty"` - - Commits []RepositoryCommit `json:"commits,omitempty"` - - Files []CommitFile `json:"files,omitempty"` - - HTMLURL *string `json:"html_url,omitempty"` - PermalinkURL *string `json:"permalink_url,omitempty"` - DiffURL *string `json:"diff_url,omitempty"` - PatchURL *string `json:"patch_url,omitempty"` - URL *string `json:"url,omitempty"` // API URL. -} - -func (c CommitsComparison) String() string { - return Stringify(c) -} - -// CommitsListOptions specifies the optional parameters to the -// RepositoriesService.ListCommits method. -type CommitsListOptions struct { - // SHA or branch to start listing Commits from. - SHA string `url:"sha,omitempty"` - - // Path that should be touched by the returned Commits. - Path string `url:"path,omitempty"` - - // Author of by which to filter Commits. - Author string `url:"author,omitempty"` - - // Since when should Commits be included in the response. - Since time.Time `url:"since,omitempty"` - - // Until when should Commits be included in the response. - Until time.Time `url:"until,omitempty"` - - ListOptions -} - -// ListCommits lists the commits of a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/commits/#list -func (s *RepositoriesService) ListCommits(ctx context.Context, owner, repo string, opt *CommitsListOptions) ([]*RepositoryCommit, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/commits", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGitSigningPreview) - - var commits []*RepositoryCommit - resp, err := s.client.Do(ctx, req, &commits) - if err != nil { - return nil, resp, err - } - - return commits, resp, nil -} - -// GetCommit fetches the specified commit, including all details about it. -// -// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-a-single-commit -// See also: https://developer.github.com/v3/git/commits/#get-a-single-commit provides the same functionality -func (s *RepositoriesService) GetCommit(ctx context.Context, owner, repo, sha string) (*RepositoryCommit, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGitSigningPreview) - - commit := new(RepositoryCommit) - resp, err := s.client.Do(ctx, req, commit) - if err != nil { - return nil, resp, err - } - - return commit, resp, nil -} - -// GetCommitRaw fetches the specified commit in raw (diff or patch) format. -func (s *RepositoriesService) GetCommitRaw(ctx context.Context, owner string, repo string, sha string, opt RawOptions) (string, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, sha) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return "", nil, err - } - - switch opt.Type { - case Diff: - req.Header.Set("Accept", mediaTypeV3Diff) - case Patch: - req.Header.Set("Accept", mediaTypeV3Patch) - default: - return "", nil, fmt.Errorf("unsupported raw type %d", opt.Type) - } - - var buf bytes.Buffer - resp, err := s.client.Do(ctx, req, &buf) - if err != nil { - return "", resp, err - } - - return buf.String(), resp, nil -} - -// GetCommitSHA1 gets the SHA-1 of a commit reference. If a last-known SHA1 is -// supplied and no new commits have occurred, a 304 Unmodified response is returned. -// -// GitHub API docs: https://developer.github.com/v3/repos/commits/#get-the-sha-1-of-a-commit-reference -func (s *RepositoriesService) GetCommitSHA1(ctx context.Context, owner, repo, ref, lastSHA string) (string, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/commits/%v", owner, repo, ref) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return "", nil, err - } - if lastSHA != "" { - req.Header.Set("If-None-Match", `"`+lastSHA+`"`) - } - - req.Header.Set("Accept", mediaTypeV3SHA) - - var buf bytes.Buffer - resp, err := s.client.Do(ctx, req, &buf) - if err != nil { - return "", resp, err - } - - return buf.String(), resp, nil -} - -// CompareCommits compares a range of commits with each other. -// todo: support media formats - https://github.com/google/go-github/issues/6 -// -// GitHub API docs: https://developer.github.com/v3/repos/commits/index.html#compare-two-commits -func (s *RepositoriesService) CompareCommits(ctx context.Context, owner, repo string, base, head string) (*CommitsComparison, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/compare/%v...%v", owner, repo, base, head) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - comp := new(CommitsComparison) - resp, err := s.client.Do(ctx, req, comp) - if err != nil { - return nil, resp, err - } - - return comp, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/repos_community_health.go b/vendor/github.com/google/go-github/github/repos_community_health.go deleted file mode 100644 index b5c75d6f562..00000000000 --- a/vendor/github.com/google/go-github/github/repos_community_health.go +++ /dev/null @@ -1,57 +0,0 @@ -// Copyright 2017 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// Metric represents the different fields for one file in community health files. -type Metric struct { - Name *string `json:"name"` - Key *string `json:"key"` - URL *string `json:"url"` - HTMLURL *string `json:"html_url"` -} - -// CommunityHealthFiles represents the different files in the community health metrics response. -type CommunityHealthFiles struct { - CodeOfConduct *Metric `json:"code_of_conduct"` - Contributing *Metric `json:"contributing"` - License *Metric `json:"license"` - Readme *Metric `json:"readme"` -} - -// CommunityHealthMetrics represents a response containing the community metrics of a repository. -type CommunityHealthMetrics struct { - HealthPercentage *int `json:"health_percentage"` - Files *CommunityHealthFiles `json:"files"` - UpdatedAt *time.Time `json:"updated_at"` -} - -// GetCommunityHealthMetrics retrieves all the community health metrics for a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/community/#retrieve-community-health-metrics -func (s *RepositoriesService) GetCommunityHealthMetrics(ctx context.Context, owner, repo string) (*CommunityHealthMetrics, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/community/profile", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeRepositoryCommunityHealthMetricsPreview) - - metrics := &CommunityHealthMetrics{} - resp, err := s.client.Do(ctx, req, metrics) - if err != nil { - return nil, resp, err - } - - return metrics, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/repos_contents.go b/vendor/github.com/google/go-github/github/repos_contents.go deleted file mode 100644 index ffb56b90df2..00000000000 --- a/vendor/github.com/google/go-github/github/repos_contents.go +++ /dev/null @@ -1,266 +0,0 @@ -// Copyright 2013 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Repository contents API methods. -// GitHub API docs: https://developer.github.com/v3/repos/contents/ - -package github - -import ( - "context" - "encoding/base64" - "encoding/json" - "fmt" - "io" - "net/http" - "net/url" - "path" -) - -// RepositoryContent represents a file or directory in a github repository. -type RepositoryContent struct { - Type *string `json:"type,omitempty"` - Encoding *string `json:"encoding,omitempty"` - Size *int `json:"size,omitempty"` - Name *string `json:"name,omitempty"` - Path *string `json:"path,omitempty"` - // Content contains the actual file content, which may be encoded. - // Callers should call GetContent which will decode the content if - // necessary. - Content *string `json:"content,omitempty"` - SHA *string `json:"sha,omitempty"` - URL *string `json:"url,omitempty"` - GitURL *string `json:"git_url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - DownloadURL *string `json:"download_url,omitempty"` -} - -// RepositoryContentResponse holds the parsed response from CreateFile, UpdateFile, and DeleteFile. -type RepositoryContentResponse struct { - Content *RepositoryContent `json:"content,omitempty"` - Commit `json:"commit,omitempty"` -} - -// RepositoryContentFileOptions specifies optional parameters for CreateFile, UpdateFile, and DeleteFile. -type RepositoryContentFileOptions struct { - Message *string `json:"message,omitempty"` - Content []byte `json:"content,omitempty"` // unencoded - SHA *string `json:"sha,omitempty"` - Branch *string `json:"branch,omitempty"` - Author *CommitAuthor `json:"author,omitempty"` - Committer *CommitAuthor `json:"committer,omitempty"` -} - -// RepositoryContentGetOptions represents an optional ref parameter, which can be a SHA, -// branch, or tag -type RepositoryContentGetOptions struct { - Ref string `url:"ref,omitempty"` -} - -// String converts RepositoryContent to a string. It's primarily for testing. -func (r RepositoryContent) String() string { - return Stringify(r) -} - -// GetContent returns the content of r, decoding it if necessary. -func (r *RepositoryContent) GetContent() (string, error) { - var encoding string - if r.Encoding != nil { - encoding = *r.Encoding - } - - switch encoding { - case "base64": - c, err := base64.StdEncoding.DecodeString(*r.Content) - return string(c), err - case "": - if r.Content == nil { - return "", nil - } - return *r.Content, nil - default: - return "", fmt.Errorf("unsupported content encoding: %v", encoding) - } -} - -// GetReadme gets the Readme file for the repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-the-readme -func (s *RepositoriesService) GetReadme(ctx context.Context, owner, repo string, opt *RepositoryContentGetOptions) (*RepositoryContent, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/readme", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - readme := new(RepositoryContent) - resp, err := s.client.Do(ctx, req, readme) - if err != nil { - return nil, resp, err - } - return readme, resp, nil -} - -// DownloadContents returns an io.ReadCloser that reads the contents of the -// specified file. This function will work with files of any size, as opposed -// to GetContents which is limited to 1 Mb files. It is the caller's -// responsibility to close the ReadCloser. -func (s *RepositoriesService) DownloadContents(ctx context.Context, owner, repo, filepath string, opt *RepositoryContentGetOptions) (io.ReadCloser, error) { - dir := path.Dir(filepath) - filename := path.Base(filepath) - _, dirContents, _, err := s.GetContents(ctx, owner, repo, dir, opt) - if err != nil { - return nil, err - } - for _, contents := range dirContents { - if *contents.Name == filename { - if contents.DownloadURL == nil || *contents.DownloadURL == "" { - return nil, fmt.Errorf("No download link found for %s", filepath) - } - resp, err := s.client.client.Get(*contents.DownloadURL) - if err != nil { - return nil, err - } - return resp.Body, nil - } - } - return nil, fmt.Errorf("No file named %s found in %s", filename, dir) -} - -// GetContents can return either the metadata and content of a single file -// (when path references a file) or the metadata of all the files and/or -// subdirectories of a directory (when path references a directory). To make it -// easy to distinguish between both result types and to mimic the API as much -// as possible, both result types will be returned but only one will contain a -// value and the other will be nil. -// -// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-contents -func (s *RepositoriesService) GetContents(ctx context.Context, owner, repo, path string, opt *RepositoryContentGetOptions) (fileContent *RepositoryContent, directoryContent []*RepositoryContent, resp *Response, err error) { - escapedPath := (&url.URL{Path: path}).String() - u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, escapedPath) - u, err = addOptions(u, opt) - if err != nil { - return nil, nil, nil, err - } - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, nil, err - } - var rawJSON json.RawMessage - resp, err = s.client.Do(ctx, req, &rawJSON) - if err != nil { - return nil, nil, resp, err - } - fileUnmarshalError := json.Unmarshal(rawJSON, &fileContent) - if fileUnmarshalError == nil { - return fileContent, nil, resp, nil - } - directoryUnmarshalError := json.Unmarshal(rawJSON, &directoryContent) - if directoryUnmarshalError == nil { - return nil, directoryContent, resp, nil - } - return nil, nil, resp, fmt.Errorf("unmarshalling failed for both file and directory content: %s and %s", fileUnmarshalError, directoryUnmarshalError) -} - -// CreateFile creates a new file in a repository at the given path and returns -// the commit and file metadata. -// -// GitHub API docs: https://developer.github.com/v3/repos/contents/#create-a-file -func (s *RepositoriesService) CreateFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) - req, err := s.client.NewRequest("PUT", u, opt) - if err != nil { - return nil, nil, err - } - createResponse := new(RepositoryContentResponse) - resp, err := s.client.Do(ctx, req, createResponse) - if err != nil { - return nil, resp, err - } - return createResponse, resp, nil -} - -// UpdateFile updates a file in a repository at the given path and returns the -// commit and file metadata. Requires the blob SHA of the file being updated. -// -// GitHub API docs: https://developer.github.com/v3/repos/contents/#update-a-file -func (s *RepositoriesService) UpdateFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) - req, err := s.client.NewRequest("PUT", u, opt) - if err != nil { - return nil, nil, err - } - updateResponse := new(RepositoryContentResponse) - resp, err := s.client.Do(ctx, req, updateResponse) - if err != nil { - return nil, resp, err - } - return updateResponse, resp, nil -} - -// DeleteFile deletes a file from a repository and returns the commit. -// Requires the blob SHA of the file to be deleted. -// -// GitHub API docs: https://developer.github.com/v3/repos/contents/#delete-a-file -func (s *RepositoriesService) DeleteFile(ctx context.Context, owner, repo, path string, opt *RepositoryContentFileOptions) (*RepositoryContentResponse, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/contents/%s", owner, repo, path) - req, err := s.client.NewRequest("DELETE", u, opt) - if err != nil { - return nil, nil, err - } - deleteResponse := new(RepositoryContentResponse) - resp, err := s.client.Do(ctx, req, deleteResponse) - if err != nil { - return nil, resp, err - } - return deleteResponse, resp, nil -} - -// archiveFormat is used to define the archive type when calling GetArchiveLink. -type archiveFormat string - -const ( - // Tarball specifies an archive in gzipped tar format. - Tarball archiveFormat = "tarball" - - // Zipball specifies an archive in zip format. - Zipball archiveFormat = "zipball" -) - -// GetArchiveLink returns an URL to download a tarball or zipball archive for a -// repository. The archiveFormat can be specified by either the github.Tarball -// or github.Zipball constant. -// -// GitHub API docs: https://developer.github.com/v3/repos/contents/#get-archive-link -func (s *RepositoriesService) GetArchiveLink(ctx context.Context, owner, repo string, archiveformat archiveFormat, opt *RepositoryContentGetOptions) (*url.URL, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/%s", owner, repo, archiveformat) - if opt != nil && opt.Ref != "" { - u += fmt.Sprintf("/%s", opt.Ref) - } - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - var resp *http.Response - // Use http.DefaultTransport if no custom Transport is configured - req = withContext(ctx, req) - if s.client.client.Transport == nil { - resp, err = http.DefaultTransport.RoundTrip(req) - } else { - resp, err = s.client.client.Transport.RoundTrip(req) - } - if err != nil { - return nil, nil, err - } - resp.Body.Close() - if resp.StatusCode != http.StatusFound { - return nil, newResponse(resp), fmt.Errorf("unexpected status code: %s", resp.Status) - } - parsedURL, err := url.Parse(resp.Header.Get("Location")) - return parsedURL, newResponse(resp), err -} diff --git a/vendor/github.com/google/go-github/github/repos_deployments.go b/vendor/github.com/google/go-github/github/repos_deployments.go deleted file mode 100644 index 1300f05ee29..00000000000 --- a/vendor/github.com/google/go-github/github/repos_deployments.go +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright 2014 The go-github 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 github - -import ( - "context" - "encoding/json" - "fmt" - "strings" -) - -// Deployment represents a deployment in a repo -type Deployment struct { - URL *string `json:"url,omitempty"` - ID *int64 `json:"id,omitempty"` - SHA *string `json:"sha,omitempty"` - Ref *string `json:"ref,omitempty"` - Task *string `json:"task,omitempty"` - Payload json.RawMessage `json:"payload,omitempty"` - Environment *string `json:"environment,omitempty"` - Description *string `json:"description,omitempty"` - Creator *User `json:"creator,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - StatusesURL *string `json:"statuses_url,omitempty"` - RepositoryURL *string `json:"repository_url,omitempty"` - NodeID *string `json:"node_id,omitempty"` -} - -// DeploymentRequest represents a deployment request -type DeploymentRequest struct { - Ref *string `json:"ref,omitempty"` - Task *string `json:"task,omitempty"` - AutoMerge *bool `json:"auto_merge,omitempty"` - RequiredContexts *[]string `json:"required_contexts,omitempty"` - Payload *string `json:"payload,omitempty"` - Environment *string `json:"environment,omitempty"` - Description *string `json:"description,omitempty"` - TransientEnvironment *bool `json:"transient_environment,omitempty"` - ProductionEnvironment *bool `json:"production_environment,omitempty"` -} - -// DeploymentsListOptions specifies the optional parameters to the -// RepositoriesService.ListDeployments method. -type DeploymentsListOptions struct { - // SHA of the Deployment. - SHA string `url:"sha,omitempty"` - - // List deployments for a given ref. - Ref string `url:"ref,omitempty"` - - // List deployments for a given task. - Task string `url:"task,omitempty"` - - // List deployments for a given environment. - Environment string `url:"environment,omitempty"` - - ListOptions -} - -// ListDeployments lists the deployments of a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/deployments/#list-deployments -func (s *RepositoriesService) ListDeployments(ctx context.Context, owner, repo string, opt *DeploymentsListOptions) ([]*Deployment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/deployments", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var deployments []*Deployment - resp, err := s.client.Do(ctx, req, &deployments) - if err != nil { - return nil, resp, err - } - - return deployments, resp, nil -} - -// GetDeployment returns a single deployment of a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/deployments/#get-a-single-deployment -func (s *RepositoriesService) GetDeployment(ctx context.Context, owner, repo string, deploymentID int64) (*Deployment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/deployments/%v", owner, repo, deploymentID) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - deployment := new(Deployment) - resp, err := s.client.Do(ctx, req, deployment) - if err != nil { - return nil, resp, err - } - - return deployment, resp, nil -} - -// CreateDeployment creates a new deployment for a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/deployments/#create-a-deployment -func (s *RepositoriesService) CreateDeployment(ctx context.Context, owner, repo string, request *DeploymentRequest) (*Deployment, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/deployments", owner, repo) - - req, err := s.client.NewRequest("POST", u, request) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeGraphQLNodeIDPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - d := new(Deployment) - resp, err := s.client.Do(ctx, req, d) - if err != nil { - return nil, resp, err - } - - return d, resp, nil -} - -// DeploymentStatus represents the status of a -// particular deployment. -type DeploymentStatus struct { - ID *int64 `json:"id,omitempty"` - // State is the deployment state. - // Possible values are: "pending", "success", "failure", "error", "inactive". - State *string `json:"state,omitempty"` - Creator *User `json:"creator,omitempty"` - Description *string `json:"description,omitempty"` - TargetURL *string `json:"target_url,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - DeploymentURL *string `json:"deployment_url,omitempty"` - RepositoryURL *string `json:"repository_url,omitempty"` - NodeID *string `json:"node_id,omitempty"` -} - -// DeploymentStatusRequest represents a deployment request -type DeploymentStatusRequest struct { - State *string `json:"state,omitempty"` - LogURL *string `json:"log_url,omitempty"` - Description *string `json:"description,omitempty"` - EnvironmentURL *string `json:"environment_url,omitempty"` - AutoInactive *bool `json:"auto_inactive,omitempty"` -} - -// ListDeploymentStatuses lists the statuses of a given deployment of a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/deployments/#list-deployment-statuses -func (s *RepositoriesService) ListDeploymentStatuses(ctx context.Context, owner, repo string, deployment int64, opt *ListOptions) ([]*DeploymentStatus, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses", owner, repo, deployment) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGraphQLNodeIDPreview) - - var statuses []*DeploymentStatus - resp, err := s.client.Do(ctx, req, &statuses) - if err != nil { - return nil, resp, err - } - - return statuses, resp, nil -} - -// GetDeploymentStatus returns a single deployment status of a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/deployments/#get-a-single-deployment-status -func (s *RepositoriesService) GetDeploymentStatus(ctx context.Context, owner, repo string, deploymentID, deploymentStatusID int64) (*DeploymentStatus, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses/%v", owner, repo, deploymentID, deploymentStatusID) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeGraphQLNodeIDPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - d := new(DeploymentStatus) - resp, err := s.client.Do(ctx, req, d) - if err != nil { - return nil, resp, err - } - - return d, resp, nil -} - -// CreateDeploymentStatus creates a new status for a deployment. -// -// GitHub API docs: https://developer.github.com/v3/repos/deployments/#create-a-deployment-status -func (s *RepositoriesService) CreateDeploymentStatus(ctx context.Context, owner, repo string, deployment int64, request *DeploymentStatusRequest) (*DeploymentStatus, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/deployments/%v/statuses", owner, repo, deployment) - - req, err := s.client.NewRequest("POST", u, request) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept headers when APIs fully launch. - acceptHeaders := []string{mediaTypeDeploymentStatusPreview, mediaTypeGraphQLNodeIDPreview} - req.Header.Set("Accept", strings.Join(acceptHeaders, ", ")) - - d := new(DeploymentStatus) - resp, err := s.client.Do(ctx, req, d) - if err != nil { - return nil, resp, err - } - - return d, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/repos_forks.go b/vendor/github.com/google/go-github/github/repos_forks.go deleted file mode 100644 index 4ca19a42dc3..00000000000 --- a/vendor/github.com/google/go-github/github/repos_forks.go +++ /dev/null @@ -1,85 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// RepositoryListForksOptions specifies the optional parameters to the -// RepositoriesService.ListForks method. -type RepositoryListForksOptions struct { - // How to sort the forks list. Possible values are: newest, oldest, - // watchers. Default is "newest". - Sort string `url:"sort,omitempty"` - - ListOptions -} - -// ListForks lists the forks of the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/forks/#list-forks -func (s *RepositoriesService) ListForks(ctx context.Context, owner, repo string, opt *RepositoryListForksOptions) ([]*Repository, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/forks", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when topics API fully launches. - req.Header.Set("Accept", mediaTypeTopicsPreview) - - var repos []*Repository - resp, err := s.client.Do(ctx, req, &repos) - if err != nil { - return nil, resp, err - } - - return repos, resp, nil -} - -// RepositoryCreateForkOptions specifies the optional parameters to the -// RepositoriesService.CreateFork method. -type RepositoryCreateForkOptions struct { - // The organization to fork the repository into. - Organization string `url:"organization,omitempty"` -} - -// CreateFork creates a fork of the specified repository. -// -// This method might return an *AcceptedError and a status code of -// 202. This is because this is the status that GitHub returns to signify that -// it is now computing creating the fork in a background task. -// A follow up request, after a delay of a second or so, should result -// in a successful request. -// -// GitHub API docs: https://developer.github.com/v3/repos/forks/#create-a-fork -func (s *RepositoriesService) CreateFork(ctx context.Context, owner, repo string, opt *RepositoryCreateForkOptions) (*Repository, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/forks", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("POST", u, nil) - if err != nil { - return nil, nil, err - } - - fork := new(Repository) - resp, err := s.client.Do(ctx, req, fork) - if err != nil { - return nil, resp, err - } - - return fork, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/repos_hooks.go b/vendor/github.com/google/go-github/github/repos_hooks.go deleted file mode 100644 index f7ab3a13d63..00000000000 --- a/vendor/github.com/google/go-github/github/repos_hooks.go +++ /dev/null @@ -1,192 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// WebHookPayload represents the data that is received from GitHub when a push -// event hook is triggered. The format of these payloads pre-date most of the -// GitHub v3 API, so there are lots of minor incompatibilities with the types -// defined in the rest of the API. Therefore, several types are duplicated -// here to account for these differences. -// -// GitHub API docs: https://help.github.com/articles/post-receive-hooks -type WebHookPayload struct { - After *string `json:"after,omitempty"` - Before *string `json:"before,omitempty"` - Commits []WebHookCommit `json:"commits,omitempty"` - Compare *string `json:"compare,omitempty"` - Created *bool `json:"created,omitempty"` - Deleted *bool `json:"deleted,omitempty"` - Forced *bool `json:"forced,omitempty"` - HeadCommit *WebHookCommit `json:"head_commit,omitempty"` - Pusher *User `json:"pusher,omitempty"` - Ref *string `json:"ref,omitempty"` - Repo *Repository `json:"repository,omitempty"` - Sender *User `json:"sender,omitempty"` -} - -func (w WebHookPayload) String() string { - return Stringify(w) -} - -// WebHookCommit represents the commit variant we receive from GitHub in a -// WebHookPayload. -type WebHookCommit struct { - Added []string `json:"added,omitempty"` - Author *WebHookAuthor `json:"author,omitempty"` - Committer *WebHookAuthor `json:"committer,omitempty"` - Distinct *bool `json:"distinct,omitempty"` - ID *string `json:"id,omitempty"` - Message *string `json:"message,omitempty"` - Modified []string `json:"modified,omitempty"` - Removed []string `json:"removed,omitempty"` - Timestamp *time.Time `json:"timestamp,omitempty"` -} - -func (w WebHookCommit) String() string { - return Stringify(w) -} - -// WebHookAuthor represents the author or committer of a commit, as specified -// in a WebHookCommit. The commit author may not correspond to a GitHub User. -type WebHookAuthor struct { - Email *string `json:"email,omitempty"` - Name *string `json:"name,omitempty"` - Username *string `json:"username,omitempty"` -} - -func (w WebHookAuthor) String() string { - return Stringify(w) -} - -// Hook represents a GitHub (web and service) hook for a repository. -type Hook struct { - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` - Name *string `json:"name,omitempty"` - URL *string `json:"url,omitempty"` - Events []string `json:"events,omitempty"` - Active *bool `json:"active,omitempty"` - Config map[string]interface{} `json:"config,omitempty"` - ID *int64 `json:"id,omitempty"` -} - -func (h Hook) String() string { - return Stringify(h) -} - -// CreateHook creates a Hook for the specified repository. -// Name and Config are required fields. -// -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#create-a-hook -func (s *RepositoriesService) CreateHook(ctx context.Context, owner, repo string, hook *Hook) (*Hook, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/hooks", owner, repo) - req, err := s.client.NewRequest("POST", u, hook) - if err != nil { - return nil, nil, err - } - - h := new(Hook) - resp, err := s.client.Do(ctx, req, h) - if err != nil { - return nil, resp, err - } - - return h, resp, nil -} - -// ListHooks lists all Hooks for the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#list -func (s *RepositoriesService) ListHooks(ctx context.Context, owner, repo string, opt *ListOptions) ([]*Hook, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/hooks", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var hooks []*Hook - resp, err := s.client.Do(ctx, req, &hooks) - if err != nil { - return nil, resp, err - } - - return hooks, resp, nil -} - -// GetHook returns a single specified Hook. -// -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#get-single-hook -func (s *RepositoriesService) GetHook(ctx context.Context, owner, repo string, id int64) (*Hook, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - hook := new(Hook) - resp, err := s.client.Do(ctx, req, hook) - return hook, resp, err -} - -// EditHook updates a specified Hook. -// -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#edit-a-hook -func (s *RepositoriesService) EditHook(ctx context.Context, owner, repo string, id int64, hook *Hook) (*Hook, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) - req, err := s.client.NewRequest("PATCH", u, hook) - if err != nil { - return nil, nil, err - } - h := new(Hook) - resp, err := s.client.Do(ctx, req, h) - return h, resp, err -} - -// DeleteHook deletes a specified Hook. -// -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#delete-a-hook -func (s *RepositoriesService) DeleteHook(ctx context.Context, owner, repo string, id int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/hooks/%d", owner, repo, id) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} - -// PingHook triggers a 'ping' event to be sent to the Hook. -// -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#ping-a-hook -func (s *RepositoriesService) PingHook(ctx context.Context, owner, repo string, id int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/hooks/%d/pings", owner, repo, id) - req, err := s.client.NewRequest("POST", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} - -// TestHook triggers a test Hook by github. -// -// GitHub API docs: https://developer.github.com/v3/repos/hooks/#test-a-push-hook -func (s *RepositoriesService) TestHook(ctx context.Context, owner, repo string, id int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/hooks/%d/tests", owner, repo, id) - req, err := s.client.NewRequest("POST", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/repos_invitations.go b/vendor/github.com/google/go-github/github/repos_invitations.go deleted file mode 100644 index 34bf3830fb1..00000000000 --- a/vendor/github.com/google/go-github/github/repos_invitations.go +++ /dev/null @@ -1,98 +0,0 @@ -// Copyright 2016 The go-github 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 github - -import ( - "context" - "fmt" -) - -// RepositoryInvitation represents an invitation to collaborate on a repo. -type RepositoryInvitation struct { - ID *int64 `json:"id,omitempty"` - Repo *Repository `json:"repository,omitempty"` - Invitee *User `json:"invitee,omitempty"` - Inviter *User `json:"inviter,omitempty"` - - // Permissions represents the permissions that the associated user will have - // on the repository. Possible values are: "read", "write", "admin". - Permissions *string `json:"permissions,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` -} - -// ListInvitations lists all currently-open repository invitations. -// -// GitHub API docs: https://developer.github.com/v3/repos/invitations/#list-invitations-for-a-repository -func (s *RepositoriesService) ListInvitations(ctx context.Context, owner, repo string, opt *ListOptions) ([]*RepositoryInvitation, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/invitations", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) - - invites := []*RepositoryInvitation{} - resp, err := s.client.Do(ctx, req, &invites) - if err != nil { - return nil, resp, err - } - - return invites, resp, nil -} - -// DeleteInvitation deletes a repository invitation. -// -// GitHub API docs: https://developer.github.com/v3/repos/invitations/#delete-a-repository-invitation -func (s *RepositoriesService) DeleteInvitation(ctx context.Context, owner, repo string, invitationID int64) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/invitations/%v", owner, repo, invitationID) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) - - return s.client.Do(ctx, req, nil) -} - -// UpdateInvitation updates the permissions associated with a repository -// invitation. -// -// permissions represents the permissions that the associated user will have -// on the repository. Possible values are: "read", "write", "admin". -// -// GitHub API docs: https://developer.github.com/v3/repos/invitations/#update-a-repository-invitation -func (s *RepositoriesService) UpdateInvitation(ctx context.Context, owner, repo string, invitationID int64, permissions string) (*RepositoryInvitation, *Response, error) { - opts := &struct { - Permissions string `json:"permissions"` - }{Permissions: permissions} - u := fmt.Sprintf("repos/%v/%v/invitations/%v", owner, repo, invitationID) - req, err := s.client.NewRequest("PATCH", u, opts) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) - - invite := &RepositoryInvitation{} - resp, err := s.client.Do(ctx, req, invite) - if err != nil { - return nil, resp, err - } - - return invite, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/repos_keys.go b/vendor/github.com/google/go-github/github/repos_keys.go deleted file mode 100644 index 966d7b540b1..00000000000 --- a/vendor/github.com/google/go-github/github/repos_keys.go +++ /dev/null @@ -1,111 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// The Key type is defined in users_keys.go - -// ListKeys lists the deploy keys for a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/keys/#list -func (s *RepositoriesService) ListKeys(ctx context.Context, owner string, repo string, opt *ListOptions) ([]*Key, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/keys", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var keys []*Key - resp, err := s.client.Do(ctx, req, &keys) - if err != nil { - return nil, resp, err - } - - return keys, resp, nil -} - -// GetKey fetches a single deploy key. -// -// GitHub API docs: https://developer.github.com/v3/repos/keys/#get -func (s *RepositoriesService) GetKey(ctx context.Context, owner string, repo string, id int64) (*Key, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/keys/%v", owner, repo, id) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - key := new(Key) - resp, err := s.client.Do(ctx, req, key) - if err != nil { - return nil, resp, err - } - - return key, resp, nil -} - -// CreateKey adds a deploy key for a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/keys/#create -func (s *RepositoriesService) CreateKey(ctx context.Context, owner string, repo string, key *Key) (*Key, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/keys", owner, repo) - - req, err := s.client.NewRequest("POST", u, key) - if err != nil { - return nil, nil, err - } - - k := new(Key) - resp, err := s.client.Do(ctx, req, k) - if err != nil { - return nil, resp, err - } - - return k, resp, nil -} - -// EditKey edits a deploy key. -// -// GitHub API docs: https://developer.github.com/v3/repos/keys/#edit -func (s *RepositoriesService) EditKey(ctx context.Context, owner string, repo string, id int, key *Key) (*Key, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/keys/%v", owner, repo, id) - - req, err := s.client.NewRequest("PATCH", u, key) - if err != nil { - return nil, nil, err - } - - k := new(Key) - resp, err := s.client.Do(ctx, req, k) - if err != nil { - return nil, resp, err - } - - return k, resp, nil -} - -// DeleteKey deletes a deploy key. -// -// GitHub API docs: https://developer.github.com/v3/repos/keys/#delete -func (s *RepositoriesService) DeleteKey(ctx context.Context, owner string, repo string, id int) (*Response, error) { - u := fmt.Sprintf("repos/%v/%v/keys/%v", owner, repo, id) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/repos_merging.go b/vendor/github.com/google/go-github/github/repos_merging.go deleted file mode 100644 index 04383c1ae32..00000000000 --- a/vendor/github.com/google/go-github/github/repos_merging.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2014 The go-github 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 github - -import ( - "context" - "fmt" -) - -// RepositoryMergeRequest represents a request to merge a branch in a -// repository. -type RepositoryMergeRequest struct { - Base *string `json:"base,omitempty"` - Head *string `json:"head,omitempty"` - CommitMessage *string `json:"commit_message,omitempty"` -} - -// Merge a branch in the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/merging/#perform-a-merge -func (s *RepositoriesService) Merge(ctx context.Context, owner, repo string, request *RepositoryMergeRequest) (*RepositoryCommit, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/merges", owner, repo) - req, err := s.client.NewRequest("POST", u, request) - if err != nil { - return nil, nil, err - } - - commit := new(RepositoryCommit) - resp, err := s.client.Do(ctx, req, commit) - if err != nil { - return nil, resp, err - } - - return commit, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/repos_pages.go b/vendor/github.com/google/go-github/github/repos_pages.go deleted file mode 100644 index 94a95f2b8ed..00000000000 --- a/vendor/github.com/google/go-github/github/repos_pages.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2014 The go-github 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 github - -import ( - "context" - "fmt" -) - -// Pages represents a GitHub Pages site configuration. -type Pages struct { - URL *string `json:"url,omitempty"` - Status *string `json:"status,omitempty"` - CNAME *string `json:"cname,omitempty"` - Custom404 *bool `json:"custom_404,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` -} - -// PagesError represents a build error for a GitHub Pages site. -type PagesError struct { - Message *string `json:"message,omitempty"` -} - -// PagesBuild represents the build information for a GitHub Pages site. -type PagesBuild struct { - URL *string `json:"url,omitempty"` - Status *string `json:"status,omitempty"` - Error *PagesError `json:"error,omitempty"` - Pusher *User `json:"pusher,omitempty"` - Commit *string `json:"commit,omitempty"` - Duration *int `json:"duration,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` -} - -// GetPagesInfo fetches information about a GitHub Pages site. -// -// GitHub API docs: https://developer.github.com/v3/repos/pages/#get-information-about-a-pages-site -func (s *RepositoriesService) GetPagesInfo(ctx context.Context, owner, repo string) (*Pages, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pages", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypePagesPreview) - - site := new(Pages) - resp, err := s.client.Do(ctx, req, site) - if err != nil { - return nil, resp, err - } - - return site, resp, nil -} - -// ListPagesBuilds lists the builds for a GitHub Pages site. -// -// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-pages-builds -func (s *RepositoriesService) ListPagesBuilds(ctx context.Context, owner, repo string, opt *ListOptions) ([]*PagesBuild, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var pages []*PagesBuild - resp, err := s.client.Do(ctx, req, &pages) - if err != nil { - return nil, resp, err - } - - return pages, resp, nil -} - -// GetLatestPagesBuild fetches the latest build information for a GitHub pages site. -// -// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-latest-pages-build -func (s *RepositoriesService) GetLatestPagesBuild(ctx context.Context, owner, repo string) (*PagesBuild, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pages/builds/latest", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - build := new(PagesBuild) - resp, err := s.client.Do(ctx, req, build) - if err != nil { - return nil, resp, err - } - - return build, resp, nil -} - -// GetPageBuild fetches the specific build information for a GitHub pages site. -// -// GitHub API docs: https://developer.github.com/v3/repos/pages/#list-a-specific-pages-build -func (s *RepositoriesService) GetPageBuild(ctx context.Context, owner, repo string, id int64) (*PagesBuild, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pages/builds/%v", owner, repo, id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - build := new(PagesBuild) - resp, err := s.client.Do(ctx, req, build) - if err != nil { - return nil, resp, err - } - - return build, resp, nil -} - -// RequestPageBuild requests a build of a GitHub Pages site without needing to push new commit. -// -// GitHub API docs: https://developer.github.com/v3/repos/pages/#request-a-page-build -func (s *RepositoriesService) RequestPageBuild(ctx context.Context, owner, repo string) (*PagesBuild, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/pages/builds", owner, repo) - req, err := s.client.NewRequest("POST", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypePagesPreview) - - build := new(PagesBuild) - resp, err := s.client.Do(ctx, req, build) - if err != nil { - return nil, resp, err - } - - return build, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/repos_projects.go b/vendor/github.com/google/go-github/github/repos_projects.go deleted file mode 100644 index 770ffc76fa6..00000000000 --- a/vendor/github.com/google/go-github/github/repos_projects.go +++ /dev/null @@ -1,69 +0,0 @@ -// Copyright 2017 The go-github 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 github - -import ( - "context" - "fmt" -) - -// ProjectListOptions specifies the optional parameters to the -// OrganizationsService.ListProjects and RepositoriesService.ListProjects methods. -type ProjectListOptions struct { - // Indicates the state of the projects to return. Can be either open, closed, or all. Default: open - State string `url:"state,omitempty"` - - ListOptions -} - -// ListProjects lists the projects for a repo. -// -// GitHub API docs: https://developer.github.com/v3/projects/#list-repository-projects -func (s *RepositoriesService) ListProjects(ctx context.Context, owner, repo string, opt *ProjectListOptions) ([]*Project, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/projects", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - var projects []*Project - resp, err := s.client.Do(ctx, req, &projects) - if err != nil { - return nil, resp, err - } - - return projects, resp, nil -} - -// CreateProject creates a GitHub Project for the specified repository. -// -// GitHub API docs: https://developer.github.com/v3/projects/#create-a-repository-project -func (s *RepositoriesService) CreateProject(ctx context.Context, owner, repo string, opt *ProjectOptions) (*Project, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/projects", owner, repo) - req, err := s.client.NewRequest("POST", u, opt) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeProjectsPreview) - - project := &Project{} - resp, err := s.client.Do(ctx, req, project) - if err != nil { - return nil, resp, err - } - - return project, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/repos_releases.go b/vendor/github.com/google/go-github/github/repos_releases.go deleted file mode 100644 index 7ad2b278ab5..00000000000 --- a/vendor/github.com/google/go-github/github/repos_releases.go +++ /dev/null @@ -1,327 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "errors" - "fmt" - "io" - "mime" - "net/http" - "os" - "path/filepath" - "strings" -) - -// RepositoryRelease represents a GitHub release in a repository. -type RepositoryRelease struct { - ID *int64 `json:"id,omitempty"` - TagName *string `json:"tag_name,omitempty"` - TargetCommitish *string `json:"target_commitish,omitempty"` - Name *string `json:"name,omitempty"` - Body *string `json:"body,omitempty"` - Draft *bool `json:"draft,omitempty"` - Prerelease *bool `json:"prerelease,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - PublishedAt *Timestamp `json:"published_at,omitempty"` - URL *string `json:"url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - AssetsURL *string `json:"assets_url,omitempty"` - Assets []ReleaseAsset `json:"assets,omitempty"` - UploadURL *string `json:"upload_url,omitempty"` - ZipballURL *string `json:"zipball_url,omitempty"` - TarballURL *string `json:"tarball_url,omitempty"` - Author *User `json:"author,omitempty"` -} - -func (r RepositoryRelease) String() string { - return Stringify(r) -} - -// ReleaseAsset represents a GitHub release asset in a repository. -type ReleaseAsset struct { - ID *int64 `json:"id,omitempty"` - URL *string `json:"url,omitempty"` - Name *string `json:"name,omitempty"` - Label *string `json:"label,omitempty"` - State *string `json:"state,omitempty"` - ContentType *string `json:"content_type,omitempty"` - Size *int `json:"size,omitempty"` - DownloadCount *int `json:"download_count,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - BrowserDownloadURL *string `json:"browser_download_url,omitempty"` - Uploader *User `json:"uploader,omitempty"` -} - -func (r ReleaseAsset) String() string { - return Stringify(r) -} - -// ListReleases lists the releases for a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/releases/#list-releases-for-a-repository -func (s *RepositoriesService) ListReleases(ctx context.Context, owner, repo string, opt *ListOptions) ([]*RepositoryRelease, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var releases []*RepositoryRelease - resp, err := s.client.Do(ctx, req, &releases) - if err != nil { - return nil, resp, err - } - return releases, resp, nil -} - -// GetRelease fetches a single release. -// -// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-single-release -func (s *RepositoriesService) GetRelease(ctx context.Context, owner, repo string, id int64) (*RepositoryRelease, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id) - return s.getSingleRelease(ctx, u) -} - -// GetLatestRelease fetches the latest published release for the repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-the-latest-release -func (s *RepositoriesService) GetLatestRelease(ctx context.Context, owner, repo string) (*RepositoryRelease, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/latest", owner, repo) - return s.getSingleRelease(ctx, u) -} - -// GetReleaseByTag fetches a release with the specified tag. -// -// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-release-by-tag-name -func (s *RepositoriesService) GetReleaseByTag(ctx context.Context, owner, repo, tag string) (*RepositoryRelease, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/tags/%s", owner, repo, tag) - return s.getSingleRelease(ctx, u) -} - -func (s *RepositoriesService) getSingleRelease(ctx context.Context, url string) (*RepositoryRelease, *Response, error) { - req, err := s.client.NewRequest("GET", url, nil) - if err != nil { - return nil, nil, err - } - - release := new(RepositoryRelease) - resp, err := s.client.Do(ctx, req, release) - if err != nil { - return nil, resp, err - } - return release, resp, nil -} - -// CreateRelease adds a new release for a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/releases/#create-a-release -func (s *RepositoriesService) CreateRelease(ctx context.Context, owner, repo string, release *RepositoryRelease) (*RepositoryRelease, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases", owner, repo) - - req, err := s.client.NewRequest("POST", u, release) - if err != nil { - return nil, nil, err - } - - r := new(RepositoryRelease) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - return r, resp, nil -} - -// EditRelease edits a repository release. -// -// GitHub API docs: https://developer.github.com/v3/repos/releases/#edit-a-release -func (s *RepositoriesService) EditRelease(ctx context.Context, owner, repo string, id int64, release *RepositoryRelease) (*RepositoryRelease, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id) - - req, err := s.client.NewRequest("PATCH", u, release) - if err != nil { - return nil, nil, err - } - - r := new(RepositoryRelease) - resp, err := s.client.Do(ctx, req, r) - if err != nil { - return nil, resp, err - } - return r, resp, nil -} - -// DeleteRelease delete a single release from a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/releases/#delete-a-release -func (s *RepositoriesService) DeleteRelease(ctx context.Context, owner, repo string, id int64) (*Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/%d", owner, repo, id) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} - -// ListReleaseAssets lists the release's assets. -// -// GitHub API docs: https://developer.github.com/v3/repos/releases/#list-assets-for-a-release -func (s *RepositoriesService) ListReleaseAssets(ctx context.Context, owner, repo string, id int64, opt *ListOptions) ([]*ReleaseAsset, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var assets []*ReleaseAsset - resp, err := s.client.Do(ctx, req, &assets) - if err != nil { - return nil, resp, err - } - return assets, resp, nil -} - -// GetReleaseAsset fetches a single release asset. -// -// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-single-release-asset -func (s *RepositoriesService) GetReleaseAsset(ctx context.Context, owner, repo string, id int64) (*ReleaseAsset, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - asset := new(ReleaseAsset) - resp, err := s.client.Do(ctx, req, asset) - if err != nil { - return nil, resp, err - } - return asset, resp, nil -} - -// DownloadReleaseAsset downloads a release asset or returns a redirect URL. -// -// DownloadReleaseAsset returns an io.ReadCloser that reads the contents of the -// specified release asset. It is the caller's responsibility to close the ReadCloser. -// If a redirect is returned, the redirect URL will be returned as a string instead -// of the io.ReadCloser. Exactly one of rc and redirectURL will be zero. -// -// GitHub API docs: https://developer.github.com/v3/repos/releases/#get-a-single-release-asset -func (s *RepositoriesService) DownloadReleaseAsset(ctx context.Context, owner, repo string, id int64) (rc io.ReadCloser, redirectURL string, err error) { - u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, "", err - } - req.Header.Set("Accept", defaultMediaType) - - s.client.clientMu.Lock() - defer s.client.clientMu.Unlock() - - var loc string - saveRedirect := s.client.client.CheckRedirect - s.client.client.CheckRedirect = func(req *http.Request, via []*http.Request) error { - loc = req.URL.String() - return errors.New("disable redirect") - } - defer func() { s.client.client.CheckRedirect = saveRedirect }() - - req = withContext(ctx, req) - resp, err := s.client.client.Do(req) - if err != nil { - if !strings.Contains(err.Error(), "disable redirect") { - return nil, "", err - } - return nil, loc, nil // Intentionally return no error with valid redirect URL. - } - - if err := CheckResponse(resp); err != nil { - resp.Body.Close() - return nil, "", err - } - - return resp.Body, "", nil -} - -// EditReleaseAsset edits a repository release asset. -// -// GitHub API docs: https://developer.github.com/v3/repos/releases/#edit-a-release-asset -func (s *RepositoriesService) EditReleaseAsset(ctx context.Context, owner, repo string, id int64, release *ReleaseAsset) (*ReleaseAsset, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) - - req, err := s.client.NewRequest("PATCH", u, release) - if err != nil { - return nil, nil, err - } - - asset := new(ReleaseAsset) - resp, err := s.client.Do(ctx, req, asset) - if err != nil { - return nil, resp, err - } - return asset, resp, nil -} - -// DeleteReleaseAsset delete a single release asset from a repository. -// -// GitHub API docs: https://developer.github.com/v3/repos/releases/#delete-a-release-asset -func (s *RepositoriesService) DeleteReleaseAsset(ctx context.Context, owner, repo string, id int64) (*Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/assets/%d", owner, repo, id) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - return s.client.Do(ctx, req, nil) -} - -// UploadReleaseAsset creates an asset by uploading a file into a release repository. -// To upload assets that cannot be represented by an os.File, call NewUploadRequest directly. -// -// GitHub API docs: https://developer.github.com/v3/repos/releases/#upload-a-release-asset -func (s *RepositoriesService) UploadReleaseAsset(ctx context.Context, owner, repo string, id int64, opt *UploadOptions, file *os.File) (*ReleaseAsset, *Response, error) { - u := fmt.Sprintf("repos/%s/%s/releases/%d/assets", owner, repo, id) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - stat, err := file.Stat() - if err != nil { - return nil, nil, err - } - if stat.IsDir() { - return nil, nil, errors.New("the asset to upload can't be a directory") - } - - mediaType := mime.TypeByExtension(filepath.Ext(file.Name())) - req, err := s.client.NewUploadRequest(u, file, stat.Size(), mediaType) - if err != nil { - return nil, nil, err - } - - asset := new(ReleaseAsset) - resp, err := s.client.Do(ctx, req, asset) - if err != nil { - return nil, resp, err - } - return asset, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/repos_stats.go b/vendor/github.com/google/go-github/github/repos_stats.go deleted file mode 100644 index bb355aeadd3..00000000000 --- a/vendor/github.com/google/go-github/github/repos_stats.go +++ /dev/null @@ -1,226 +0,0 @@ -// Copyright 2014 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// ContributorStats represents a contributor to a repository and their -// weekly contributions to a given repo. -type ContributorStats struct { - Author *Contributor `json:"author,omitempty"` - Total *int `json:"total,omitempty"` - Weeks []WeeklyStats `json:"weeks,omitempty"` -} - -func (c ContributorStats) String() string { - return Stringify(c) -} - -// WeeklyStats represents the number of additions, deletions and commits -// a Contributor made in a given week. -type WeeklyStats struct { - Week *Timestamp `json:"w,omitempty"` - Additions *int `json:"a,omitempty"` - Deletions *int `json:"d,omitempty"` - Commits *int `json:"c,omitempty"` -} - -func (w WeeklyStats) String() string { - return Stringify(w) -} - -// ListContributorsStats gets a repo's contributor list with additions, -// deletions and commit counts. -// -// If this is the first time these statistics are requested for the given -// repository, this method will return an *AcceptedError and a status code of -// 202. This is because this is the status that GitHub returns to signify that -// it is now computing the requested statistics. A follow up request, after a -// delay of a second or so, should result in a successful request. -// -// GitHub API docs: https://developer.github.com/v3/repos/statistics/#contributors -func (s *RepositoriesService) ListContributorsStats(ctx context.Context, owner, repo string) ([]*ContributorStats, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/stats/contributors", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var contributorStats []*ContributorStats - resp, err := s.client.Do(ctx, req, &contributorStats) - if err != nil { - return nil, resp, err - } - - return contributorStats, resp, nil -} - -// WeeklyCommitActivity represents the weekly commit activity for a repository. -// The days array is a group of commits per day, starting on Sunday. -type WeeklyCommitActivity struct { - Days []int `json:"days,omitempty"` - Total *int `json:"total,omitempty"` - Week *Timestamp `json:"week,omitempty"` -} - -func (w WeeklyCommitActivity) String() string { - return Stringify(w) -} - -// ListCommitActivity returns the last year of commit activity -// grouped by week. The days array is a group of commits per day, -// starting on Sunday. -// -// If this is the first time these statistics are requested for the given -// repository, this method will return an *AcceptedError and a status code of -// 202. This is because this is the status that GitHub returns to signify that -// it is now computing the requested statistics. A follow up request, after a -// delay of a second or so, should result in a successful request. -// -// GitHub API docs: https://developer.github.com/v3/repos/statistics/#commit-activity -func (s *RepositoriesService) ListCommitActivity(ctx context.Context, owner, repo string) ([]*WeeklyCommitActivity, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/stats/commit_activity", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var weeklyCommitActivity []*WeeklyCommitActivity - resp, err := s.client.Do(ctx, req, &weeklyCommitActivity) - if err != nil { - return nil, resp, err - } - - return weeklyCommitActivity, resp, nil -} - -// ListCodeFrequency returns a weekly aggregate of the number of additions and -// deletions pushed to a repository. Returned WeeklyStats will contain -// additions and deletions, but not total commits. -// -// If this is the first time these statistics are requested for the given -// repository, this method will return an *AcceptedError and a status code of -// 202. This is because this is the status that GitHub returns to signify that -// it is now computing the requested statistics. A follow up request, after a -// delay of a second or so, should result in a successful request. -// -// GitHub API docs: https://developer.github.com/v3/repos/statistics/#code-frequency -func (s *RepositoriesService) ListCodeFrequency(ctx context.Context, owner, repo string) ([]*WeeklyStats, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/stats/code_frequency", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var weeks [][]int - resp, err := s.client.Do(ctx, req, &weeks) - - // convert int slices into WeeklyStats - var stats []*WeeklyStats - for _, week := range weeks { - if len(week) != 3 { - continue - } - stat := &WeeklyStats{ - Week: &Timestamp{time.Unix(int64(week[0]), 0)}, - Additions: Int(week[1]), - Deletions: Int(week[2]), - } - stats = append(stats, stat) - } - - return stats, resp, err -} - -// RepositoryParticipation is the number of commits by everyone -// who has contributed to the repository (including the owner) -// as well as the number of commits by the owner themself. -type RepositoryParticipation struct { - All []int `json:"all,omitempty"` - Owner []int `json:"owner,omitempty"` -} - -func (r RepositoryParticipation) String() string { - return Stringify(r) -} - -// ListParticipation returns the total commit counts for the 'owner' -// and total commit counts in 'all'. 'all' is everyone combined, -// including the 'owner' in the last 52 weeks. If you’d like to get -// the commit counts for non-owners, you can subtract 'all' from 'owner'. -// -// The array order is oldest week (index 0) to most recent week. -// -// If this is the first time these statistics are requested for the given -// repository, this method will return an *AcceptedError and a status code of -// 202. This is because this is the status that GitHub returns to signify that -// it is now computing the requested statistics. A follow up request, after a -// delay of a second or so, should result in a successful request. -// -// GitHub API docs: https://developer.github.com/v3/repos/statistics/#participation -func (s *RepositoriesService) ListParticipation(ctx context.Context, owner, repo string) (*RepositoryParticipation, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/stats/participation", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - participation := new(RepositoryParticipation) - resp, err := s.client.Do(ctx, req, participation) - if err != nil { - return nil, resp, err - } - - return participation, resp, nil -} - -// PunchCard represents the number of commits made during a given hour of a -// day of the week. -type PunchCard struct { - Day *int // Day of the week (0-6: =Sunday - Saturday). - Hour *int // Hour of day (0-23). - Commits *int // Number of commits. -} - -// ListPunchCard returns the number of commits per hour in each day. -// -// If this is the first time these statistics are requested for the given -// repository, this method will return an *AcceptedError and a status code of -// 202. This is because this is the status that GitHub returns to signify that -// it is now computing the requested statistics. A follow up request, after a -// delay of a second or so, should result in a successful request. -// -// GitHub API docs: https://developer.github.com/v3/repos/statistics/#punch-card -func (s *RepositoriesService) ListPunchCard(ctx context.Context, owner, repo string) ([]*PunchCard, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/stats/punch_card", owner, repo) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var results [][]int - resp, err := s.client.Do(ctx, req, &results) - - // convert int slices into Punchcards - var cards []*PunchCard - for _, result := range results { - if len(result) != 3 { - continue - } - card := &PunchCard{ - Day: Int(result[0]), - Hour: Int(result[1]), - Commits: Int(result[2]), - } - cards = append(cards, card) - } - - return cards, resp, err -} diff --git a/vendor/github.com/google/go-github/github/repos_statuses.go b/vendor/github.com/google/go-github/github/repos_statuses.go deleted file mode 100644 index f94fdc858b8..00000000000 --- a/vendor/github.com/google/go-github/github/repos_statuses.go +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// RepoStatus represents the status of a repository at a particular reference. -type RepoStatus struct { - ID *int64 `json:"id,omitempty"` - URL *string `json:"url,omitempty"` - - // State is the current state of the repository. Possible values are: - // pending, success, error, or failure. - State *string `json:"state,omitempty"` - - // TargetURL is the URL of the page representing this status. It will be - // linked from the GitHub UI to allow users to see the source of the status. - TargetURL *string `json:"target_url,omitempty"` - - // Description is a short high level summary of the status. - Description *string `json:"description,omitempty"` - - // A string label to differentiate this status from the statuses of other systems. - Context *string `json:"context,omitempty"` - - Creator *User `json:"creator,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - UpdatedAt *time.Time `json:"updated_at,omitempty"` -} - -func (r RepoStatus) String() string { - return Stringify(r) -} - -// ListStatuses lists the statuses of a repository at the specified -// reference. ref can be a SHA, a branch name, or a tag name. -// -// GitHub API docs: https://developer.github.com/v3/repos/statuses/#list-statuses-for-a-specific-ref -func (s *RepositoriesService) ListStatuses(ctx context.Context, owner, repo, ref string, opt *ListOptions) ([]*RepoStatus, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/commits/%v/statuses", owner, repo, ref) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var statuses []*RepoStatus - resp, err := s.client.Do(ctx, req, &statuses) - if err != nil { - return nil, resp, err - } - - return statuses, resp, nil -} - -// CreateStatus creates a new status for a repository at the specified -// reference. Ref can be a SHA, a branch name, or a tag name. -// -// GitHub API docs: https://developer.github.com/v3/repos/statuses/#create-a-status -func (s *RepositoriesService) CreateStatus(ctx context.Context, owner, repo, ref string, status *RepoStatus) (*RepoStatus, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/statuses/%v", owner, repo, ref) - req, err := s.client.NewRequest("POST", u, status) - if err != nil { - return nil, nil, err - } - - repoStatus := new(RepoStatus) - resp, err := s.client.Do(ctx, req, repoStatus) - if err != nil { - return nil, resp, err - } - - return repoStatus, resp, nil -} - -// CombinedStatus represents the combined status of a repository at a particular reference. -type CombinedStatus struct { - // State is the combined state of the repository. Possible values are: - // failure, pending, or success. - State *string `json:"state,omitempty"` - - Name *string `json:"name,omitempty"` - SHA *string `json:"sha,omitempty"` - TotalCount *int `json:"total_count,omitempty"` - Statuses []RepoStatus `json:"statuses,omitempty"` - - CommitURL *string `json:"commit_url,omitempty"` - RepositoryURL *string `json:"repository_url,omitempty"` -} - -func (s CombinedStatus) String() string { - return Stringify(s) -} - -// GetCombinedStatus returns the combined status of a repository at the specified -// reference. ref can be a SHA, a branch name, or a tag name. -// -// GitHub API docs: https://developer.github.com/v3/repos/statuses/#get-the-combined-status-for-a-specific-ref -func (s *RepositoriesService) GetCombinedStatus(ctx context.Context, owner, repo, ref string, opt *ListOptions) (*CombinedStatus, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/commits/%v/status", owner, repo, ref) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - status := new(CombinedStatus) - resp, err := s.client.Do(ctx, req, status) - if err != nil { - return nil, resp, err - } - - return status, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/repos_traffic.go b/vendor/github.com/google/go-github/github/repos_traffic.go deleted file mode 100644 index fb1c97648a7..00000000000 --- a/vendor/github.com/google/go-github/github/repos_traffic.go +++ /dev/null @@ -1,141 +0,0 @@ -// Copyright 2016 The go-github 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 github - -import ( - "context" - "fmt" -) - -// TrafficReferrer represent information about traffic from a referrer . -type TrafficReferrer struct { - Referrer *string `json:"referrer,omitempty"` - Count *int `json:"count,omitempty"` - Uniques *int `json:"uniques,omitempty"` -} - -// TrafficPath represent information about the traffic on a path of the repo. -type TrafficPath struct { - Path *string `json:"path,omitempty"` - Title *string `json:"title,omitempty"` - Count *int `json:"count,omitempty"` - Uniques *int `json:"uniques,omitempty"` -} - -// TrafficData represent information about a specific timestamp in views or clones list. -type TrafficData struct { - Timestamp *Timestamp `json:"timestamp,omitempty"` - Count *int `json:"count,omitempty"` - Uniques *int `json:"uniques,omitempty"` -} - -// TrafficViews represent information about the number of views in the last 14 days. -type TrafficViews struct { - Views []*TrafficData `json:"views,omitempty"` - Count *int `json:"count,omitempty"` - Uniques *int `json:"uniques,omitempty"` -} - -// TrafficClones represent information about the number of clones in the last 14 days. -type TrafficClones struct { - Clones []*TrafficData `json:"clones,omitempty"` - Count *int `json:"count,omitempty"` - Uniques *int `json:"uniques,omitempty"` -} - -// TrafficBreakdownOptions specifies the parameters to methods that support breakdown per day or week. -// Can be one of: day, week. Default: day. -type TrafficBreakdownOptions struct { - Per string `url:"per,omitempty"` -} - -// ListTrafficReferrers list the top 10 referrers over the last 14 days. -// -// GitHub API docs: https://developer.github.com/v3/repos/traffic/#list-referrers -func (s *RepositoriesService) ListTrafficReferrers(ctx context.Context, owner, repo string) ([]*TrafficReferrer, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/traffic/popular/referrers", owner, repo) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var trafficReferrers []*TrafficReferrer - resp, err := s.client.Do(ctx, req, &trafficReferrers) - if err != nil { - return nil, resp, err - } - - return trafficReferrers, resp, nil -} - -// ListTrafficPaths list the top 10 popular content over the last 14 days. -// -// GitHub API docs: https://developer.github.com/v3/repos/traffic/#list-paths -func (s *RepositoriesService) ListTrafficPaths(ctx context.Context, owner, repo string) ([]*TrafficPath, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/traffic/popular/paths", owner, repo) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var paths []*TrafficPath - resp, err := s.client.Do(ctx, req, &paths) - if err != nil { - return nil, resp, err - } - - return paths, resp, nil -} - -// ListTrafficViews get total number of views for the last 14 days and breaks it down either per day or week. -// -// GitHub API docs: https://developer.github.com/v3/repos/traffic/#views -func (s *RepositoriesService) ListTrafficViews(ctx context.Context, owner, repo string, opt *TrafficBreakdownOptions) (*TrafficViews, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/traffic/views", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - trafficViews := new(TrafficViews) - resp, err := s.client.Do(ctx, req, &trafficViews) - if err != nil { - return nil, resp, err - } - - return trafficViews, resp, nil -} - -// ListTrafficClones get total number of clones for the last 14 days and breaks it down either per day or week for the last 14 days. -// -// GitHub API docs: https://developer.github.com/v3/repos/traffic/#views -func (s *RepositoriesService) ListTrafficClones(ctx context.Context, owner, repo string, opt *TrafficBreakdownOptions) (*TrafficClones, *Response, error) { - u := fmt.Sprintf("repos/%v/%v/traffic/clones", owner, repo) - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - trafficClones := new(TrafficClones) - resp, err := s.client.Do(ctx, req, &trafficClones) - if err != nil { - return nil, resp, err - } - - return trafficClones, resp, nil -} diff --git a/vendor/github.com/google/go-github/github/search.go b/vendor/github.com/google/go-github/github/search.go deleted file mode 100644 index a5973520208..00000000000 --- a/vendor/github.com/google/go-github/github/search.go +++ /dev/null @@ -1,210 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" - - qs "github.com/google/go-querystring/query" -) - -// SearchService provides access to the search related functions -// in the GitHub API. -// -// Each method takes a query string defining the search keywords and any search qualifiers. -// For example, when searching issues, the query "gopher is:issue language:go" will search -// for issues containing the word "gopher" in Go repositories. The method call -// opts := &github.SearchOptions{Sort: "created", Order: "asc"} -// cl.Search.Issues(ctx, "gopher is:issue language:go", opts) -// will search for such issues, sorting by creation date in ascending order -// (i.e., oldest first). -// -// GitHub API docs: https://developer.github.com/v3/search/ -type SearchService service - -// SearchOptions specifies optional parameters to the SearchService methods. -type SearchOptions struct { - // How to sort the search results. Possible values are: - // - for repositories: stars, fork, updated - // - for commits: author-date, committer-date - // - for code: indexed - // - for issues: comments, created, updated - // - for users: followers, repositories, joined - // - // Default is to sort by best match. - Sort string `url:"sort,omitempty"` - - // Sort order if sort parameter is provided. Possible values are: asc, - // desc. Default is desc. - Order string `url:"order,omitempty"` - - // Whether to retrieve text match metadata with a query - TextMatch bool `url:"-"` - - ListOptions -} - -// RepositoriesSearchResult represents the result of a repositories search. -type RepositoriesSearchResult struct { - Total *int `json:"total_count,omitempty"` - IncompleteResults *bool `json:"incomplete_results,omitempty"` - Repositories []Repository `json:"items,omitempty"` -} - -// Repositories searches repositories via various criteria. -// -// GitHub API docs: https://developer.github.com/v3/search/#search-repositories -func (s *SearchService) Repositories(ctx context.Context, query string, opt *SearchOptions) (*RepositoriesSearchResult, *Response, error) { - result := new(RepositoriesSearchResult) - resp, err := s.search(ctx, "repositories", query, opt, result) - return result, resp, err -} - -// CommitsSearchResult represents the result of a commits search. -type CommitsSearchResult struct { - Total *int `json:"total_count,omitempty"` - IncompleteResults *bool `json:"incomplete_results,omitempty"` - Commits []*CommitResult `json:"items,omitempty"` -} - -// CommitResult represents a commit object as returned in commit search endpoint response. -type CommitResult struct { - SHA *string `json:"sha,omitempty"` - Commit *Commit `json:"commit,omitempty"` - Author *User `json:"author,omitempty"` - Committer *User `json:"committer,omitempty"` - Parents []*Commit `json:"parents,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - URL *string `json:"url,omitempty"` - CommentsURL *string `json:"comments_url,omitempty"` - - Repository *Repository `json:"repository,omitempty"` - Score *float64 `json:"score,omitempty"` -} - -// Commits searches commits via various criteria. -// -// GitHub API docs: https://developer.github.com/v3/search/#search-commits -func (s *SearchService) Commits(ctx context.Context, query string, opt *SearchOptions) (*CommitsSearchResult, *Response, error) { - result := new(CommitsSearchResult) - resp, err := s.search(ctx, "commits", query, opt, result) - return result, resp, err -} - -// IssuesSearchResult represents the result of an issues search. -type IssuesSearchResult struct { - Total *int `json:"total_count,omitempty"` - IncompleteResults *bool `json:"incomplete_results,omitempty"` - Issues []Issue `json:"items,omitempty"` -} - -// Issues searches issues via various criteria. -// -// GitHub API docs: https://developer.github.com/v3/search/#search-issues -func (s *SearchService) Issues(ctx context.Context, query string, opt *SearchOptions) (*IssuesSearchResult, *Response, error) { - result := new(IssuesSearchResult) - resp, err := s.search(ctx, "issues", query, opt, result) - return result, resp, err -} - -// UsersSearchResult represents the result of a users search. -type UsersSearchResult struct { - Total *int `json:"total_count,omitempty"` - IncompleteResults *bool `json:"incomplete_results,omitempty"` - Users []User `json:"items,omitempty"` -} - -// Users searches users via various criteria. -// -// GitHub API docs: https://developer.github.com/v3/search/#search-users -func (s *SearchService) Users(ctx context.Context, query string, opt *SearchOptions) (*UsersSearchResult, *Response, error) { - result := new(UsersSearchResult) - resp, err := s.search(ctx, "users", query, opt, result) - return result, resp, err -} - -// Match represents a single text match. -type Match struct { - Text *string `json:"text,omitempty"` - Indices []int `json:"indices,omitempty"` -} - -// TextMatch represents a text match for a SearchResult -type TextMatch struct { - ObjectURL *string `json:"object_url,omitempty"` - ObjectType *string `json:"object_type,omitempty"` - Property *string `json:"property,omitempty"` - Fragment *string `json:"fragment,omitempty"` - Matches []Match `json:"matches,omitempty"` -} - -func (tm TextMatch) String() string { - return Stringify(tm) -} - -// CodeSearchResult represents the result of a code search. -type CodeSearchResult struct { - Total *int `json:"total_count,omitempty"` - IncompleteResults *bool `json:"incomplete_results,omitempty"` - CodeResults []CodeResult `json:"items,omitempty"` -} - -// CodeResult represents a single search result. -type CodeResult struct { - Name *string `json:"name,omitempty"` - Path *string `json:"path,omitempty"` - SHA *string `json:"sha,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - Repository *Repository `json:"repository,omitempty"` - TextMatches []TextMatch `json:"text_matches,omitempty"` -} - -func (c CodeResult) String() string { - return Stringify(c) -} - -// Code searches code via various criteria. -// -// GitHub API docs: https://developer.github.com/v3/search/#search-code -func (s *SearchService) Code(ctx context.Context, query string, opt *SearchOptions) (*CodeSearchResult, *Response, error) { - result := new(CodeSearchResult) - resp, err := s.search(ctx, "code", query, opt, result) - return result, resp, err -} - -// Helper function that executes search queries against different -// GitHub search types (repositories, commits, code, issues, users) -func (s *SearchService) search(ctx context.Context, searchType string, query string, opt *SearchOptions, result interface{}) (*Response, error) { - params, err := qs.Values(opt) - if err != nil { - return nil, err - } - params.Set("q", query) - u := fmt.Sprintf("search/%s?%s", searchType, params.Encode()) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, err - } - - switch { - case searchType == "commits": - // Accept header for search commits preview endpoint - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeCommitSearchPreview) - case searchType == "repositories": - // Accept header for search repositories based on topics preview endpoint - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeTopicsPreview) - case opt != nil && opt.TextMatch: - // Accept header defaults to "application/vnd.github.v3+json" - // We change it here to fetch back text-match metadata - req.Header.Set("Accept", "application/vnd.github.v3.text-match+json") - } - - return s.client.Do(ctx, req, result) -} diff --git a/vendor/github.com/google/go-github/github/strings.go b/vendor/github.com/google/go-github/github/strings.go deleted file mode 100644 index 431e1cc6c1d..00000000000 --- a/vendor/github.com/google/go-github/github/strings.go +++ /dev/null @@ -1,93 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "bytes" - "fmt" - "io" - - "reflect" -) - -var timestampType = reflect.TypeOf(Timestamp{}) - -// Stringify attempts to create a reasonable string representation of types in -// the GitHub library. It does things like resolve pointers to their values -// and omits struct fields with nil values. -func Stringify(message interface{}) string { - var buf bytes.Buffer - v := reflect.ValueOf(message) - stringifyValue(&buf, v) - return buf.String() -} - -// stringifyValue was heavily inspired by the goprotobuf library. - -func stringifyValue(w io.Writer, val reflect.Value) { - if val.Kind() == reflect.Ptr && val.IsNil() { - w.Write([]byte("")) - return - } - - v := reflect.Indirect(val) - - switch v.Kind() { - case reflect.String: - fmt.Fprintf(w, `"%s"`, v) - case reflect.Slice: - w.Write([]byte{'['}) - for i := 0; i < v.Len(); i++ { - if i > 0 { - w.Write([]byte{' '}) - } - - stringifyValue(w, v.Index(i)) - } - - w.Write([]byte{']'}) - return - case reflect.Struct: - if v.Type().Name() != "" { - w.Write([]byte(v.Type().String())) - } - - // special handling of Timestamp values - if v.Type() == timestampType { - fmt.Fprintf(w, "{%s}", v.Interface()) - return - } - - w.Write([]byte{'{'}) - - var sep bool - for i := 0; i < v.NumField(); i++ { - fv := v.Field(i) - if fv.Kind() == reflect.Ptr && fv.IsNil() { - continue - } - if fv.Kind() == reflect.Slice && fv.IsNil() { - continue - } - - if sep { - w.Write([]byte(", ")) - } else { - sep = true - } - - w.Write([]byte(v.Type().Field(i).Name)) - w.Write([]byte{':'}) - stringifyValue(w, fv) - } - - w.Write([]byte{'}'}) - default: - if v.CanInterface() { - fmt.Fprint(w, v.Interface()) - } - } -} diff --git a/vendor/github.com/google/go-github/github/timestamp.go b/vendor/github.com/google/go-github/github/timestamp.go deleted file mode 100644 index a1c1554a30e..00000000000 --- a/vendor/github.com/google/go-github/github/timestamp.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "strconv" - "time" -) - -// Timestamp represents a time that can be unmarshalled from a JSON string -// formatted as either an RFC3339 or Unix timestamp. This is necessary for some -// fields since the GitHub API is inconsistent in how it represents times. All -// exported methods of time.Time can be called on Timestamp. -type Timestamp struct { - time.Time -} - -func (t Timestamp) String() string { - return t.Time.String() -} - -// UnmarshalJSON implements the json.Unmarshaler interface. -// Time is expected in RFC3339 or Unix format. -func (t *Timestamp) UnmarshalJSON(data []byte) (err error) { - str := string(data) - i, err := strconv.ParseInt(str, 10, 64) - if err == nil { - (*t).Time = time.Unix(i, 0) - } else { - (*t).Time, err = time.Parse(`"`+time.RFC3339+`"`, str) - } - return -} - -// Equal reports whether t and u are equal based on time.Equal -func (t Timestamp) Equal(u Timestamp) bool { - return t.Time.Equal(u.Time) -} diff --git a/vendor/github.com/google/go-github/github/users.go b/vendor/github.com/google/go-github/github/users.go deleted file mode 100644 index ef8f3dd573a..00000000000 --- a/vendor/github.com/google/go-github/github/users.go +++ /dev/null @@ -1,230 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// UsersService handles communication with the user related -// methods of the GitHub API. -// -// GitHub API docs: https://developer.github.com/v3/users/ -type UsersService service - -// User represents a GitHub user. -type User struct { - Login *string `json:"login,omitempty"` - ID *int64 `json:"id,omitempty"` - AvatarURL *string `json:"avatar_url,omitempty"` - HTMLURL *string `json:"html_url,omitempty"` - GravatarID *string `json:"gravatar_id,omitempty"` - Name *string `json:"name,omitempty"` - Company *string `json:"company,omitempty"` - Blog *string `json:"blog,omitempty"` - Location *string `json:"location,omitempty"` - Email *string `json:"email,omitempty"` - Hireable *bool `json:"hireable,omitempty"` - Bio *string `json:"bio,omitempty"` - PublicRepos *int `json:"public_repos,omitempty"` - PublicGists *int `json:"public_gists,omitempty"` - Followers *int `json:"followers,omitempty"` - Following *int `json:"following,omitempty"` - CreatedAt *Timestamp `json:"created_at,omitempty"` - UpdatedAt *Timestamp `json:"updated_at,omitempty"` - SuspendedAt *Timestamp `json:"suspended_at,omitempty"` - Type *string `json:"type,omitempty"` - SiteAdmin *bool `json:"site_admin,omitempty"` - TotalPrivateRepos *int `json:"total_private_repos,omitempty"` - OwnedPrivateRepos *int `json:"owned_private_repos,omitempty"` - PrivateGists *int `json:"private_gists,omitempty"` - DiskUsage *int `json:"disk_usage,omitempty"` - Collaborators *int `json:"collaborators,omitempty"` - Plan *Plan `json:"plan,omitempty"` - - // API URLs - URL *string `json:"url,omitempty"` - EventsURL *string `json:"events_url,omitempty"` - FollowingURL *string `json:"following_url,omitempty"` - FollowersURL *string `json:"followers_url,omitempty"` - GistsURL *string `json:"gists_url,omitempty"` - OrganizationsURL *string `json:"organizations_url,omitempty"` - ReceivedEventsURL *string `json:"received_events_url,omitempty"` - ReposURL *string `json:"repos_url,omitempty"` - StarredURL *string `json:"starred_url,omitempty"` - SubscriptionsURL *string `json:"subscriptions_url,omitempty"` - - // TextMatches is only populated from search results that request text matches - // See: search.go and https://developer.github.com/v3/search/#text-match-metadata - TextMatches []TextMatch `json:"text_matches,omitempty"` - - // Permissions identifies the permissions that a user has on a given - // repository. This is only populated when calling Repositories.ListCollaborators. - Permissions *map[string]bool `json:"permissions,omitempty"` -} - -func (u User) String() string { - return Stringify(u) -} - -// Get fetches a user. Passing the empty string will fetch the authenticated -// user. -// -// GitHub API docs: https://developer.github.com/v3/users/#get-a-single-user -func (s *UsersService) Get(ctx context.Context, user string) (*User, *Response, error) { - var u string - if user != "" { - u = fmt.Sprintf("users/%v", user) - } else { - u = "user" - } - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - uResp := new(User) - resp, err := s.client.Do(ctx, req, uResp) - if err != nil { - return nil, resp, err - } - - return uResp, resp, nil -} - -// GetByID fetches a user. -// -// Note: GetByID uses the undocumented GitHub API endpoint /user/:id. -func (s *UsersService) GetByID(ctx context.Context, id int64) (*User, *Response, error) { - u := fmt.Sprintf("user/%d", id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - user := new(User) - resp, err := s.client.Do(ctx, req, user) - if err != nil { - return nil, resp, err - } - - return user, resp, nil -} - -// Edit the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/users/#update-the-authenticated-user -func (s *UsersService) Edit(ctx context.Context, user *User) (*User, *Response, error) { - u := "user" - req, err := s.client.NewRequest("PATCH", u, user) - if err != nil { - return nil, nil, err - } - - uResp := new(User) - resp, err := s.client.Do(ctx, req, uResp) - if err != nil { - return nil, resp, err - } - - return uResp, resp, nil -} - -// UserListOptions specifies optional parameters to the UsersService.ListAll -// method. -type UserListOptions struct { - // ID of the last user seen - Since int64 `url:"since,omitempty"` - - ListOptions -} - -// ListAll lists all GitHub users. -// -// To paginate through all users, populate 'Since' with the ID of the last user. -// -// GitHub API docs: https://developer.github.com/v3/users/#get-all-users -func (s *UsersService) ListAll(ctx context.Context, opt *UserListOptions) ([]*User, *Response, error) { - u, err := addOptions("users", opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var users []*User - resp, err := s.client.Do(ctx, req, &users) - if err != nil { - return nil, resp, err - } - - return users, resp, nil -} - -// ListInvitations lists all currently-open repository invitations for the -// authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/repos/invitations/#list-a-users-repository-invitations -func (s *UsersService) ListInvitations(ctx context.Context, opt *ListOptions) ([]*RepositoryInvitation, *Response, error) { - u, err := addOptions("user/repository_invitations", opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) - - invites := []*RepositoryInvitation{} - resp, err := s.client.Do(ctx, req, &invites) - if err != nil { - return nil, resp, err - } - - return invites, resp, nil -} - -// AcceptInvitation accepts the currently-open repository invitation for the -// authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/repos/invitations/#accept-a-repository-invitation -func (s *UsersService) AcceptInvitation(ctx context.Context, invitationID int64) (*Response, error) { - u := fmt.Sprintf("user/repository_invitations/%v", invitationID) - req, err := s.client.NewRequest("PATCH", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) - - return s.client.Do(ctx, req, nil) -} - -// DeclineInvitation declines the currently-open repository invitation for the -// authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/repos/invitations/#decline-a-repository-invitation -func (s *UsersService) DeclineInvitation(ctx context.Context, invitationID int64) (*Response, error) { - u := fmt.Sprintf("user/repository_invitations/%v", invitationID) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeRepositoryInvitationsPreview) - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/users_administration.go b/vendor/github.com/google/go-github/github/users_administration.go deleted file mode 100644 index e042398d8c5..00000000000 --- a/vendor/github.com/google/go-github/github/users_administration.go +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2014 The go-github 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 github - -import ( - "context" - "fmt" -) - -// PromoteSiteAdmin promotes a user to a site administrator of a GitHub Enterprise instance. -// -// GitHub API docs: https://developer.github.com/v3/users/administration/#promote-an-ordinary-user-to-a-site-administrator -func (s *UsersService) PromoteSiteAdmin(ctx context.Context, user string) (*Response, error) { - u := fmt.Sprintf("users/%v/site_admin", user) - - req, err := s.client.NewRequest("PUT", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// DemoteSiteAdmin demotes a user from site administrator of a GitHub Enterprise instance. -// -// GitHub API docs: https://developer.github.com/v3/users/administration/#demote-a-site-administrator-to-an-ordinary-user -func (s *UsersService) DemoteSiteAdmin(ctx context.Context, user string) (*Response, error) { - u := fmt.Sprintf("users/%v/site_admin", user) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// Suspend a user on a GitHub Enterprise instance. -// -// GitHub API docs: https://developer.github.com/v3/users/administration/#suspend-a-user -func (s *UsersService) Suspend(ctx context.Context, user string) (*Response, error) { - u := fmt.Sprintf("users/%v/suspended", user) - - req, err := s.client.NewRequest("PUT", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// Unsuspend a user on a GitHub Enterprise instance. -// -// GitHub API docs: https://developer.github.com/v3/users/administration/#unsuspend-a-user -func (s *UsersService) Unsuspend(ctx context.Context, user string) (*Response, error) { - u := fmt.Sprintf("users/%v/suspended", user) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/users_blocking.go b/vendor/github.com/google/go-github/github/users_blocking.go deleted file mode 100644 index 39e45601cc1..00000000000 --- a/vendor/github.com/google/go-github/github/users_blocking.go +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2017 The go-github 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 github - -import ( - "context" - "fmt" -) - -// ListBlockedUsers lists all the blocked users by the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/users/blocking/#list-blocked-users -func (s *UsersService) ListBlockedUsers(ctx context.Context, opt *ListOptions) ([]*User, *Response, error) { - u := "user/blocks" - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeBlockUsersPreview) - - var blockedUsers []*User - resp, err := s.client.Do(ctx, req, &blockedUsers) - if err != nil { - return nil, resp, err - } - - return blockedUsers, resp, nil -} - -// IsBlocked reports whether specified user is blocked by the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/users/blocking/#check-whether-youve-blocked-a-user -func (s *UsersService) IsBlocked(ctx context.Context, user string) (bool, *Response, error) { - u := fmt.Sprintf("user/blocks/%v", user) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return false, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeBlockUsersPreview) - - resp, err := s.client.Do(ctx, req, nil) - isBlocked, err := parseBoolResponse(err) - return isBlocked, resp, err -} - -// BlockUser blocks specified user for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/users/blocking/#block-a-user -func (s *UsersService) BlockUser(ctx context.Context, user string) (*Response, error) { - u := fmt.Sprintf("user/blocks/%v", user) - - req, err := s.client.NewRequest("PUT", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeBlockUsersPreview) - - return s.client.Do(ctx, req, nil) -} - -// UnblockUser unblocks specified user for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/users/blocking/#unblock-a-user -func (s *UsersService) UnblockUser(ctx context.Context, user string) (*Response, error) { - u := fmt.Sprintf("user/blocks/%v", user) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeBlockUsersPreview) - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/users_emails.go b/vendor/github.com/google/go-github/github/users_emails.go deleted file mode 100644 index 0bbd4627e3b..00000000000 --- a/vendor/github.com/google/go-github/github/users_emails.go +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import "context" - -// UserEmail represents user's email address -type UserEmail struct { - Email *string `json:"email,omitempty"` - Primary *bool `json:"primary,omitempty"` - Verified *bool `json:"verified,omitempty"` -} - -// ListEmails lists all email addresses for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/users/emails/#list-email-addresses-for-a-user -func (s *UsersService) ListEmails(ctx context.Context, opt *ListOptions) ([]*UserEmail, *Response, error) { - u := "user/emails" - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var emails []*UserEmail - resp, err := s.client.Do(ctx, req, &emails) - if err != nil { - return nil, resp, err - } - - return emails, resp, nil -} - -// AddEmails adds email addresses of the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/users/emails/#add-email-addresses -func (s *UsersService) AddEmails(ctx context.Context, emails []string) ([]*UserEmail, *Response, error) { - u := "user/emails" - req, err := s.client.NewRequest("POST", u, emails) - if err != nil { - return nil, nil, err - } - - var e []*UserEmail - resp, err := s.client.Do(ctx, req, &e) - if err != nil { - return nil, resp, err - } - - return e, resp, nil -} - -// DeleteEmails deletes email addresses from authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/users/emails/#delete-email-addresses -func (s *UsersService) DeleteEmails(ctx context.Context, emails []string) (*Response, error) { - u := "user/emails" - req, err := s.client.NewRequest("DELETE", u, emails) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/users_followers.go b/vendor/github.com/google/go-github/github/users_followers.go deleted file mode 100644 index c2224096a62..00000000000 --- a/vendor/github.com/google/go-github/github/users_followers.go +++ /dev/null @@ -1,119 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// ListFollowers lists the followers for a user. Passing the empty string will -// fetch followers for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/users/followers/#list-followers-of-a-user -func (s *UsersService) ListFollowers(ctx context.Context, user string, opt *ListOptions) ([]*User, *Response, error) { - var u string - if user != "" { - u = fmt.Sprintf("users/%v/followers", user) - } else { - u = "user/followers" - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var users []*User - resp, err := s.client.Do(ctx, req, &users) - if err != nil { - return nil, resp, err - } - - return users, resp, nil -} - -// ListFollowing lists the people that a user is following. Passing the empty -// string will list people the authenticated user is following. -// -// GitHub API docs: https://developer.github.com/v3/users/followers/#list-users-followed-by-another-user -func (s *UsersService) ListFollowing(ctx context.Context, user string, opt *ListOptions) ([]*User, *Response, error) { - var u string - if user != "" { - u = fmt.Sprintf("users/%v/following", user) - } else { - u = "user/following" - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var users []*User - resp, err := s.client.Do(ctx, req, &users) - if err != nil { - return nil, resp, err - } - - return users, resp, nil -} - -// IsFollowing checks if "user" is following "target". Passing the empty -// string for "user" will check if the authenticated user is following "target". -// -// GitHub API docs: https://developer.github.com/v3/users/followers/#check-if-you-are-following-a-user -func (s *UsersService) IsFollowing(ctx context.Context, user, target string) (bool, *Response, error) { - var u string - if user != "" { - u = fmt.Sprintf("users/%v/following/%v", user, target) - } else { - u = fmt.Sprintf("user/following/%v", target) - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return false, nil, err - } - - resp, err := s.client.Do(ctx, req, nil) - following, err := parseBoolResponse(err) - return following, resp, err -} - -// Follow will cause the authenticated user to follow the specified user. -// -// GitHub API docs: https://developer.github.com/v3/users/followers/#follow-a-user -func (s *UsersService) Follow(ctx context.Context, user string) (*Response, error) { - u := fmt.Sprintf("user/following/%v", user) - req, err := s.client.NewRequest("PUT", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} - -// Unfollow will cause the authenticated user to unfollow the specified user. -// -// GitHub API docs: https://developer.github.com/v3/users/followers/#unfollow-a-user -func (s *UsersService) Unfollow(ctx context.Context, user string) (*Response, error) { - u := fmt.Sprintf("user/following/%v", user) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/users_gpg_keys.go b/vendor/github.com/google/go-github/github/users_gpg_keys.go deleted file mode 100644 index d8bbc5201f3..00000000000 --- a/vendor/github.com/google/go-github/github/users_gpg_keys.go +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2016 The go-github 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 github - -import ( - "context" - "fmt" - "time" -) - -// GPGKey represents a GitHub user's public GPG key used to verify GPG signed commits and tags. -// -// https://developer.github.com/changes/2016-04-04-git-signing-api-preview/ -type GPGKey struct { - ID *int64 `json:"id,omitempty"` - PrimaryKeyID *int64 `json:"primary_key_id,omitempty"` - KeyID *string `json:"key_id,omitempty"` - PublicKey *string `json:"public_key,omitempty"` - Emails []GPGEmail `json:"emails,omitempty"` - Subkeys []GPGKey `json:"subkeys,omitempty"` - CanSign *bool `json:"can_sign,omitempty"` - CanEncryptComms *bool `json:"can_encrypt_comms,omitempty"` - CanEncryptStorage *bool `json:"can_encrypt_storage,omitempty"` - CanCertify *bool `json:"can_certify,omitempty"` - CreatedAt *time.Time `json:"created_at,omitempty"` - ExpiresAt *time.Time `json:"expires_at,omitempty"` -} - -// String stringifies a GPGKey. -func (k GPGKey) String() string { - return Stringify(k) -} - -// GPGEmail represents an email address associated to a GPG key. -type GPGEmail struct { - Email *string `json:"email,omitempty"` - Verified *bool `json:"verified,omitempty"` -} - -// ListGPGKeys lists the public GPG keys for a user. Passing the empty -// string will fetch keys for the authenticated user. It requires authentication -// via Basic Auth or via OAuth with at least read:gpg_key scope. -// -// GitHub API docs: https://developer.github.com/v3/users/gpg_keys/#list-gpg-keys-for-a-user -func (s *UsersService) ListGPGKeys(ctx context.Context, user string, opt *ListOptions) ([]*GPGKey, *Response, error) { - var u string - if user != "" { - u = fmt.Sprintf("users/%v/gpg_keys", user) - } else { - u = "user/gpg_keys" - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGitSigningPreview) - - var keys []*GPGKey - resp, err := s.client.Do(ctx, req, &keys) - if err != nil { - return nil, resp, err - } - - return keys, resp, nil -} - -// GetGPGKey gets extended details for a single GPG key. It requires authentication -// via Basic Auth or via OAuth with at least read:gpg_key scope. -// -// GitHub API docs: https://developer.github.com/v3/users/gpg_keys/#get-a-single-gpg-key -func (s *UsersService) GetGPGKey(ctx context.Context, id int64) (*GPGKey, *Response, error) { - u := fmt.Sprintf("user/gpg_keys/%v", id) - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGitSigningPreview) - - key := &GPGKey{} - resp, err := s.client.Do(ctx, req, key) - if err != nil { - return nil, resp, err - } - - return key, resp, nil -} - -// CreateGPGKey creates a GPG key. It requires authenticatation via Basic Auth -// or OAuth with at least write:gpg_key scope. -// -// GitHub API docs: https://developer.github.com/v3/users/gpg_keys/#create-a-gpg-key -func (s *UsersService) CreateGPGKey(ctx context.Context, armoredPublicKey string) (*GPGKey, *Response, error) { - gpgKey := &struct { - ArmoredPublicKey string `json:"armored_public_key"` - }{ArmoredPublicKey: armoredPublicKey} - req, err := s.client.NewRequest("POST", "user/gpg_keys", gpgKey) - if err != nil { - return nil, nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGitSigningPreview) - - key := &GPGKey{} - resp, err := s.client.Do(ctx, req, key) - if err != nil { - return nil, resp, err - } - - return key, resp, nil -} - -// DeleteGPGKey deletes a GPG key. It requires authentication via Basic Auth or -// via OAuth with at least admin:gpg_key scope. -// -// GitHub API docs: https://developer.github.com/v3/users/gpg_keys/#delete-a-gpg-key -func (s *UsersService) DeleteGPGKey(ctx context.Context, id int64) (*Response, error) { - u := fmt.Sprintf("user/gpg_keys/%v", id) - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - // TODO: remove custom Accept header when this API fully launches. - req.Header.Set("Accept", mediaTypeGitSigningPreview) - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/users_keys.go b/vendor/github.com/google/go-github/github/users_keys.go deleted file mode 100644 index ddc832a1ece..00000000000 --- a/vendor/github.com/google/go-github/github/users_keys.go +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2013 The go-github 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 github - -import ( - "context" - "fmt" -) - -// Key represents a public SSH key used to authenticate a user or deploy script. -type Key struct { - ID *int64 `json:"id,omitempty"` - Key *string `json:"key,omitempty"` - URL *string `json:"url,omitempty"` - Title *string `json:"title,omitempty"` - ReadOnly *bool `json:"read_only,omitempty"` -} - -func (k Key) String() string { - return Stringify(k) -} - -// ListKeys lists the verified public keys for a user. Passing the empty -// string will fetch keys for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/users/keys/#list-public-keys-for-a-user -func (s *UsersService) ListKeys(ctx context.Context, user string, opt *ListOptions) ([]*Key, *Response, error) { - var u string - if user != "" { - u = fmt.Sprintf("users/%v/keys", user) - } else { - u = "user/keys" - } - u, err := addOptions(u, opt) - if err != nil { - return nil, nil, err - } - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - var keys []*Key - resp, err := s.client.Do(ctx, req, &keys) - if err != nil { - return nil, resp, err - } - - return keys, resp, nil -} - -// GetKey fetches a single public key. -// -// GitHub API docs: https://developer.github.com/v3/users/keys/#get-a-single-public-key -func (s *UsersService) GetKey(ctx context.Context, id int64) (*Key, *Response, error) { - u := fmt.Sprintf("user/keys/%v", id) - - req, err := s.client.NewRequest("GET", u, nil) - if err != nil { - return nil, nil, err - } - - key := new(Key) - resp, err := s.client.Do(ctx, req, key) - if err != nil { - return nil, resp, err - } - - return key, resp, nil -} - -// CreateKey adds a public key for the authenticated user. -// -// GitHub API docs: https://developer.github.com/v3/users/keys/#create-a-public-key -func (s *UsersService) CreateKey(ctx context.Context, key *Key) (*Key, *Response, error) { - u := "user/keys" - - req, err := s.client.NewRequest("POST", u, key) - if err != nil { - return nil, nil, err - } - - k := new(Key) - resp, err := s.client.Do(ctx, req, k) - if err != nil { - return nil, resp, err - } - - return k, resp, nil -} - -// DeleteKey deletes a public key. -// -// GitHub API docs: https://developer.github.com/v3/users/keys/#delete-a-public-key -func (s *UsersService) DeleteKey(ctx context.Context, id int64) (*Response, error) { - u := fmt.Sprintf("user/keys/%v", id) - - req, err := s.client.NewRequest("DELETE", u, nil) - if err != nil { - return nil, err - } - - return s.client.Do(ctx, req, nil) -} diff --git a/vendor/github.com/google/go-github/github/with_appengine.go b/vendor/github.com/google/go-github/github/with_appengine.go deleted file mode 100644 index 87a228ad7be..00000000000 --- a/vendor/github.com/google/go-github/github/with_appengine.go +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2017 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build appengine - -// This file provides glue for making github work on App Engine. -// In order to get the entire github package to compile with -// Go 1.6, you will need to rewrite all the import "context" lines. -// Fortunately, this is easy with "gofmt": -// -// gofmt -w -r '"context" -> "golang.org/x/net/context"' *.go - -package github - -import ( - "context" - "net/http" -) - -func withContext(ctx context.Context, req *http.Request) *http.Request { - // No-op because App Engine adds context to a request differently. - return req -} diff --git a/vendor/github.com/google/go-github/github/without_appengine.go b/vendor/github.com/google/go-github/github/without_appengine.go deleted file mode 100644 index 6f8fdac5603..00000000000 --- a/vendor/github.com/google/go-github/github/without_appengine.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2017 The go-github AUTHORS. All rights reserved. -// -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !appengine - -// This file provides glue for making github work without App Engine. - -package github - -import ( - "context" - "net/http" -) - -func withContext(ctx context.Context, req *http.Request) *http.Request { - return req.WithContext(ctx) -} diff --git a/vendor/github.com/google/go-querystring/LICENSE b/vendor/github.com/google/go-querystring/LICENSE deleted file mode 100644 index ae121a1e46d..00000000000 --- a/vendor/github.com/google/go-querystring/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2013 Google. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/google/go-querystring/query/encode.go b/vendor/github.com/google/go-querystring/query/encode.go deleted file mode 100644 index 37080b19b5d..00000000000 --- a/vendor/github.com/google/go-querystring/query/encode.go +++ /dev/null @@ -1,320 +0,0 @@ -// Copyright 2013 The Go 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 query implements encoding of structs into URL query parameters. -// -// As a simple example: -// -// type Options struct { -// Query string `url:"q"` -// ShowAll bool `url:"all"` -// Page int `url:"page"` -// } -// -// opt := Options{ "foo", true, 2 } -// v, _ := query.Values(opt) -// fmt.Print(v.Encode()) // will output: "q=foo&all=true&page=2" -// -// The exact mapping between Go values and url.Values is described in the -// documentation for the Values() function. -package query - -import ( - "bytes" - "fmt" - "net/url" - "reflect" - "strconv" - "strings" - "time" -) - -var timeType = reflect.TypeOf(time.Time{}) - -var encoderType = reflect.TypeOf(new(Encoder)).Elem() - -// Encoder is an interface implemented by any type that wishes to encode -// itself into URL values in a non-standard way. -type Encoder interface { - EncodeValues(key string, v *url.Values) error -} - -// Values returns the url.Values encoding of v. -// -// Values expects to be passed a struct, and traverses it recursively using the -// following encoding rules. -// -// Each exported struct field is encoded as a URL parameter unless -// -// - the field's tag is "-", or -// - the field is empty and its tag specifies the "omitempty" option -// -// The empty values are false, 0, any nil pointer or interface value, any array -// slice, map, or string of length zero, and any time.Time that returns true -// for IsZero(). -// -// The URL parameter name defaults to the struct field name but can be -// specified in the struct field's tag value. The "url" key in the struct -// field's tag value is the key name, followed by an optional comma and -// options. For example: -// -// // Field is ignored by this package. -// Field int `url:"-"` -// -// // Field appears as URL parameter "myName". -// Field int `url:"myName"` -// -// // Field appears as URL parameter "myName" and the field is omitted if -// // its value is empty -// Field int `url:"myName,omitempty"` -// -// // Field appears as URL parameter "Field" (the default), but the field -// // is skipped if empty. Note the leading comma. -// Field int `url:",omitempty"` -// -// For encoding individual field values, the following type-dependent rules -// apply: -// -// Boolean values default to encoding as the strings "true" or "false". -// Including the "int" option signals that the field should be encoded as the -// strings "1" or "0". -// -// time.Time values default to encoding as RFC3339 timestamps. Including the -// "unix" option signals that the field should be encoded as a Unix time (see -// time.Unix()) -// -// Slice and Array values default to encoding as multiple URL values of the -// same name. Including the "comma" option signals that the field should be -// encoded as a single comma-delimited value. Including the "space" option -// similarly encodes the value as a single space-delimited string. Including -// the "semicolon" option will encode the value as a semicolon-delimited string. -// Including the "brackets" option signals that the multiple URL values should -// have "[]" appended to the value name. "numbered" will append a number to -// the end of each incidence of the value name, example: -// name0=value0&name1=value1, etc. -// -// Anonymous struct fields are usually encoded as if their inner exported -// fields were fields in the outer struct, subject to the standard Go -// visibility rules. An anonymous struct field with a name given in its URL -// tag is treated as having that name, rather than being anonymous. -// -// Non-nil pointer values are encoded as the value pointed to. -// -// Nested structs are encoded including parent fields in value names for -// scoping. e.g: -// -// "user[name]=acme&user[addr][postcode]=1234&user[addr][city]=SFO" -// -// All other values are encoded using their default string representation. -// -// Multiple fields that encode to the same URL parameter name will be included -// as multiple URL values of the same name. -func Values(v interface{}) (url.Values, error) { - values := make(url.Values) - val := reflect.ValueOf(v) - for val.Kind() == reflect.Ptr { - if val.IsNil() { - return values, nil - } - val = val.Elem() - } - - if v == nil { - return values, nil - } - - if val.Kind() != reflect.Struct { - return nil, fmt.Errorf("query: Values() expects struct input. Got %v", val.Kind()) - } - - err := reflectValue(values, val, "") - return values, err -} - -// reflectValue populates the values parameter from the struct fields in val. -// Embedded structs are followed recursively (using the rules defined in the -// Values function documentation) breadth-first. -func reflectValue(values url.Values, val reflect.Value, scope string) error { - var embedded []reflect.Value - - typ := val.Type() - for i := 0; i < typ.NumField(); i++ { - sf := typ.Field(i) - if sf.PkgPath != "" && !sf.Anonymous { // unexported - continue - } - - sv := val.Field(i) - tag := sf.Tag.Get("url") - if tag == "-" { - continue - } - name, opts := parseTag(tag) - if name == "" { - if sf.Anonymous && sv.Kind() == reflect.Struct { - // save embedded struct for later processing - embedded = append(embedded, sv) - continue - } - - name = sf.Name - } - - if scope != "" { - name = scope + "[" + name + "]" - } - - if opts.Contains("omitempty") && isEmptyValue(sv) { - continue - } - - if sv.Type().Implements(encoderType) { - if !reflect.Indirect(sv).IsValid() { - sv = reflect.New(sv.Type().Elem()) - } - - m := sv.Interface().(Encoder) - if err := m.EncodeValues(name, &values); err != nil { - return err - } - continue - } - - if sv.Kind() == reflect.Slice || sv.Kind() == reflect.Array { - var del byte - if opts.Contains("comma") { - del = ',' - } else if opts.Contains("space") { - del = ' ' - } else if opts.Contains("semicolon") { - del = ';' - } else if opts.Contains("brackets") { - name = name + "[]" - } - - if del != 0 { - s := new(bytes.Buffer) - first := true - for i := 0; i < sv.Len(); i++ { - if first { - first = false - } else { - s.WriteByte(del) - } - s.WriteString(valueString(sv.Index(i), opts)) - } - values.Add(name, s.String()) - } else { - for i := 0; i < sv.Len(); i++ { - k := name - if opts.Contains("numbered") { - k = fmt.Sprintf("%s%d", name, i) - } - values.Add(k, valueString(sv.Index(i), opts)) - } - } - continue - } - - for sv.Kind() == reflect.Ptr { - if sv.IsNil() { - break - } - sv = sv.Elem() - } - - if sv.Type() == timeType { - values.Add(name, valueString(sv, opts)) - continue - } - - if sv.Kind() == reflect.Struct { - reflectValue(values, sv, name) - continue - } - - values.Add(name, valueString(sv, opts)) - } - - for _, f := range embedded { - if err := reflectValue(values, f, scope); err != nil { - return err - } - } - - return nil -} - -// valueString returns the string representation of a value. -func valueString(v reflect.Value, opts tagOptions) string { - for v.Kind() == reflect.Ptr { - if v.IsNil() { - return "" - } - v = v.Elem() - } - - if v.Kind() == reflect.Bool && opts.Contains("int") { - if v.Bool() { - return "1" - } - return "0" - } - - if v.Type() == timeType { - t := v.Interface().(time.Time) - if opts.Contains("unix") { - return strconv.FormatInt(t.Unix(), 10) - } - return t.Format(time.RFC3339) - } - - return fmt.Sprint(v.Interface()) -} - -// isEmptyValue checks if a value should be considered empty for the purposes -// of omitting fields with the "omitempty" option. -func isEmptyValue(v reflect.Value) bool { - switch v.Kind() { - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - return v.Len() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Interface, reflect.Ptr: - return v.IsNil() - } - - if v.Type() == timeType { - return v.Interface().(time.Time).IsZero() - } - - return false -} - -// tagOptions is the string following a comma in a struct field's "url" tag, or -// the empty string. It does not include the leading comma. -type tagOptions []string - -// parseTag splits a struct field's url tag into its name and comma-separated -// options. -func parseTag(tag string) (string, tagOptions) { - s := strings.Split(tag, ",") - return s[0], s[1:] -} - -// Contains checks whether the tagOptions contains the specified option. -func (o tagOptions) Contains(option string) bool { - for _, s := range o { - if s == option { - return true - } - } - return false -} diff --git a/vendor/github.com/googleapis/gax-go/LICENSE b/vendor/github.com/googleapis/gax-go/LICENSE deleted file mode 100644 index 6d16b6578a2..00000000000 --- a/vendor/github.com/googleapis/gax-go/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright 2016, Google Inc. -All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/googleapis/gax-go/call_option.go b/vendor/github.com/googleapis/gax-go/call_option.go deleted file mode 100644 index 7b621643e94..00000000000 --- a/vendor/github.com/googleapis/gax-go/call_option.go +++ /dev/null @@ -1,157 +0,0 @@ -// Copyright 2016, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package gax - -import ( - "math/rand" - "time" - - "google.golang.org/grpc" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -// CallOption is an option used by Invoke to control behaviors of RPC calls. -// CallOption works by modifying relevant fields of CallSettings. -type CallOption interface { - // Resolve applies the option by modifying cs. - Resolve(cs *CallSettings) -} - -// Retryer is used by Invoke to determine retry behavior. -type Retryer interface { - // Retry reports whether a request should be retriedand how long to pause before retrying - // if the previous attempt returned with err. Invoke never calls Retry with nil error. - Retry(err error) (pause time.Duration, shouldRetry bool) -} - -type retryerOption func() Retryer - -func (o retryerOption) Resolve(s *CallSettings) { - s.Retry = o -} - -// WithRetry sets CallSettings.Retry to fn. -func WithRetry(fn func() Retryer) CallOption { - return retryerOption(fn) -} - -// OnCodes returns a Retryer that retries if and only if -// the previous attempt returns a GRPC error whose error code is stored in cc. -// Pause times between retries are specified by bo. -// -// bo is only used for its parameters; each Retryer has its own copy. -func OnCodes(cc []codes.Code, bo Backoff) Retryer { - return &boRetryer{ - backoff: bo, - codes: append([]codes.Code(nil), cc...), - } -} - -type boRetryer struct { - backoff Backoff - codes []codes.Code -} - -func (r *boRetryer) Retry(err error) (time.Duration, bool) { - st, ok := status.FromError(err) - if !ok { - return 0, false - } - c := st.Code() - for _, rc := range r.codes { - if c == rc { - return r.backoff.Pause(), true - } - } - return 0, false -} - -// Backoff implements exponential backoff. -// The wait time between retries is a random value between 0 and the "retry envelope". -// The envelope starts at Initial and increases by the factor of Multiplier every retry, -// but is capped at Max. -type Backoff struct { - // Initial is the initial value of the retry envelope, defaults to 1 second. - Initial time.Duration - - // Max is the maximum value of the retry envelope, defaults to 30 seconds. - Max time.Duration - - // Multiplier is the factor by which the retry envelope increases. - // It should be greater than 1 and defaults to 2. - Multiplier float64 - - // cur is the current retry envelope - cur time.Duration -} - -func (bo *Backoff) Pause() time.Duration { - if bo.Initial == 0 { - bo.Initial = time.Second - } - if bo.cur == 0 { - bo.cur = bo.Initial - } - if bo.Max == 0 { - bo.Max = 30 * time.Second - } - if bo.Multiplier < 1 { - bo.Multiplier = 2 - } - // Select a duration between zero and the current max. It might seem counterintuitive to - // have so much jitter, but https://www.awsarchitectureblog.com/2015/03/backoff.html - // argues that that is the best strategy. - d := time.Duration(rand.Int63n(int64(bo.cur))) - bo.cur = time.Duration(float64(bo.cur) * bo.Multiplier) - if bo.cur > bo.Max { - bo.cur = bo.Max - } - return d -} - -type grpcOpt []grpc.CallOption - -func (o grpcOpt) Resolve(s *CallSettings) { - s.GRPC = o -} - -func WithGRPCOptions(opt ...grpc.CallOption) CallOption { - return grpcOpt(append([]grpc.CallOption(nil), opt...)) -} - -type CallSettings struct { - // Retry returns a Retryer to be used to control retry logic of a method call. - // If Retry is nil or the returned Retryer is nil, the call will not be retried. - Retry func() Retryer - - // CallOptions to be forwarded to GRPC. - GRPC []grpc.CallOption -} diff --git a/vendor/github.com/googleapis/gax-go/gax.go b/vendor/github.com/googleapis/gax-go/gax.go deleted file mode 100644 index 5ebedff0d02..00000000000 --- a/vendor/github.com/googleapis/gax-go/gax.go +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2016, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Package gax contains a set of modules which aid the development of APIs -// for clients and servers based on gRPC and Google API conventions. -// -// Application code will rarely need to use this library directly. -// However, code generated automatically from API definition files can use it -// to simplify code generation and to provide more convenient and idiomatic API surfaces. -// -// This project is currently experimental and not supported. -package gax - -const Version = "0.1.0" diff --git a/vendor/github.com/googleapis/gax-go/header.go b/vendor/github.com/googleapis/gax-go/header.go deleted file mode 100644 index d81455eccd9..00000000000 --- a/vendor/github.com/googleapis/gax-go/header.go +++ /dev/null @@ -1,24 +0,0 @@ -package gax - -import "bytes" - -// XGoogHeader is for use by the Google Cloud Libraries only. -// -// XGoogHeader formats key-value pairs. -// The resulting string is suitable for x-goog-api-client header. -func XGoogHeader(keyval ...string) string { - if len(keyval) == 0 { - return "" - } - if len(keyval)%2 != 0 { - panic("gax.Header: odd argument count") - } - var buf bytes.Buffer - for i := 0; i < len(keyval); i += 2 { - buf.WriteByte(' ') - buf.WriteString(keyval[i]) - buf.WriteByte('/') - buf.WriteString(keyval[i+1]) - } - return buf.String()[1:] -} diff --git a/vendor/github.com/googleapis/gax-go/invoke.go b/vendor/github.com/googleapis/gax-go/invoke.go deleted file mode 100644 index 86049d826f8..00000000000 --- a/vendor/github.com/googleapis/gax-go/invoke.go +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright 2016, Google Inc. -// All rights reserved. -// -// Redistribution and use in source and binary forms, with or without -// modification, are permitted provided that the following conditions are -// met: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following disclaimer -// in the documentation and/or other materials provided with the -// distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived from -// this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -package gax - -import ( - "time" - - "golang.org/x/net/context" -) - -// A user defined call stub. -type APICall func(context.Context, CallSettings) error - -// Invoke calls the given APICall, -// performing retries as specified by opts, if any. -func Invoke(ctx context.Context, call APICall, opts ...CallOption) error { - var settings CallSettings - for _, opt := range opts { - opt.Resolve(&settings) - } - return invoke(ctx, call, settings, Sleep) -} - -// Sleep is similar to time.Sleep, but it can be interrupted by ctx.Done() closing. -// If interrupted, Sleep returns ctx.Err(). -func Sleep(ctx context.Context, d time.Duration) error { - t := time.NewTimer(d) - select { - case <-ctx.Done(): - t.Stop() - return ctx.Err() - case <-t.C: - return nil - } -} - -type sleeper func(ctx context.Context, d time.Duration) error - -// invoke implements Invoke, taking an additional sleeper argument for testing. -func invoke(ctx context.Context, call APICall, settings CallSettings, sp sleeper) error { - var retryer Retryer - for { - err := call(ctx, settings) - if err == nil { - return nil - } - if settings.Retry == nil { - return err - } - if retryer == nil { - if r := settings.Retry(); r != nil { - retryer = r - } else { - return err - } - } - if d, ok := retryer.Retry(err); !ok { - return err - } else if err = sp(ctx, d); err != nil { - return err - } - } -} diff --git a/vendor/github.com/pierrec/lz4/LICENSE b/vendor/github.com/pierrec/lz4/LICENSE deleted file mode 100644 index bd899d8353d..00000000000 --- a/vendor/github.com/pierrec/lz4/LICENSE +++ /dev/null @@ -1,28 +0,0 @@ -Copyright (c) 2015, Pierre Curto -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -* Neither the name of xxHash nor the names of its - contributors may be used to endorse or promote products derived from - this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - diff --git a/vendor/github.com/pierrec/lz4/block.go b/vendor/github.com/pierrec/lz4/block.go deleted file mode 100644 index ef24f17e57a..00000000000 --- a/vendor/github.com/pierrec/lz4/block.go +++ /dev/null @@ -1,397 +0,0 @@ -package lz4 - -import ( - "encoding/binary" - "errors" -) - -var ( - // ErrInvalidSourceShortBuffer is returned by UncompressBlock or CompressBLock when a compressed - // block is corrupted or the destination buffer is not large enough for the uncompressed data. - ErrInvalidSourceShortBuffer = errors.New("lz4: invalid source or destination buffer too short") - // ErrInvalid is returned when reading an invalid LZ4 archive. - ErrInvalid = errors.New("lz4: bad magic number") -) - -// blockHash hashes 4 bytes into a value < winSize. -func blockHash(x uint32) uint32 { - const hasher uint32 = 2654435761 // Knuth multiplicative hash. - return x * hasher >> hashShift -} - -// CompressBlockBound returns the maximum size of a given buffer of size n, when not compressible. -func CompressBlockBound(n int) int { - return n + n/255 + 16 -} - -// UncompressBlock uncompresses the source buffer into the destination one, -// and returns the uncompressed size. -// -// The destination buffer must be sized appropriately. -// -// An error is returned if the source data is invalid or the destination buffer is too small. -func UncompressBlock(src, dst []byte) (si int, err error) { - defer func() { - // It is now faster to let the runtime panic and recover on out of bound slice access - // than checking indices as we go along. - if recover() != nil { - err = ErrInvalidSourceShortBuffer - } - }() - sn := len(src) - if sn == 0 { - return 0, nil - } - var di int - - for { - // Literals and match lengths (token). - b := int(src[si]) - si++ - - // Literals. - if lLen := b >> 4; lLen > 0 { - if lLen == 0xF { - for src[si] == 0xFF { - lLen += 0xFF - si++ - } - lLen += int(src[si]) - si++ - } - i := si - si += lLen - di += copy(dst[di:], src[i:si]) - - if si >= sn { - return di, nil - } - } - - si++ - _ = src[si] // Bound check elimination. - offset := int(src[si-1]) | int(src[si])<<8 - si++ - - // Match. - mLen := b & 0xF - if mLen == 0xF { - for src[si] == 0xFF { - mLen += 0xFF - si++ - } - mLen += int(src[si]) - si++ - } - mLen += minMatch - - // Copy the match. - i := di - offset - if offset > 0 && mLen >= offset { - // Efficiently copy the match dst[di-offset:di] into the dst slice. - bytesToCopy := offset * (mLen / offset) - expanded := dst[i:] - for n := offset; n <= bytesToCopy+offset; n *= 2 { - copy(expanded[n:], expanded[:n]) - } - di += bytesToCopy - mLen -= bytesToCopy - } - di += copy(dst[di:], dst[i:i+mLen]) - } -} - -// CompressBlock compresses the source buffer into the destination one. -// This is the fast version of LZ4 compression and also the default one. -// The size of hashTable must be at least 64Kb. -// -// The size of the compressed data is returned. If it is 0 and no error, then the data is incompressible. -// -// An error is returned if the destination buffer is too small. -func CompressBlock(src, dst []byte, hashTable []int) (di int, err error) { - defer func() { - if recover() != nil { - err = ErrInvalidSourceShortBuffer - } - }() - - sn, dn := len(src)-mfLimit, len(dst) - if sn <= 0 || dn == 0 { - return 0, nil - } - var si int - - // Fast scan strategy: the hash table only stores the last 4 bytes sequences. - // const accInit = 1 << skipStrength - - anchor := si // Position of the current literals. - // acc := accInit // Variable step: improves performance on non-compressible data. - - for si < sn { - // Hash the next 4 bytes (sequence)... - match := binary.LittleEndian.Uint32(src[si:]) - h := blockHash(match) - - ref := hashTable[h] - hashTable[h] = si - if ref >= sn { // Invalid reference (dirty hashtable). - si++ - continue - } - offset := si - ref - if offset <= 0 || offset >= winSize || // Out of window. - match != binary.LittleEndian.Uint32(src[ref:]) { // Hash collision on different matches. - // si += acc >> skipStrength - // acc++ - si++ - continue - } - - // Match found. - // acc = accInit - lLen := si - anchor // Literal length. - - // Encode match length part 1. - si += minMatch - mLen := si // Match length has minMatch already. - // Find the longest match, first looking by batches of 8 bytes. - for si < sn && binary.LittleEndian.Uint64(src[si:]) == binary.LittleEndian.Uint64(src[si-offset:]) { - si += 8 - } - // Then byte by byte. - for si < sn && src[si] == src[si-offset] { - si++ - } - - mLen = si - mLen - if mLen < 0xF { - dst[di] = byte(mLen) - } else { - dst[di] = 0xF - } - - // Encode literals length. - if lLen < 0xF { - dst[di] |= byte(lLen << 4) - } else { - dst[di] |= 0xF0 - di++ - l := lLen - 0xF - for ; l >= 0xFF; l -= 0xFF { - dst[di] = 0xFF - di++ - } - dst[di] = byte(l) - } - di++ - - // Literals. - copy(dst[di:], src[anchor:anchor+lLen]) - di += lLen + 2 - anchor = si - - // Encode offset. - _ = dst[di] // Bound check elimination. - dst[di-2], dst[di-1] = byte(offset), byte(offset>>8) - - // Encode match length part 2. - if mLen >= 0xF { - for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF { - dst[di] = 0xFF - di++ - } - dst[di] = byte(mLen) - di++ - } - } - - if anchor == 0 { - // Incompressible. - return 0, nil - } - - // Last literals. - lLen := len(src) - anchor - if lLen < 0xF { - dst[di] = byte(lLen << 4) - } else { - dst[di] = 0xF0 - di++ - for lLen -= 0xF; lLen >= 0xFF; lLen -= 0xFF { - dst[di] = 0xFF - di++ - } - dst[di] = byte(lLen) - } - di++ - - // Write the last literals. - if di >= anchor { - // Incompressible. - return 0, nil - } - di += copy(dst[di:], src[anchor:]) - return di, nil -} - -// CompressBlockHC compresses the source buffer src into the destination dst -// with max search depth (use 0 or negative value for no max). -// -// CompressBlockHC compression ratio is better than CompressBlock but it is also slower. -// -// The size of the compressed data is returned. If it is 0 and no error, then the data is not compressible. -// -// An error is returned if the destination buffer is too small. -func CompressBlockHC(src, dst []byte, depth int) (di int, err error) { - defer func() { - if recover() != nil { - err = ErrInvalidSourceShortBuffer - } - }() - - sn, dn := len(src)-mfLimit, len(dst) - if sn <= 0 || dn == 0 { - return 0, nil - } - var si int - - // hashTable: stores the last position found for a given hash - // chaingTable: stores previous positions for a given hash - var hashTable, chainTable [winSize]int - - if depth <= 0 { - depth = winSize - } - - anchor := si - for si < sn { - // Hash the next 4 bytes (sequence). - match := binary.LittleEndian.Uint32(src[si:]) - h := blockHash(match) - - // Follow the chain until out of window and give the longest match. - mLen := 0 - offset := 0 - for next, try := hashTable[h], depth; try > 0 && next > 0 && si-next < winSize; next = chainTable[next&winMask] { - // The first (mLen==0) or next byte (mLen>=minMatch) at current match length - // must match to improve on the match length. - if src[next+mLen] != src[si+mLen] { - continue - } - ml := 0 - // Compare the current position with a previous with the same hash. - for ml < sn-si && binary.LittleEndian.Uint64(src[next+ml:]) == binary.LittleEndian.Uint64(src[si+ml:]) { - ml += 8 - } - for ml < sn-si && src[next+ml] == src[si+ml] { - ml++ - } - if ml+1 < minMatch || ml <= mLen { - // Match too small ( winStart { - winStart = ws - } - for si, ml := winStart, si+mLen; si < ml; { - match >>= 8 - match |= uint32(src[si+3]) << 24 - h := blockHash(match) - chainTable[si&winMask] = hashTable[h] - hashTable[h] = si - si++ - } - - lLen := si - anchor - si += mLen - mLen -= minMatch // Match length does not include minMatch. - - if mLen < 0xF { - dst[di] = byte(mLen) - } else { - dst[di] = 0xF - } - - // Encode literals length. - if lLen < 0xF { - dst[di] |= byte(lLen << 4) - } else { - dst[di] |= 0xF0 - di++ - l := lLen - 0xF - for ; l >= 0xFF; l -= 0xFF { - dst[di] = 0xFF - di++ - } - dst[di] = byte(l) - } - di++ - - // Literals. - copy(dst[di:], src[anchor:anchor+lLen]) - di += lLen - anchor = si - - // Encode offset. - di += 2 - dst[di-2], dst[di-1] = byte(offset), byte(offset>>8) - - // Encode match length part 2. - if mLen >= 0xF { - for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF { - dst[di] = 0xFF - di++ - } - dst[di] = byte(mLen) - di++ - } - } - - if anchor == 0 { - // Incompressible. - return 0, nil - } - - // Last literals. - lLen := len(src) - anchor - if lLen < 0xF { - dst[di] = byte(lLen << 4) - } else { - dst[di] = 0xF0 - di++ - lLen -= 0xF - for ; lLen >= 0xFF; lLen -= 0xFF { - dst[di] = 0xFF - di++ - } - dst[di] = byte(lLen) - } - di++ - - // Write the last literals. - if di >= anchor { - // Incompressible. - return 0, nil - } - di += copy(dst[di:], src[anchor:]) - return di, nil -} diff --git a/vendor/github.com/pierrec/lz4/debug.go b/vendor/github.com/pierrec/lz4/debug.go deleted file mode 100644 index 5a5b1b9507f..00000000000 --- a/vendor/github.com/pierrec/lz4/debug.go +++ /dev/null @@ -1,21 +0,0 @@ -// +build lz4debug - -package lz4 - -import ( - "fmt" - "os" - "path/filepath" - "runtime" -) - -func debug(args ...interface{}) { - _, file, line, _ := runtime.Caller(1) - file = filepath.Base(file) - - f := fmt.Sprintf("LZ4: %s:%d %s", file, line, args[0]) - if f[len(f)-1] != '\n' { - f += "\n" - } - fmt.Fprintf(os.Stderr, f, args[1:]...) -} diff --git a/vendor/github.com/pierrec/lz4/debug_stub.go b/vendor/github.com/pierrec/lz4/debug_stub.go deleted file mode 100644 index f57b90cf7a5..00000000000 --- a/vendor/github.com/pierrec/lz4/debug_stub.go +++ /dev/null @@ -1,5 +0,0 @@ -// +build !lz4debug - -package lz4 - -func debug(args ...interface{}) {} diff --git a/vendor/github.com/pierrec/lz4/internal/xxh32/xxh32.go b/vendor/github.com/pierrec/lz4/internal/xxh32/xxh32.go deleted file mode 100644 index da3cdef6477..00000000000 --- a/vendor/github.com/pierrec/lz4/internal/xxh32/xxh32.go +++ /dev/null @@ -1,230 +0,0 @@ -// Package xxh32 implements the very fast XXH hashing algorithm (32 bits version). -// (https://github.com/Cyan4973/XXH/) -package xxh32 - -import ( - "encoding/binary" - "hash" -) - -const ( - prime32_1 uint32 = 2654435761 - prime32_2 uint32 = 2246822519 - prime32_3 uint32 = 3266489917 - prime32_4 uint32 = 668265263 - prime32_5 uint32 = 374761393 - - prime32_1plus2 uint32 = 606290984 - prime32_minus1 uint32 = 1640531535 -) - -var _ hash.Hash32 = (*XXH)(nil) - -// XXH represents an xxhash32 object. -type XXH struct { - seed uint32 - v1 uint32 - v2 uint32 - v3 uint32 - v4 uint32 - totalLen uint64 - buf [16]byte - bufused int -} - -// Init sets the seed and Reset(). -func (xxh *XXH) Init(seed uint32) { - xxh.seed = seed - xxh.Reset() -} - -// Sum appends the current hash to b and returns the resulting slice. -// It does not change the underlying hash state. -func (xxh XXH) Sum(b []byte) []byte { - h32 := xxh.Sum32() - return append(b, byte(h32), byte(h32>>8), byte(h32>>16), byte(h32>>24)) -} - -// Reset resets the Hash to its initial state. -func (xxh *XXH) Reset() { - seed := xxh.seed - xxh.v1 = seed + prime32_1plus2 - xxh.v2 = seed + prime32_2 - xxh.v3 = seed - xxh.v4 = seed - prime32_1 - xxh.totalLen = 0 - xxh.bufused = 0 -} - -// Size returns the number of bytes returned by Sum(). -func (xxh *XXH) Size() int { - return 4 -} - -// BlockSize gives the minimum number of bytes accepted by Write(). -func (xxh *XXH) BlockSize() int { - return 1 -} - -// Write adds input bytes to the Hash. -// It never returns an error. -func (xxh *XXH) Write(input []byte) (int, error) { - n := len(input) - m := xxh.bufused - - xxh.totalLen += uint64(n) - - r := len(xxh.buf) - m - if n < r { - copy(xxh.buf[m:], input) - xxh.bufused += len(input) - return n, nil - } - - // Causes compiler to work directly from registers instead of stack: - v1, v2, v3, v4 := xxh.v1, xxh.v2, xxh.v3, xxh.v4 - p := 0 - if m > 0 { - // some data left from previous update - copy(xxh.buf[xxh.bufused:], input[:r]) - xxh.bufused += len(input) - r - - // fast rotl(13) - buf := xxh.buf[:16] // BCE hint. - xxh.v1 = rol13(v1+binary.LittleEndian.Uint32(buf[:])*prime32_2) * prime32_1 - xxh.v2 = rol13(v2+binary.LittleEndian.Uint32(buf[4:])*prime32_2) * prime32_1 - xxh.v3 = rol13(v3+binary.LittleEndian.Uint32(buf[8:])*prime32_2) * prime32_1 - xxh.v4 = rol13(v4+binary.LittleEndian.Uint32(buf[12:])*prime32_2) * prime32_1 - p = r - xxh.bufused = 0 - } - - for n := n - 16; p <= n; p += 16 { - sub := input[p:][:16] //BCE hint for compiler - v1 = rol13(v1+binary.LittleEndian.Uint32(sub[:])*prime32_2) * prime32_1 - v2 = rol13(v2+binary.LittleEndian.Uint32(sub[4:])*prime32_2) * prime32_1 - v3 = rol13(v3+binary.LittleEndian.Uint32(sub[8:])*prime32_2) * prime32_1 - v4 = rol13(v4+binary.LittleEndian.Uint32(sub[12:])*prime32_2) * prime32_1 - } - xxh.v1, xxh.v2, xxh.v3, xxh.v4 = v1, v2, v3, v4 - - copy(xxh.buf[xxh.bufused:], input[p:]) - xxh.bufused += len(input) - p - - return n, nil -} - -// Sum32 returns the 32 bits Hash value. -func (xxh *XXH) Sum32() uint32 { - h32 := uint32(xxh.totalLen) - if h32 >= 16 { - h32 += rol1(xxh.v1) + rol7(xxh.v2) + rol12(xxh.v3) + rol18(xxh.v4) - } else { - h32 += xxh.seed + prime32_5 - } - - p := 0 - n := xxh.bufused - buf := xxh.buf - for n := n - 4; p <= n; p += 4 { - h32 += binary.LittleEndian.Uint32(buf[p:p+4]) * prime32_3 - h32 = rol17(h32) * prime32_4 - } - for ; p < n; p++ { - h32 += uint32(buf[p]) * prime32_5 - h32 = rol11(h32) * prime32_1 - } - - h32 ^= h32 >> 15 - h32 *= prime32_2 - h32 ^= h32 >> 13 - h32 *= prime32_3 - h32 ^= h32 >> 16 - - return h32 -} - -// Checksum returns the 32bits Hash value. -func Checksum(input []byte, seed uint32) uint32 { - n := len(input) - h32 := uint32(n) - - if n < 16 { - h32 += seed + prime32_5 - } else { - v1 := seed + prime32_1 + prime32_2 - v2 := seed + prime32_2 - v3 := seed - v4 := seed - prime32_1 - p := 0 - for n := n - 16; p <= n; p += 16 { - sub := input[p:][:16] //BCE hint for compiler - v1 = rol13(v1+binary.LittleEndian.Uint32(sub[:])*prime32_2) * prime32_1 - v2 = rol13(v2+binary.LittleEndian.Uint32(sub[4:])*prime32_2) * prime32_1 - v3 = rol13(v3+binary.LittleEndian.Uint32(sub[8:])*prime32_2) * prime32_1 - v4 = rol13(v4+binary.LittleEndian.Uint32(sub[12:])*prime32_2) * prime32_1 - } - input = input[p:] - n -= p - h32 += rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) - } - - p := 0 - for n := n - 4; p <= n; p += 4 { - h32 += binary.LittleEndian.Uint32(input[p:p+4]) * prime32_3 - h32 = rol17(h32) * prime32_4 - } - for p < n { - h32 += uint32(input[p]) * prime32_5 - h32 = rol11(h32) * prime32_1 - p++ - } - - h32 ^= h32 >> 15 - h32 *= prime32_2 - h32 ^= h32 >> 13 - h32 *= prime32_3 - h32 ^= h32 >> 16 - - return h32 -} - -func rol1(u uint32) uint32 { - return u<<1 | u>>31 -} - -func rol7(u uint32) uint32 { - return u<<7 | u>>25 -} - -func rol11(u uint32) uint32 { - return u<<11 | u>>21 -} - -func rol12(u uint32) uint32 { - return u<<12 | u>>20 -} - -func rol13(u uint32) uint32 { - return u<<13 | u>>19 -} - -func rol17(u uint32) uint32 { - return u<<17 | u>>15 -} - -func rol18(u uint32) uint32 { - return u<<18 | u>>14 -} - -// Uint32 hashes x with the given seed. -func Uint32(x, seed uint32) uint32 { - h := seed + prime32_5 + 4 + x*prime32_3 - h = rol17(h) * prime32_4 - h ^= h >> 15 - h *= prime32_2 - h ^= h >> 13 - h *= prime32_3 - h ^= h >> 16 - return h -} diff --git a/vendor/github.com/pierrec/lz4/internal/xxh32/xxh32zero.go b/vendor/github.com/pierrec/lz4/internal/xxh32/xxh32zero.go deleted file mode 100644 index 6eff850a626..00000000000 --- a/vendor/github.com/pierrec/lz4/internal/xxh32/xxh32zero.go +++ /dev/null @@ -1,180 +0,0 @@ -// Package xxh32 implements the very fast XXH hashing algorithm (32 bits version). -// (https://github.com/Cyan4973/XXH/) -package xxh32 - -import ( - "encoding/binary" -) - -// XXHZero represents an xxhash32 object with seed 0. -type XXHZero struct { - v1 uint32 - v2 uint32 - v3 uint32 - v4 uint32 - totalLen uint64 - buf [16]byte - bufused int -} - -// Sum appends the current hash to b and returns the resulting slice. -// It does not change the underlying hash state. -func (xxh XXHZero) Sum(b []byte) []byte { - h32 := xxh.Sum32() - return append(b, byte(h32), byte(h32>>8), byte(h32>>16), byte(h32>>24)) -} - -// Reset resets the Hash to its initial state. -func (xxh *XXHZero) Reset() { - xxh.v1 = prime32_1plus2 - xxh.v2 = prime32_2 - xxh.v3 = 0 - xxh.v4 = prime32_minus1 - xxh.totalLen = 0 - xxh.bufused = 0 -} - -// Size returns the number of bytes returned by Sum(). -func (xxh *XXHZero) Size() int { - return 4 -} - -// BlockSize gives the minimum number of bytes accepted by Write(). -func (xxh *XXHZero) BlockSize() int { - return 1 -} - -// Write adds input bytes to the Hash. -// It never returns an error. -func (xxh *XXHZero) Write(input []byte) (int, error) { - n := len(input) - m := xxh.bufused - - xxh.totalLen += uint64(n) - - r := len(xxh.buf) - m - if n < r { - copy(xxh.buf[m:], input) - xxh.bufused += len(input) - return n, nil - } - - // Causes compiler to work directly from registers instead of stack: - v1, v2, v3, v4 := xxh.v1, xxh.v2, xxh.v3, xxh.v4 - p := 0 - if m > 0 { - // some data left from previous update - copy(xxh.buf[xxh.bufused:], input[:r]) - xxh.bufused += len(input) - r - - // fast rotl(13) - buf := xxh.buf[:16] // BCE hint. - xxh.v1 = rol13(v1+binary.LittleEndian.Uint32(buf[:])*prime32_2) * prime32_1 - xxh.v2 = rol13(v2+binary.LittleEndian.Uint32(buf[4:])*prime32_2) * prime32_1 - xxh.v3 = rol13(v3+binary.LittleEndian.Uint32(buf[8:])*prime32_2) * prime32_1 - xxh.v4 = rol13(v4+binary.LittleEndian.Uint32(buf[12:])*prime32_2) * prime32_1 - p = r - xxh.bufused = 0 - } - - for n := n - 16; p <= n; p += 16 { - sub := input[p:][:16] //BCE hint for compiler - v1 = rol13(v1+binary.LittleEndian.Uint32(sub[:])*prime32_2) * prime32_1 - v2 = rol13(v2+binary.LittleEndian.Uint32(sub[4:])*prime32_2) * prime32_1 - v3 = rol13(v3+binary.LittleEndian.Uint32(sub[8:])*prime32_2) * prime32_1 - v4 = rol13(v4+binary.LittleEndian.Uint32(sub[12:])*prime32_2) * prime32_1 - } - xxh.v1, xxh.v2, xxh.v3, xxh.v4 = v1, v2, v3, v4 - - copy(xxh.buf[xxh.bufused:], input[p:]) - xxh.bufused += len(input) - p - - return n, nil -} - -// Sum32 returns the 32 bits Hash value. -func (xxh *XXHZero) Sum32() uint32 { - h32 := uint32(xxh.totalLen) - if h32 >= 16 { - h32 += rol1(xxh.v1) + rol7(xxh.v2) + rol12(xxh.v3) + rol18(xxh.v4) - } else { - h32 += prime32_5 - } - - p := 0 - n := xxh.bufused - buf := xxh.buf - for n := n - 4; p <= n; p += 4 { - h32 += binary.LittleEndian.Uint32(buf[p:p+4]) * prime32_3 - h32 = rol17(h32) * prime32_4 - } - for ; p < n; p++ { - h32 += uint32(buf[p]) * prime32_5 - h32 = rol11(h32) * prime32_1 - } - - h32 ^= h32 >> 15 - h32 *= prime32_2 - h32 ^= h32 >> 13 - h32 *= prime32_3 - h32 ^= h32 >> 16 - - return h32 -} - -// ChecksumZero returns the 32bits Hash value. -func ChecksumZero(input []byte) uint32 { - n := len(input) - h32 := uint32(n) - - if n < 16 { - h32 += prime32_5 - } else { - v1 := prime32_1plus2 - v2 := prime32_2 - v3 := uint32(0) - v4 := prime32_minus1 - p := 0 - for n := n - 16; p <= n; p += 16 { - sub := input[p:][:16] //BCE hint for compiler - v1 = rol13(v1+binary.LittleEndian.Uint32(sub[:])*prime32_2) * prime32_1 - v2 = rol13(v2+binary.LittleEndian.Uint32(sub[4:])*prime32_2) * prime32_1 - v3 = rol13(v3+binary.LittleEndian.Uint32(sub[8:])*prime32_2) * prime32_1 - v4 = rol13(v4+binary.LittleEndian.Uint32(sub[12:])*prime32_2) * prime32_1 - } - input = input[p:] - n -= p - h32 += rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) - } - - p := 0 - for n := n - 4; p <= n; p += 4 { - h32 += binary.LittleEndian.Uint32(input[p:p+4]) * prime32_3 - h32 = rol17(h32) * prime32_4 - } - for p < n { - h32 += uint32(input[p]) * prime32_5 - h32 = rol11(h32) * prime32_1 - p++ - } - - h32 ^= h32 >> 15 - h32 *= prime32_2 - h32 ^= h32 >> 13 - h32 *= prime32_3 - h32 ^= h32 >> 16 - - return h32 -} - -// Uint32Zero hashes x with seed 0. -func Uint32Zero(x uint32) uint32 { - h := prime32_5 + 4 + x*prime32_3 - h = rol17(h) * prime32_4 - h ^= h >> 15 - h *= prime32_2 - h ^= h >> 13 - h *= prime32_3 - h ^= h >> 16 - return h -} diff --git a/vendor/github.com/pierrec/lz4/lz4.go b/vendor/github.com/pierrec/lz4/lz4.go deleted file mode 100644 index 35802756c48..00000000000 --- a/vendor/github.com/pierrec/lz4/lz4.go +++ /dev/null @@ -1,68 +0,0 @@ -// Package lz4 implements reading and writing lz4 compressed data (a frame), -// as specified in http://fastcompression.blogspot.fr/2013/04/lz4-streaming-format-final.html. -// -// Although the block level compression and decompression functions are exposed and are fully compatible -// with the lz4 block format definition, they are low level and should not be used directly. -// For a complete description of an lz4 compressed block, see: -// http://fastcompression.blogspot.fr/2011/05/lz4-explained.html -// -// See https://github.com/Cyan4973/lz4 for the reference C implementation. -// -package lz4 - -const ( - // Extension is the LZ4 frame file name extension - Extension = ".lz4" - // Version is the LZ4 frame format version - Version = 1 - - frameMagic uint32 = 0x184D2204 - frameSkipMagic uint32 = 0x184D2A50 - - // The following constants are used to setup the compression algorithm. - minMatch = 4 // the minimum size of the match sequence size (4 bytes) - winSizeLog = 16 // LZ4 64Kb window size limit - winSize = 1 << winSizeLog - winMask = winSize - 1 // 64Kb window of previous data for dependent blocks - compressedBlockFlag = 1 << 31 - compressedBlockMask = compressedBlockFlag - 1 - - // hashLog determines the size of the hash table used to quickly find a previous match position. - // Its value influences the compression speed and memory usage, the lower the faster, - // but at the expense of the compression ratio. - // 16 seems to be the best compromise. - hashLog = 16 - hashTableSize = 1 << hashLog - hashShift = uint((minMatch * 8) - hashLog) - - mfLimit = 8 + minMatch // The last match cannot start within the last 12 bytes. - skipStrength = 6 // variable step for fast scan -) - -// map the block max size id with its value in bytes: 64Kb, 256Kb, 1Mb and 4Mb. -var ( - bsMapID = map[byte]int{4: 64 << 10, 5: 256 << 10, 6: 1 << 20, 7: 4 << 20} - bsMapValue = make(map[int]byte, len(bsMapID)) -) - -// Reversed. -func init() { - for i, v := range bsMapID { - bsMapValue[v] = i - } -} - -// Header describes the various flags that can be set on a Writer or obtained from a Reader. -// The default values match those of the LZ4 frame format definition -// (http://fastcompression.blogspot.com/2013/04/lz4-streaming-format-final.html). -// -// NB. in a Reader, in case of concatenated frames, the Header values may change between Read() calls. -// It is the caller responsibility to check them if necessary. -type Header struct { - BlockChecksum bool // Compressed blocks checksum flag. - NoChecksum bool // Frame checksum flag. - BlockMaxSize int // Size of the uncompressed data block (one of [64KB, 256KB, 1MB, 4MB]). Default=4MB. - Size uint64 // Frame total size. It is _not_ computed by the Writer. - CompressionLevel int // Compression level (higher is better, use 0 for fastest compression). - done bool // Header processed flag (Read or Write and checked). -} diff --git a/vendor/github.com/pierrec/lz4/lz4_go1.10.go b/vendor/github.com/pierrec/lz4/lz4_go1.10.go deleted file mode 100644 index 9a0fb00709d..00000000000 --- a/vendor/github.com/pierrec/lz4/lz4_go1.10.go +++ /dev/null @@ -1,29 +0,0 @@ -//+build go1.10 - -package lz4 - -import ( - "fmt" - "strings" -) - -func (h Header) String() string { - var s strings.Builder - - s.WriteString(fmt.Sprintf("%T{", h)) - if h.BlockChecksum { - s.WriteString("BlockChecksum: true ") - } - if h.NoChecksum { - s.WriteString("NoChecksum: true ") - } - if bs := h.BlockMaxSize; bs != 0 && bs != 4<<20 { - s.WriteString(fmt.Sprintf("BlockMaxSize: %d ", bs)) - } - if l := h.CompressionLevel; l != 0 { - s.WriteString(fmt.Sprintf("CompressionLevel: %d ", l)) - } - s.WriteByte('}') - - return s.String() -} diff --git a/vendor/github.com/pierrec/lz4/lz4_notgo1.10.go b/vendor/github.com/pierrec/lz4/lz4_notgo1.10.go deleted file mode 100644 index 12c761a2e7f..00000000000 --- a/vendor/github.com/pierrec/lz4/lz4_notgo1.10.go +++ /dev/null @@ -1,29 +0,0 @@ -//+build !go1.10 - -package lz4 - -import ( - "bytes" - "fmt" -) - -func (h Header) String() string { - var s bytes.Buffer - - s.WriteString(fmt.Sprintf("%T{", h)) - if h.BlockChecksum { - s.WriteString("BlockChecksum: true ") - } - if h.NoChecksum { - s.WriteString("NoChecksum: true ") - } - if bs := h.BlockMaxSize; bs != 0 && bs != 4<<20 { - s.WriteString(fmt.Sprintf("BlockMaxSize: %d ", bs)) - } - if l := h.CompressionLevel; l != 0 { - s.WriteString(fmt.Sprintf("CompressionLevel: %d ", l)) - } - s.WriteByte('}') - - return s.String() -} diff --git a/vendor/github.com/pierrec/lz4/reader.go b/vendor/github.com/pierrec/lz4/reader.go deleted file mode 100644 index c7656760198..00000000000 --- a/vendor/github.com/pierrec/lz4/reader.go +++ /dev/null @@ -1,273 +0,0 @@ -package lz4 - -import ( - "encoding/binary" - "fmt" - "io" - "io/ioutil" - - "github.com/pierrec/lz4/internal/xxh32" -) - -// Reader implements the LZ4 frame decoder. -// The Header is set after the first call to Read(). -// The Header may change between Read() calls in case of concatenated frames. -type Reader struct { - Header - - buf [8]byte // Scrap buffer. - pos int64 // Current position in src. - src io.Reader // Source. - zdata []byte // Compressed data. - data []byte // Uncompressed data. - idx int // Index of unread bytes into data. - checksum xxh32.XXHZero // Frame hash. -} - -// NewReader returns a new LZ4 frame decoder. -// No access to the underlying io.Reader is performed. -func NewReader(src io.Reader) *Reader { - r := &Reader{src: src} - return r -} - -// readHeader checks the frame magic number and parses the frame descriptoz. -// Skippable frames are supported even as a first frame although the LZ4 -// specifications recommends skippable frames not to be used as first frames. -func (z *Reader) readHeader(first bool) error { - defer z.checksum.Reset() - - buf := z.buf[:] - for { - magic, err := z.readUint32() - if err != nil { - z.pos += 4 - if !first && err == io.ErrUnexpectedEOF { - return io.EOF - } - return err - } - if magic == frameMagic { - break - } - if magic>>8 != frameSkipMagic>>8 { - return ErrInvalid - } - skipSize, err := z.readUint32() - if err != nil { - return err - } - z.pos += 4 - m, err := io.CopyN(ioutil.Discard, z.src, int64(skipSize)) - if err != nil { - return err - } - z.pos += m - } - - // Header. - if _, err := io.ReadFull(z.src, buf[:2]); err != nil { - return err - } - z.pos += 8 - - b := buf[0] - if v := b >> 6; v != Version { - return fmt.Errorf("lz4: invalid version: got %d; expected %d", v, Version) - } - if b>>5&1 == 0 { - return fmt.Errorf("lz4: block dependency not supported") - } - z.BlockChecksum = b>>4&1 > 0 - frameSize := b>>3&1 > 0 - z.NoChecksum = b>>2&1 == 0 - - bmsID := buf[1] >> 4 & 0x7 - bSize, ok := bsMapID[bmsID] - if !ok { - return fmt.Errorf("lz4: invalid block max size ID: %d", bmsID) - } - z.BlockMaxSize = bSize - - // Allocate the compressed/uncompressed buffers. - // The compressed buffer cannot exceed the uncompressed one. - if n := 2 * bSize; cap(z.zdata) < n { - z.zdata = make([]byte, n, n) - } - debug("header block max size id=%d size=%d", bmsID, bSize) - z.zdata = z.zdata[:bSize] - z.data = z.zdata[:cap(z.zdata)][bSize:] - z.idx = len(z.data) - - z.checksum.Write(buf[0:2]) - - if frameSize { - buf := buf[:8] - if _, err := io.ReadFull(z.src, buf); err != nil { - return err - } - z.Size = binary.LittleEndian.Uint64(buf) - z.pos += 8 - z.checksum.Write(buf) - } - - // Header checksum. - if _, err := io.ReadFull(z.src, buf[:1]); err != nil { - return err - } - z.pos++ - if h := byte(z.checksum.Sum32() >> 8 & 0xFF); h != buf[0] { - return fmt.Errorf("lz4: invalid header checksum: got %x; expected %x", buf[0], h) - } - - z.Header.done = true - debug("header read: %v", z.Header) - - return nil -} - -// Read decompresses data from the underlying source into the supplied buffer. -// -// Since there can be multiple streams concatenated, Header values may -// change between calls to Read(). If that is the case, no data is actually read from -// the underlying io.Reader, to allow for potential input buffer resizing. -func (z *Reader) Read(buf []byte) (int, error) { - debug("Read buf len=%d", len(buf)) - if !z.Header.done { - if err := z.readHeader(true); err != nil { - return 0, err - } - debug("header read OK compressed buffer %d / %d uncompressed buffer %d : %d index=%d", - len(z.zdata), cap(z.zdata), len(z.data), cap(z.data), z.idx) - } - - if len(buf) == 0 { - return 0, nil - } - - if z.idx == len(z.data) { - // No data ready for reading, process the next block. - debug("reading block from writer") - // Block length: 0 = end of frame, highest bit set: uncompressed. - bLen, err := z.readUint32() - if err != nil { - return 0, err - } - z.pos += 4 - - if bLen == 0 { - // End of frame reached. - if !z.NoChecksum { - // Validate the frame checksum. - checksum, err := z.readUint32() - if err != nil { - return 0, err - } - debug("frame checksum got=%x / want=%x", z.checksum.Sum32(), checksum) - z.pos += 4 - if h := z.checksum.Sum32(); checksum != h { - return 0, fmt.Errorf("lz4: invalid frame checksum: got %x; expected %x", h, checksum) - } - } - - // Get ready for the next concatenated frame and keep the position. - pos := z.pos - z.Reset(z.src) - z.pos = pos - - // Since multiple frames can be concatenated, check for more. - return 0, z.readHeader(false) - } - - debug("raw block size %d", bLen) - if bLen&compressedBlockFlag > 0 { - // Uncompressed block. - bLen &= compressedBlockMask - debug("uncompressed block size %d", bLen) - if int(bLen) > cap(z.data) { - return 0, fmt.Errorf("lz4: invalid block size: %d", bLen) - } - z.data = z.data[:bLen] - if _, err := io.ReadFull(z.src, z.data); err != nil { - return 0, err - } - z.pos += int64(bLen) - - if z.BlockChecksum { - checksum, err := z.readUint32() - if err != nil { - return 0, err - } - z.pos += 4 - - if h := xxh32.ChecksumZero(z.data); h != checksum { - return 0, fmt.Errorf("lz4: invalid block checksum: got %x; expected %x", h, checksum) - } - } - - } else { - // Compressed block. - debug("compressed block size %d", bLen) - if int(bLen) > cap(z.data) { - return 0, fmt.Errorf("lz4: invalid block size: %d", bLen) - } - zdata := z.zdata[:bLen] - if _, err := io.ReadFull(z.src, zdata); err != nil { - return 0, err - } - z.pos += int64(bLen) - - if z.BlockChecksum { - checksum, err := z.readUint32() - if err != nil { - return 0, err - } - z.pos += 4 - - if h := xxh32.ChecksumZero(zdata); h != checksum { - return 0, fmt.Errorf("lz4: invalid block checksum: got %x; expected %x", h, checksum) - } - } - - n, err := UncompressBlock(zdata, z.data) - if err != nil { - return 0, err - } - z.data = z.data[:n] - } - - if !z.NoChecksum { - z.checksum.Write(z.data) - debug("current frame checksum %x", z.checksum.Sum32()) - } - z.idx = 0 - } - - n := copy(buf, z.data[z.idx:]) - z.idx += n - debug("copied %d bytes to input", n) - - return n, nil -} - -// Reset discards the Reader's state and makes it equivalent to the -// result of its original state from NewReader, but reading from r instead. -// This permits reusing a Reader rather than allocating a new one. -func (z *Reader) Reset(r io.Reader) { - z.Header = Header{} - z.pos = 0 - z.src = r - z.zdata = z.zdata[:0] - z.data = z.data[:0] - z.idx = 0 - z.checksum.Reset() -} - -// readUint32 reads an uint32 into the supplied buffer. -// The idea is to make use of the already allocated buffers avoiding additional allocations. -func (z *Reader) readUint32() (uint32, error) { - buf := z.buf[:4] - _, err := io.ReadFull(z.src, buf) - x := binary.LittleEndian.Uint32(buf) - return x, err -} diff --git a/vendor/github.com/pierrec/lz4/writer.go b/vendor/github.com/pierrec/lz4/writer.go deleted file mode 100644 index 62a561703c5..00000000000 --- a/vendor/github.com/pierrec/lz4/writer.go +++ /dev/null @@ -1,235 +0,0 @@ -package lz4 - -import ( - "encoding/binary" - "fmt" - "io" - - "github.com/pierrec/lz4/internal/xxh32" -) - -// Writer implements the LZ4 frame encoder. -type Writer struct { - Header - - buf [19]byte // magic number(4) + header(flags(2)+[Size(8)+DictID(4)]+checksum(1)) does not exceed 19 bytes - dst io.Writer // Destination. - checksum xxh32.XXHZero // Frame checksum. - zdata []byte // Compressed data. - data []byte // Data to be compressed. - idx int // Index into data. - hashtable [winSize]int // Hash table used in CompressBlock(). -} - -// NewWriter returns a new LZ4 frame encoder. -// No access to the underlying io.Writer is performed. -// The supplied Header is checked at the first Write. -// It is ok to change it before the first Write but then not until a Reset() is performed. -func NewWriter(dst io.Writer) *Writer { - return &Writer{dst: dst} -} - -// writeHeader builds and writes the header (magic+header) to the underlying io.Writer. -func (z *Writer) writeHeader() error { - // Default to 4Mb if BlockMaxSize is not set. - if z.Header.BlockMaxSize == 0 { - z.Header.BlockMaxSize = bsMapID[7] - } - // The only option that needs to be validated. - bSize := z.Header.BlockMaxSize - bSizeID, ok := bsMapValue[bSize] - if !ok { - return fmt.Errorf("lz4: invalid block max size: %d", bSize) - } - // Allocate the compressed/uncompressed buffers. - // The compressed buffer cannot exceed the uncompressed one. - if n := 2 * bSize; cap(z.zdata) < n { - z.zdata = make([]byte, n, n) - } - z.zdata = z.zdata[:bSize] - z.data = z.zdata[:cap(z.zdata)][bSize:] - z.idx = 0 - - // Size is optional. - buf := z.buf[:] - - // Set the fixed size data: magic number, block max size and flags. - binary.LittleEndian.PutUint32(buf[0:], frameMagic) - flg := byte(Version << 6) - flg |= 1 << 5 // No block dependency. - if z.Header.BlockChecksum { - flg |= 1 << 4 - } - if z.Header.Size > 0 { - flg |= 1 << 3 - } - if !z.Header.NoChecksum { - flg |= 1 << 2 - } - buf[4] = flg - buf[5] = bSizeID << 4 - - // Current buffer size: magic(4) + flags(1) + block max size (1). - n := 6 - // Optional items. - if z.Header.Size > 0 { - binary.LittleEndian.PutUint64(buf[n:], z.Header.Size) - n += 8 - } - - // The header checksum includes the flags, block max size and optional Size. - buf[n] = byte(xxh32.ChecksumZero(buf[4:n]) >> 8 & 0xFF) - z.checksum.Reset() - - // Header ready, write it out. - if _, err := z.dst.Write(buf[0 : n+1]); err != nil { - return err - } - z.Header.done = true - debug("wrote header %v", z.Header) - - return nil -} - -// Write compresses data from the supplied buffer into the underlying io.Writer. -// Write does not return until the data has been written. -func (z *Writer) Write(buf []byte) (int, error) { - if !z.Header.done { - if err := z.writeHeader(); err != nil { - return 0, err - } - } - - if !z.NoChecksum { - z.checksum.Write(buf) - } - debug("input buffer len=%d index=%d", len(buf), z.idx) - - var n int - for len(buf) > 0 { - // Accumulate the data to be compressed. - m := copy(z.data[z.idx:], buf) - n += m - z.idx += m - buf = buf[m:] - debug("%d bytes copied to buf, current index %d", n, z.idx) - - if z.idx < len(z.data) { - // Buffer not filled. - debug("need more data for compression") - return n, nil - } - - // Buffer full. - if err := z.compressBlock(z.data); err != nil { - return n, err - } - z.idx = 0 - } - - return n, nil -} - -// compressBlock compresses a block. -func (z *Writer) compressBlock(data []byte) error { - // The compressed block size cannot exceed the input's. - var zn int - var err error - - if level := z.Header.CompressionLevel; level != 0 { - zn, err = CompressBlockHC(data, z.zdata, level) - } else { - zn, err = CompressBlock(data, z.zdata, z.hashtable[:]) - } - - var zdata []byte - var bLen uint32 - debug("block compression %d => %d", len(data), zn) - if err == nil && zn > 0 && zn < len(data) { - // Compressible and compressed size smaller than uncompressed: ok! - bLen = uint32(zn) - zdata = z.zdata[:zn] - } else { - // Uncompressed block. - bLen = uint32(len(data)) | compressedBlockFlag - zdata = data - } - debug("block compression to be written len=%d data len=%d", bLen, len(zdata)) - - // Write the block. - if err := z.writeUint32(bLen); err != nil { - return err - } - if _, err := z.dst.Write(zdata); err != nil { - return err - } - - if z.BlockChecksum { - checksum := xxh32.ChecksumZero(zdata) - debug("block checksum %x", checksum) - if err := z.writeUint32(checksum); err != nil { - return err - } - } - debug("current frame checksum %x", z.checksum.Sum32()) - - return nil -} - -// Flush flushes any pending compressed data to the underlying writer. -// Flush does not return until the data has been written. -// If the underlying writer returns an error, Flush returns that error. -func (z *Writer) Flush() error { - debug("flush with index %d", z.idx) - if z.idx == 0 { - return nil - } - - return z.compressBlock(z.data[:z.idx]) -} - -// Close closes the Writer, flushing any unwritten data to the underlying io.Writer, but does not close the underlying io.Writer. -func (z *Writer) Close() error { - if !z.Header.done { - if err := z.writeHeader(); err != nil { - return err - } - } - - if err := z.Flush(); err != nil { - return err - } - - debug("writing last empty block") - if err := z.writeUint32(0); err != nil { - return err - } - if !z.NoChecksum { - checksum := z.checksum.Sum32() - debug("stream checksum %x", checksum) - if err := z.writeUint32(checksum); err != nil { - return err - } - } - return nil -} - -// Reset clears the state of the Writer z such that it is equivalent to its -// initial state from NewWriter, but instead writing to w. -// No access to the underlying io.Writer is performed. -func (z *Writer) Reset(w io.Writer) { - z.Header = Header{} - z.dst = w - z.checksum.Reset() - z.zdata = z.zdata[:0] - z.data = z.data[:0] - z.idx = 0 -} - -// writeUint32 writes a uint32 to the underlying writer. -func (z *Writer) writeUint32(x uint32) error { - buf := z.buf[:4] - binary.LittleEndian.PutUint32(buf, x) - _, err := z.dst.Write(buf) - return err -} diff --git a/vendor/github.com/rcrowley/go-metrics/LICENSE b/vendor/github.com/rcrowley/go-metrics/LICENSE deleted file mode 100644 index 363fa9ee77b..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/LICENSE +++ /dev/null @@ -1,29 +0,0 @@ -Copyright 2012 Richard Crowley. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - 1. Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. - - 2. Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - -THIS SOFTWARE IS PROVIDED BY RICHARD CROWLEY ``AS IS'' AND ANY EXPRESS -OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL RICHARD CROWLEY OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR -CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF -SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS -INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN -CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) -ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF -THE POSSIBILITY OF SUCH DAMAGE. - -The views and conclusions contained in the software and documentation -are those of the authors and should not be interpreted as representing -official policies, either expressed or implied, of Richard Crowley. diff --git a/vendor/github.com/rcrowley/go-metrics/counter.go b/vendor/github.com/rcrowley/go-metrics/counter.go deleted file mode 100644 index bb7b039cb57..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/counter.go +++ /dev/null @@ -1,112 +0,0 @@ -package metrics - -import "sync/atomic" - -// Counters hold an int64 value that can be incremented and decremented. -type Counter interface { - Clear() - Count() int64 - Dec(int64) - Inc(int64) - Snapshot() Counter -} - -// GetOrRegisterCounter returns an existing Counter or constructs and registers -// a new StandardCounter. -func GetOrRegisterCounter(name string, r Registry) Counter { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewCounter).(Counter) -} - -// NewCounter constructs a new StandardCounter. -func NewCounter() Counter { - if UseNilMetrics { - return NilCounter{} - } - return &StandardCounter{0} -} - -// NewRegisteredCounter constructs and registers a new StandardCounter. -func NewRegisteredCounter(name string, r Registry) Counter { - c := NewCounter() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// CounterSnapshot is a read-only copy of another Counter. -type CounterSnapshot int64 - -// Clear panics. -func (CounterSnapshot) Clear() { - panic("Clear called on a CounterSnapshot") -} - -// Count returns the count at the time the snapshot was taken. -func (c CounterSnapshot) Count() int64 { return int64(c) } - -// Dec panics. -func (CounterSnapshot) Dec(int64) { - panic("Dec called on a CounterSnapshot") -} - -// Inc panics. -func (CounterSnapshot) Inc(int64) { - panic("Inc called on a CounterSnapshot") -} - -// Snapshot returns the snapshot. -func (c CounterSnapshot) Snapshot() Counter { return c } - -// NilCounter is a no-op Counter. -type NilCounter struct{} - -// Clear is a no-op. -func (NilCounter) Clear() {} - -// Count is a no-op. -func (NilCounter) Count() int64 { return 0 } - -// Dec is a no-op. -func (NilCounter) Dec(i int64) {} - -// Inc is a no-op. -func (NilCounter) Inc(i int64) {} - -// Snapshot is a no-op. -func (NilCounter) Snapshot() Counter { return NilCounter{} } - -// StandardCounter is the standard implementation of a Counter and uses the -// sync/atomic package to manage a single int64 value. -type StandardCounter struct { - count int64 -} - -// Clear sets the counter to zero. -func (c *StandardCounter) Clear() { - atomic.StoreInt64(&c.count, 0) -} - -// Count returns the current count. -func (c *StandardCounter) Count() int64 { - return atomic.LoadInt64(&c.count) -} - -// Dec decrements the counter by the given amount. -func (c *StandardCounter) Dec(i int64) { - atomic.AddInt64(&c.count, -i) -} - -// Inc increments the counter by the given amount. -func (c *StandardCounter) Inc(i int64) { - atomic.AddInt64(&c.count, i) -} - -// Snapshot returns a read-only copy of the counter. -func (c *StandardCounter) Snapshot() Counter { - return CounterSnapshot(c.Count()) -} diff --git a/vendor/github.com/rcrowley/go-metrics/debug.go b/vendor/github.com/rcrowley/go-metrics/debug.go deleted file mode 100644 index 043ccefab61..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/debug.go +++ /dev/null @@ -1,76 +0,0 @@ -package metrics - -import ( - "runtime/debug" - "time" -) - -var ( - debugMetrics struct { - GCStats struct { - LastGC Gauge - NumGC Gauge - Pause Histogram - //PauseQuantiles Histogram - PauseTotal Gauge - } - ReadGCStats Timer - } - gcStats debug.GCStats -) - -// Capture new values for the Go garbage collector statistics exported in -// debug.GCStats. This is designed to be called as a goroutine. -func CaptureDebugGCStats(r Registry, d time.Duration) { - for _ = range time.Tick(d) { - CaptureDebugGCStatsOnce(r) - } -} - -// Capture new values for the Go garbage collector statistics exported in -// debug.GCStats. This is designed to be called in a background goroutine. -// Giving a registry which has not been given to RegisterDebugGCStats will -// panic. -// -// Be careful (but much less so) with this because debug.ReadGCStats calls -// the C function runtime·lock(runtime·mheap) which, while not a stop-the-world -// operation, isn't something you want to be doing all the time. -func CaptureDebugGCStatsOnce(r Registry) { - lastGC := gcStats.LastGC - t := time.Now() - debug.ReadGCStats(&gcStats) - debugMetrics.ReadGCStats.UpdateSince(t) - - debugMetrics.GCStats.LastGC.Update(int64(gcStats.LastGC.UnixNano())) - debugMetrics.GCStats.NumGC.Update(int64(gcStats.NumGC)) - if lastGC != gcStats.LastGC && 0 < len(gcStats.Pause) { - debugMetrics.GCStats.Pause.Update(int64(gcStats.Pause[0])) - } - //debugMetrics.GCStats.PauseQuantiles.Update(gcStats.PauseQuantiles) - debugMetrics.GCStats.PauseTotal.Update(int64(gcStats.PauseTotal)) -} - -// Register metrics for the Go garbage collector statistics exported in -// debug.GCStats. The metrics are named by their fully-qualified Go symbols, -// i.e. debug.GCStats.PauseTotal. -func RegisterDebugGCStats(r Registry) { - debugMetrics.GCStats.LastGC = NewGauge() - debugMetrics.GCStats.NumGC = NewGauge() - debugMetrics.GCStats.Pause = NewHistogram(NewExpDecaySample(1028, 0.015)) - //debugMetrics.GCStats.PauseQuantiles = NewHistogram(NewExpDecaySample(1028, 0.015)) - debugMetrics.GCStats.PauseTotal = NewGauge() - debugMetrics.ReadGCStats = NewTimer() - - r.Register("debug.GCStats.LastGC", debugMetrics.GCStats.LastGC) - r.Register("debug.GCStats.NumGC", debugMetrics.GCStats.NumGC) - r.Register("debug.GCStats.Pause", debugMetrics.GCStats.Pause) - //r.Register("debug.GCStats.PauseQuantiles", debugMetrics.GCStats.PauseQuantiles) - r.Register("debug.GCStats.PauseTotal", debugMetrics.GCStats.PauseTotal) - r.Register("debug.ReadGCStats", debugMetrics.ReadGCStats) -} - -// Allocate an initial slice for gcStats.Pause to avoid allocations during -// normal operation. -func init() { - gcStats.Pause = make([]time.Duration, 11) -} diff --git a/vendor/github.com/rcrowley/go-metrics/ewma.go b/vendor/github.com/rcrowley/go-metrics/ewma.go deleted file mode 100644 index a8183dd7e21..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/ewma.go +++ /dev/null @@ -1,138 +0,0 @@ -package metrics - -import ( - "math" - "sync" - "sync/atomic" -) - -// EWMAs continuously calculate an exponentially-weighted moving average -// based on an outside source of clock ticks. -type EWMA interface { - Rate() float64 - Snapshot() EWMA - Tick() - Update(int64) -} - -// NewEWMA constructs a new EWMA with the given alpha. -func NewEWMA(alpha float64) EWMA { - if UseNilMetrics { - return NilEWMA{} - } - return &StandardEWMA{alpha: alpha} -} - -// NewEWMA1 constructs a new EWMA for a one-minute moving average. -func NewEWMA1() EWMA { - return NewEWMA(1 - math.Exp(-5.0/60.0/1)) -} - -// NewEWMA5 constructs a new EWMA for a five-minute moving average. -func NewEWMA5() EWMA { - return NewEWMA(1 - math.Exp(-5.0/60.0/5)) -} - -// NewEWMA15 constructs a new EWMA for a fifteen-minute moving average. -func NewEWMA15() EWMA { - return NewEWMA(1 - math.Exp(-5.0/60.0/15)) -} - -// EWMASnapshot is a read-only copy of another EWMA. -type EWMASnapshot float64 - -// Rate returns the rate of events per second at the time the snapshot was -// taken. -func (a EWMASnapshot) Rate() float64 { return float64(a) } - -// Snapshot returns the snapshot. -func (a EWMASnapshot) Snapshot() EWMA { return a } - -// Tick panics. -func (EWMASnapshot) Tick() { - panic("Tick called on an EWMASnapshot") -} - -// Update panics. -func (EWMASnapshot) Update(int64) { - panic("Update called on an EWMASnapshot") -} - -// NilEWMA is a no-op EWMA. -type NilEWMA struct{} - -// Rate is a no-op. -func (NilEWMA) Rate() float64 { return 0.0 } - -// Snapshot is a no-op. -func (NilEWMA) Snapshot() EWMA { return NilEWMA{} } - -// Tick is a no-op. -func (NilEWMA) Tick() {} - -// Update is a no-op. -func (NilEWMA) Update(n int64) {} - -// StandardEWMA is the standard implementation of an EWMA and tracks the number -// of uncounted events and processes them on each tick. It uses the -// sync/atomic package to manage uncounted events. -type StandardEWMA struct { - uncounted int64 // /!\ this should be the first member to ensure 64-bit alignment - alpha float64 - rate uint64 - init uint32 - mutex sync.Mutex -} - -// Rate returns the moving average rate of events per second. -func (a *StandardEWMA) Rate() float64 { - currentRate := math.Float64frombits(atomic.LoadUint64(&a.rate)) * float64(1e9) - return currentRate -} - -// Snapshot returns a read-only copy of the EWMA. -func (a *StandardEWMA) Snapshot() EWMA { - return EWMASnapshot(a.Rate()) -} - -// Tick ticks the clock to update the moving average. It assumes it is called -// every five seconds. -func (a *StandardEWMA) Tick() { - // Optimization to avoid mutex locking in the hot-path. - if atomic.LoadUint32(&a.init) == 1 { - a.updateRate(a.fetchInstantRate()) - } else { - // Slow-path: this is only needed on the first Tick() and preserves transactional updating - // of init and rate in the else block. The first conditional is needed below because - // a different thread could have set a.init = 1 between the time of the first atomic load and when - // the lock was acquired. - a.mutex.Lock() - if atomic.LoadUint32(&a.init) == 1 { - // The fetchInstantRate() uses atomic loading, which is unecessary in this critical section - // but again, this section is only invoked on the first successful Tick() operation. - a.updateRate(a.fetchInstantRate()) - } else { - atomic.StoreUint32(&a.init, 1) - atomic.StoreUint64(&a.rate, math.Float64bits(a.fetchInstantRate())) - } - a.mutex.Unlock() - } -} - -func (a *StandardEWMA) fetchInstantRate() float64 { - count := atomic.LoadInt64(&a.uncounted) - atomic.AddInt64(&a.uncounted, -count) - instantRate := float64(count) / float64(5e9) - return instantRate -} - -func (a *StandardEWMA) updateRate(instantRate float64) { - currentRate := math.Float64frombits(atomic.LoadUint64(&a.rate)) - currentRate += a.alpha * (instantRate - currentRate) - atomic.StoreUint64(&a.rate, math.Float64bits(currentRate)) -} - -// Update adds n uncounted events. -func (a *StandardEWMA) Update(n int64) { - atomic.AddInt64(&a.uncounted, n) -} diff --git a/vendor/github.com/rcrowley/go-metrics/gauge.go b/vendor/github.com/rcrowley/go-metrics/gauge.go deleted file mode 100644 index cb57a93889f..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/gauge.go +++ /dev/null @@ -1,120 +0,0 @@ -package metrics - -import "sync/atomic" - -// Gauges hold an int64 value that can be set arbitrarily. -type Gauge interface { - Snapshot() Gauge - Update(int64) - Value() int64 -} - -// GetOrRegisterGauge returns an existing Gauge or constructs and registers a -// new StandardGauge. -func GetOrRegisterGauge(name string, r Registry) Gauge { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewGauge).(Gauge) -} - -// NewGauge constructs a new StandardGauge. -func NewGauge() Gauge { - if UseNilMetrics { - return NilGauge{} - } - return &StandardGauge{0} -} - -// NewRegisteredGauge constructs and registers a new StandardGauge. -func NewRegisteredGauge(name string, r Registry) Gauge { - c := NewGauge() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// NewFunctionalGauge constructs a new FunctionalGauge. -func NewFunctionalGauge(f func() int64) Gauge { - if UseNilMetrics { - return NilGauge{} - } - return &FunctionalGauge{value: f} -} - -// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge. -func NewRegisteredFunctionalGauge(name string, r Registry, f func() int64) Gauge { - c := NewFunctionalGauge(f) - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// GaugeSnapshot is a read-only copy of another Gauge. -type GaugeSnapshot int64 - -// Snapshot returns the snapshot. -func (g GaugeSnapshot) Snapshot() Gauge { return g } - -// Update panics. -func (GaugeSnapshot) Update(int64) { - panic("Update called on a GaugeSnapshot") -} - -// Value returns the value at the time the snapshot was taken. -func (g GaugeSnapshot) Value() int64 { return int64(g) } - -// NilGauge is a no-op Gauge. -type NilGauge struct{} - -// Snapshot is a no-op. -func (NilGauge) Snapshot() Gauge { return NilGauge{} } - -// Update is a no-op. -func (NilGauge) Update(v int64) {} - -// Value is a no-op. -func (NilGauge) Value() int64 { return 0 } - -// StandardGauge is the standard implementation of a Gauge and uses the -// sync/atomic package to manage a single int64 value. -type StandardGauge struct { - value int64 -} - -// Snapshot returns a read-only copy of the gauge. -func (g *StandardGauge) Snapshot() Gauge { - return GaugeSnapshot(g.Value()) -} - -// Update updates the gauge's value. -func (g *StandardGauge) Update(v int64) { - atomic.StoreInt64(&g.value, v) -} - -// Value returns the gauge's current value. -func (g *StandardGauge) Value() int64 { - return atomic.LoadInt64(&g.value) -} - -// FunctionalGauge returns value from given function -type FunctionalGauge struct { - value func() int64 -} - -// Value returns the gauge's current value. -func (g FunctionalGauge) Value() int64 { - return g.value() -} - -// Snapshot returns the snapshot. -func (g FunctionalGauge) Snapshot() Gauge { return GaugeSnapshot(g.Value()) } - -// Update panics. -func (FunctionalGauge) Update(int64) { - panic("Update called on a FunctionalGauge") -} diff --git a/vendor/github.com/rcrowley/go-metrics/gauge_float64.go b/vendor/github.com/rcrowley/go-metrics/gauge_float64.go deleted file mode 100644 index 3962e6db09a..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/gauge_float64.go +++ /dev/null @@ -1,125 +0,0 @@ -package metrics - -import ( - "math" - "sync/atomic" -) - -// GaugeFloat64s hold a float64 value that can be set arbitrarily. -type GaugeFloat64 interface { - Snapshot() GaugeFloat64 - Update(float64) - Value() float64 -} - -// GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a -// new StandardGaugeFloat64. -func GetOrRegisterGaugeFloat64(name string, r Registry) GaugeFloat64 { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewGaugeFloat64()).(GaugeFloat64) -} - -// NewGaugeFloat64 constructs a new StandardGaugeFloat64. -func NewGaugeFloat64() GaugeFloat64 { - if UseNilMetrics { - return NilGaugeFloat64{} - } - return &StandardGaugeFloat64{ - value: 0.0, - } -} - -// NewRegisteredGaugeFloat64 constructs and registers a new StandardGaugeFloat64. -func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 { - c := NewGaugeFloat64() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// NewFunctionalGauge constructs a new FunctionalGauge. -func NewFunctionalGaugeFloat64(f func() float64) GaugeFloat64 { - if UseNilMetrics { - return NilGaugeFloat64{} - } - return &FunctionalGaugeFloat64{value: f} -} - -// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge. -func NewRegisteredFunctionalGaugeFloat64(name string, r Registry, f func() float64) GaugeFloat64 { - c := NewFunctionalGaugeFloat64(f) - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// GaugeFloat64Snapshot is a read-only copy of another GaugeFloat64. -type GaugeFloat64Snapshot float64 - -// Snapshot returns the snapshot. -func (g GaugeFloat64Snapshot) Snapshot() GaugeFloat64 { return g } - -// Update panics. -func (GaugeFloat64Snapshot) Update(float64) { - panic("Update called on a GaugeFloat64Snapshot") -} - -// Value returns the value at the time the snapshot was taken. -func (g GaugeFloat64Snapshot) Value() float64 { return float64(g) } - -// NilGauge is a no-op Gauge. -type NilGaugeFloat64 struct{} - -// Snapshot is a no-op. -func (NilGaugeFloat64) Snapshot() GaugeFloat64 { return NilGaugeFloat64{} } - -// Update is a no-op. -func (NilGaugeFloat64) Update(v float64) {} - -// Value is a no-op. -func (NilGaugeFloat64) Value() float64 { return 0.0 } - -// StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses -// sync.Mutex to manage a single float64 value. -type StandardGaugeFloat64 struct { - value uint64 -} - -// Snapshot returns a read-only copy of the gauge. -func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64 { - return GaugeFloat64Snapshot(g.Value()) -} - -// Update updates the gauge's value. -func (g *StandardGaugeFloat64) Update(v float64) { - atomic.StoreUint64(&g.value, math.Float64bits(v)) -} - -// Value returns the gauge's current value. -func (g *StandardGaugeFloat64) Value() float64 { - return math.Float64frombits(atomic.LoadUint64(&g.value)) -} - -// FunctionalGaugeFloat64 returns value from given function -type FunctionalGaugeFloat64 struct { - value func() float64 -} - -// Value returns the gauge's current value. -func (g FunctionalGaugeFloat64) Value() float64 { - return g.value() -} - -// Snapshot returns the snapshot. -func (g FunctionalGaugeFloat64) Snapshot() GaugeFloat64 { return GaugeFloat64Snapshot(g.Value()) } - -// Update panics. -func (FunctionalGaugeFloat64) Update(float64) { - panic("Update called on a FunctionalGaugeFloat64") -} diff --git a/vendor/github.com/rcrowley/go-metrics/graphite.go b/vendor/github.com/rcrowley/go-metrics/graphite.go deleted file mode 100644 index abd0a7d2918..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/graphite.go +++ /dev/null @@ -1,113 +0,0 @@ -package metrics - -import ( - "bufio" - "fmt" - "log" - "net" - "strconv" - "strings" - "time" -) - -// GraphiteConfig provides a container with configuration parameters for -// the Graphite exporter -type GraphiteConfig struct { - Addr *net.TCPAddr // Network address to connect to - Registry Registry // Registry to be exported - FlushInterval time.Duration // Flush interval - DurationUnit time.Duration // Time conversion unit for durations - Prefix string // Prefix to be prepended to metric names - Percentiles []float64 // Percentiles to export from timers and histograms -} - -// Graphite is a blocking exporter function which reports metrics in r -// to a graphite server located at addr, flushing them every d duration -// and prepending metric names with prefix. -func Graphite(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) { - GraphiteWithConfig(GraphiteConfig{ - Addr: addr, - Registry: r, - FlushInterval: d, - DurationUnit: time.Nanosecond, - Prefix: prefix, - Percentiles: []float64{0.5, 0.75, 0.95, 0.99, 0.999}, - }) -} - -// GraphiteWithConfig is a blocking exporter function just like Graphite, -// but it takes a GraphiteConfig instead. -func GraphiteWithConfig(c GraphiteConfig) { - log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015") - for _ = range time.Tick(c.FlushInterval) { - if err := graphite(&c); nil != err { - log.Println(err) - } - } -} - -// GraphiteOnce performs a single submission to Graphite, returning a -// non-nil error on failed connections. This can be used in a loop -// similar to GraphiteWithConfig for custom error handling. -func GraphiteOnce(c GraphiteConfig) error { - log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015") - return graphite(&c) -} - -func graphite(c *GraphiteConfig) error { - now := time.Now().Unix() - du := float64(c.DurationUnit) - conn, err := net.DialTCP("tcp", nil, c.Addr) - if nil != err { - return err - } - defer conn.Close() - w := bufio.NewWriter(conn) - c.Registry.Each(func(name string, i interface{}) { - switch metric := i.(type) { - case Counter: - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Count(), now) - case Gauge: - fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now) - case GaugeFloat64: - fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now) - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles(c.Percentiles) - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, h.Count(), now) - fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, h.Min(), now) - fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, h.Max(), now) - fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, h.Mean(), now) - fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, h.StdDev(), now) - for psIdx, psKey := range c.Percentiles { - key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1) - fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now) - } - case Meter: - m := metric.Snapshot() - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, m.Count(), now) - fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, m.Rate1(), now) - fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now) - fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now) - fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now) - case Timer: - t := metric.Snapshot() - ps := t.Percentiles(c.Percentiles) - fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, t.Count(), now) - fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, t.Min()/int64(du), now) - fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, t.Max()/int64(du), now) - fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, t.Mean()/du, now) - fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, t.StdDev()/du, now) - for psIdx, psKey := range c.Percentiles { - key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1) - fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now) - } - fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, t.Rate1(), now) - fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, t.Rate5(), now) - fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, t.Rate15(), now) - fmt.Fprintf(w, "%s.%s.mean-rate %.2f %d\n", c.Prefix, name, t.RateMean(), now) - } - w.Flush() - }) - return nil -} diff --git a/vendor/github.com/rcrowley/go-metrics/healthcheck.go b/vendor/github.com/rcrowley/go-metrics/healthcheck.go deleted file mode 100644 index 445131caee5..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/healthcheck.go +++ /dev/null @@ -1,61 +0,0 @@ -package metrics - -// Healthchecks hold an error value describing an arbitrary up/down status. -type Healthcheck interface { - Check() - Error() error - Healthy() - Unhealthy(error) -} - -// NewHealthcheck constructs a new Healthcheck which will use the given -// function to update its status. -func NewHealthcheck(f func(Healthcheck)) Healthcheck { - if UseNilMetrics { - return NilHealthcheck{} - } - return &StandardHealthcheck{nil, f} -} - -// NilHealthcheck is a no-op. -type NilHealthcheck struct{} - -// Check is a no-op. -func (NilHealthcheck) Check() {} - -// Error is a no-op. -func (NilHealthcheck) Error() error { return nil } - -// Healthy is a no-op. -func (NilHealthcheck) Healthy() {} - -// Unhealthy is a no-op. -func (NilHealthcheck) Unhealthy(error) {} - -// StandardHealthcheck is the standard implementation of a Healthcheck and -// stores the status and a function to call to update the status. -type StandardHealthcheck struct { - err error - f func(Healthcheck) -} - -// Check runs the healthcheck function to update the healthcheck's status. -func (h *StandardHealthcheck) Check() { - h.f(h) -} - -// Error returns the healthcheck's status, which will be nil if it is healthy. -func (h *StandardHealthcheck) Error() error { - return h.err -} - -// Healthy marks the healthcheck as healthy. -func (h *StandardHealthcheck) Healthy() { - h.err = nil -} - -// Unhealthy marks the healthcheck as unhealthy. The error is stored and -// may be retrieved by the Error method. -func (h *StandardHealthcheck) Unhealthy(err error) { - h.err = err -} diff --git a/vendor/github.com/rcrowley/go-metrics/histogram.go b/vendor/github.com/rcrowley/go-metrics/histogram.go deleted file mode 100644 index dbc837fe4d9..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/histogram.go +++ /dev/null @@ -1,202 +0,0 @@ -package metrics - -// Histograms calculate distribution statistics from a series of int64 values. -type Histogram interface { - Clear() - Count() int64 - Max() int64 - Mean() float64 - Min() int64 - Percentile(float64) float64 - Percentiles([]float64) []float64 - Sample() Sample - Snapshot() Histogram - StdDev() float64 - Sum() int64 - Update(int64) - Variance() float64 -} - -// GetOrRegisterHistogram returns an existing Histogram or constructs and -// registers a new StandardHistogram. -func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, func() Histogram { return NewHistogram(s) }).(Histogram) -} - -// NewHistogram constructs a new StandardHistogram from a Sample. -func NewHistogram(s Sample) Histogram { - if UseNilMetrics { - return NilHistogram{} - } - return &StandardHistogram{sample: s} -} - -// NewRegisteredHistogram constructs and registers a new StandardHistogram from -// a Sample. -func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram { - c := NewHistogram(s) - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// HistogramSnapshot is a read-only copy of another Histogram. -type HistogramSnapshot struct { - sample *SampleSnapshot -} - -// Clear panics. -func (*HistogramSnapshot) Clear() { - panic("Clear called on a HistogramSnapshot") -} - -// Count returns the number of samples recorded at the time the snapshot was -// taken. -func (h *HistogramSnapshot) Count() int64 { return h.sample.Count() } - -// Max returns the maximum value in the sample at the time the snapshot was -// taken. -func (h *HistogramSnapshot) Max() int64 { return h.sample.Max() } - -// Mean returns the mean of the values in the sample at the time the snapshot -// was taken. -func (h *HistogramSnapshot) Mean() float64 { return h.sample.Mean() } - -// Min returns the minimum value in the sample at the time the snapshot was -// taken. -func (h *HistogramSnapshot) Min() int64 { return h.sample.Min() } - -// Percentile returns an arbitrary percentile of values in the sample at the -// time the snapshot was taken. -func (h *HistogramSnapshot) Percentile(p float64) float64 { - return h.sample.Percentile(p) -} - -// Percentiles returns a slice of arbitrary percentiles of values in the sample -// at the time the snapshot was taken. -func (h *HistogramSnapshot) Percentiles(ps []float64) []float64 { - return h.sample.Percentiles(ps) -} - -// Sample returns the Sample underlying the histogram. -func (h *HistogramSnapshot) Sample() Sample { return h.sample } - -// Snapshot returns the snapshot. -func (h *HistogramSnapshot) Snapshot() Histogram { return h } - -// StdDev returns the standard deviation of the values in the sample at the -// time the snapshot was taken. -func (h *HistogramSnapshot) StdDev() float64 { return h.sample.StdDev() } - -// Sum returns the sum in the sample at the time the snapshot was taken. -func (h *HistogramSnapshot) Sum() int64 { return h.sample.Sum() } - -// Update panics. -func (*HistogramSnapshot) Update(int64) { - panic("Update called on a HistogramSnapshot") -} - -// Variance returns the variance of inputs at the time the snapshot was taken. -func (h *HistogramSnapshot) Variance() float64 { return h.sample.Variance() } - -// NilHistogram is a no-op Histogram. -type NilHistogram struct{} - -// Clear is a no-op. -func (NilHistogram) Clear() {} - -// Count is a no-op. -func (NilHistogram) Count() int64 { return 0 } - -// Max is a no-op. -func (NilHistogram) Max() int64 { return 0 } - -// Mean is a no-op. -func (NilHistogram) Mean() float64 { return 0.0 } - -// Min is a no-op. -func (NilHistogram) Min() int64 { return 0 } - -// Percentile is a no-op. -func (NilHistogram) Percentile(p float64) float64 { return 0.0 } - -// Percentiles is a no-op. -func (NilHistogram) Percentiles(ps []float64) []float64 { - return make([]float64, len(ps)) -} - -// Sample is a no-op. -func (NilHistogram) Sample() Sample { return NilSample{} } - -// Snapshot is a no-op. -func (NilHistogram) Snapshot() Histogram { return NilHistogram{} } - -// StdDev is a no-op. -func (NilHistogram) StdDev() float64 { return 0.0 } - -// Sum is a no-op. -func (NilHistogram) Sum() int64 { return 0 } - -// Update is a no-op. -func (NilHistogram) Update(v int64) {} - -// Variance is a no-op. -func (NilHistogram) Variance() float64 { return 0.0 } - -// StandardHistogram is the standard implementation of a Histogram and uses a -// Sample to bound its memory use. -type StandardHistogram struct { - sample Sample -} - -// Clear clears the histogram and its sample. -func (h *StandardHistogram) Clear() { h.sample.Clear() } - -// Count returns the number of samples recorded since the histogram was last -// cleared. -func (h *StandardHistogram) Count() int64 { return h.sample.Count() } - -// Max returns the maximum value in the sample. -func (h *StandardHistogram) Max() int64 { return h.sample.Max() } - -// Mean returns the mean of the values in the sample. -func (h *StandardHistogram) Mean() float64 { return h.sample.Mean() } - -// Min returns the minimum value in the sample. -func (h *StandardHistogram) Min() int64 { return h.sample.Min() } - -// Percentile returns an arbitrary percentile of the values in the sample. -func (h *StandardHistogram) Percentile(p float64) float64 { - return h.sample.Percentile(p) -} - -// Percentiles returns a slice of arbitrary percentiles of the values in the -// sample. -func (h *StandardHistogram) Percentiles(ps []float64) []float64 { - return h.sample.Percentiles(ps) -} - -// Sample returns the Sample underlying the histogram. -func (h *StandardHistogram) Sample() Sample { return h.sample } - -// Snapshot returns a read-only copy of the histogram. -func (h *StandardHistogram) Snapshot() Histogram { - return &HistogramSnapshot{sample: h.sample.Snapshot().(*SampleSnapshot)} -} - -// StdDev returns the standard deviation of the values in the sample. -func (h *StandardHistogram) StdDev() float64 { return h.sample.StdDev() } - -// Sum returns the sum in the sample. -func (h *StandardHistogram) Sum() int64 { return h.sample.Sum() } - -// Update samples a new value. -func (h *StandardHistogram) Update(v int64) { h.sample.Update(v) } - -// Variance returns the variance of the values in the sample. -func (h *StandardHistogram) Variance() float64 { return h.sample.Variance() } diff --git a/vendor/github.com/rcrowley/go-metrics/json.go b/vendor/github.com/rcrowley/go-metrics/json.go deleted file mode 100644 index 174b9477e92..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/json.go +++ /dev/null @@ -1,31 +0,0 @@ -package metrics - -import ( - "encoding/json" - "io" - "time" -) - -// MarshalJSON returns a byte slice containing a JSON representation of all -// the metrics in the Registry. -func (r *StandardRegistry) MarshalJSON() ([]byte, error) { - return json.Marshal(r.GetAll()) -} - -// WriteJSON writes metrics from the given registry periodically to the -// specified io.Writer as JSON. -func WriteJSON(r Registry, d time.Duration, w io.Writer) { - for _ = range time.Tick(d) { - WriteJSONOnce(r, w) - } -} - -// WriteJSONOnce writes metrics from the given registry to the specified -// io.Writer as JSON. -func WriteJSONOnce(r Registry, w io.Writer) { - json.NewEncoder(w).Encode(r) -} - -func (p *PrefixedRegistry) MarshalJSON() ([]byte, error) { - return json.Marshal(p.GetAll()) -} diff --git a/vendor/github.com/rcrowley/go-metrics/log.go b/vendor/github.com/rcrowley/go-metrics/log.go deleted file mode 100644 index f8074c04576..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/log.go +++ /dev/null @@ -1,80 +0,0 @@ -package metrics - -import ( - "time" -) - -type Logger interface { - Printf(format string, v ...interface{}) -} - -func Log(r Registry, freq time.Duration, l Logger) { - LogScaled(r, freq, time.Nanosecond, l) -} - -// Output each metric in the given registry periodically using the given -// logger. Print timings in `scale` units (eg time.Millisecond) rather than nanos. -func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { - du := float64(scale) - duSuffix := scale.String()[1:] - - for _ = range time.Tick(freq) { - r.Each(func(name string, i interface{}) { - switch metric := i.(type) { - case Counter: - l.Printf("counter %s\n", name) - l.Printf(" count: %9d\n", metric.Count()) - case Gauge: - l.Printf("gauge %s\n", name) - l.Printf(" value: %9d\n", metric.Value()) - case GaugeFloat64: - l.Printf("gauge %s\n", name) - l.Printf(" value: %f\n", metric.Value()) - case Healthcheck: - metric.Check() - l.Printf("healthcheck %s\n", name) - l.Printf(" error: %v\n", metric.Error()) - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - l.Printf("histogram %s\n", name) - l.Printf(" count: %9d\n", h.Count()) - l.Printf(" min: %9d\n", h.Min()) - l.Printf(" max: %9d\n", h.Max()) - l.Printf(" mean: %12.2f\n", h.Mean()) - l.Printf(" stddev: %12.2f\n", h.StdDev()) - l.Printf(" median: %12.2f\n", ps[0]) - l.Printf(" 75%%: %12.2f\n", ps[1]) - l.Printf(" 95%%: %12.2f\n", ps[2]) - l.Printf(" 99%%: %12.2f\n", ps[3]) - l.Printf(" 99.9%%: %12.2f\n", ps[4]) - case Meter: - m := metric.Snapshot() - l.Printf("meter %s\n", name) - l.Printf(" count: %9d\n", m.Count()) - l.Printf(" 1-min rate: %12.2f\n", m.Rate1()) - l.Printf(" 5-min rate: %12.2f\n", m.Rate5()) - l.Printf(" 15-min rate: %12.2f\n", m.Rate15()) - l.Printf(" mean rate: %12.2f\n", m.RateMean()) - case Timer: - t := metric.Snapshot() - ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - l.Printf("timer %s\n", name) - l.Printf(" count: %9d\n", t.Count()) - l.Printf(" min: %12.2f%s\n", float64(t.Min())/du, duSuffix) - l.Printf(" max: %12.2f%s\n", float64(t.Max())/du, duSuffix) - l.Printf(" mean: %12.2f%s\n", t.Mean()/du, duSuffix) - l.Printf(" stddev: %12.2f%s\n", t.StdDev()/du, duSuffix) - l.Printf(" median: %12.2f%s\n", ps[0]/du, duSuffix) - l.Printf(" 75%%: %12.2f%s\n", ps[1]/du, duSuffix) - l.Printf(" 95%%: %12.2f%s\n", ps[2]/du, duSuffix) - l.Printf(" 99%%: %12.2f%s\n", ps[3]/du, duSuffix) - l.Printf(" 99.9%%: %12.2f%s\n", ps[4]/du, duSuffix) - l.Printf(" 1-min rate: %12.2f\n", t.Rate1()) - l.Printf(" 5-min rate: %12.2f\n", t.Rate5()) - l.Printf(" 15-min rate: %12.2f\n", t.Rate15()) - l.Printf(" mean rate: %12.2f\n", t.RateMean()) - } - }) - } -} diff --git a/vendor/github.com/rcrowley/go-metrics/meter.go b/vendor/github.com/rcrowley/go-metrics/meter.go deleted file mode 100644 index 7807406a3be..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/meter.go +++ /dev/null @@ -1,257 +0,0 @@ -package metrics - -import ( - "math" - "sync" - "sync/atomic" - "time" -) - -// Meters count events to produce exponentially-weighted moving average rates -// at one-, five-, and fifteen-minutes and a mean rate. -type Meter interface { - Count() int64 - Mark(int64) - Rate1() float64 - Rate5() float64 - Rate15() float64 - RateMean() float64 - Snapshot() Meter - Stop() -} - -// GetOrRegisterMeter returns an existing Meter or constructs and registers a -// new StandardMeter. -// Be sure to unregister the meter from the registry once it is of no use to -// allow for garbage collection. -func GetOrRegisterMeter(name string, r Registry) Meter { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewMeter).(Meter) -} - -// NewMeter constructs a new StandardMeter and launches a goroutine. -// Be sure to call Stop() once the meter is of no use to allow for garbage collection. -func NewMeter() Meter { - if UseNilMetrics { - return NilMeter{} - } - m := newStandardMeter() - arbiter.Lock() - defer arbiter.Unlock() - arbiter.meters[m] = struct{}{} - if !arbiter.started { - arbiter.started = true - go arbiter.tick() - } - return m -} - -// NewMeter constructs and registers a new StandardMeter and launches a -// goroutine. -// Be sure to unregister the meter from the registry once it is of no use to -// allow for garbage collection. -func NewRegisteredMeter(name string, r Registry) Meter { - c := NewMeter() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// MeterSnapshot is a read-only copy of another Meter. -type MeterSnapshot struct { - count int64 - rate1, rate5, rate15, rateMean uint64 -} - -// Count returns the count of events at the time the snapshot was taken. -func (m *MeterSnapshot) Count() int64 { return m.count } - -// Mark panics. -func (*MeterSnapshot) Mark(n int64) { - panic("Mark called on a MeterSnapshot") -} - -// Rate1 returns the one-minute moving average rate of events per second at the -// time the snapshot was taken. -func (m *MeterSnapshot) Rate1() float64 { return math.Float64frombits(m.rate1) } - -// Rate5 returns the five-minute moving average rate of events per second at -// the time the snapshot was taken. -func (m *MeterSnapshot) Rate5() float64 { return math.Float64frombits(m.rate5) } - -// Rate15 returns the fifteen-minute moving average rate of events per second -// at the time the snapshot was taken. -func (m *MeterSnapshot) Rate15() float64 { return math.Float64frombits(m.rate15) } - -// RateMean returns the meter's mean rate of events per second at the time the -// snapshot was taken. -func (m *MeterSnapshot) RateMean() float64 { return math.Float64frombits(m.rateMean) } - -// Snapshot returns the snapshot. -func (m *MeterSnapshot) Snapshot() Meter { return m } - -// Stop is a no-op. -func (m *MeterSnapshot) Stop() {} - -// NilMeter is a no-op Meter. -type NilMeter struct{} - -// Count is a no-op. -func (NilMeter) Count() int64 { return 0 } - -// Mark is a no-op. -func (NilMeter) Mark(n int64) {} - -// Rate1 is a no-op. -func (NilMeter) Rate1() float64 { return 0.0 } - -// Rate5 is a no-op. -func (NilMeter) Rate5() float64 { return 0.0 } - -// Rate15is a no-op. -func (NilMeter) Rate15() float64 { return 0.0 } - -// RateMean is a no-op. -func (NilMeter) RateMean() float64 { return 0.0 } - -// Snapshot is a no-op. -func (NilMeter) Snapshot() Meter { return NilMeter{} } - -// Stop is a no-op. -func (NilMeter) Stop() {} - -// StandardMeter is the standard implementation of a Meter. -type StandardMeter struct { - // Only used on stop. - lock sync.Mutex - snapshot *MeterSnapshot - a1, a5, a15 EWMA - startTime time.Time - stopped uint32 -} - -func newStandardMeter() *StandardMeter { - return &StandardMeter{ - snapshot: &MeterSnapshot{}, - a1: NewEWMA1(), - a5: NewEWMA5(), - a15: NewEWMA15(), - startTime: time.Now(), - } -} - -// Stop stops the meter, Mark() will be a no-op if you use it after being stopped. -func (m *StandardMeter) Stop() { - m.lock.Lock() - stopped := m.stopped - m.stopped = 1 - m.lock.Unlock() - if stopped != 1 { - arbiter.Lock() - delete(arbiter.meters, m) - arbiter.Unlock() - } -} - -// Count returns the number of events recorded. -func (m *StandardMeter) Count() int64 { - return atomic.LoadInt64(&m.snapshot.count) -} - -// Mark records the occurance of n events. -func (m *StandardMeter) Mark(n int64) { - if atomic.LoadUint32(&m.stopped) == 1 { - return - } - - atomic.AddInt64(&m.snapshot.count, n) - - m.a1.Update(n) - m.a5.Update(n) - m.a15.Update(n) - m.updateSnapshot() -} - -// Rate1 returns the one-minute moving average rate of events per second. -func (m *StandardMeter) Rate1() float64 { - return math.Float64frombits(atomic.LoadUint64(&m.snapshot.rate1)) -} - -// Rate5 returns the five-minute moving average rate of events per second. -func (m *StandardMeter) Rate5() float64 { - return math.Float64frombits(atomic.LoadUint64(&m.snapshot.rate5)) -} - -// Rate15 returns the fifteen-minute moving average rate of events per second. -func (m *StandardMeter) Rate15() float64 { - return math.Float64frombits(atomic.LoadUint64(&m.snapshot.rate15)) -} - -// RateMean returns the meter's mean rate of events per second. -func (m *StandardMeter) RateMean() float64 { - return math.Float64frombits(atomic.LoadUint64(&m.snapshot.rateMean)) -} - -// Snapshot returns a read-only copy of the meter. -func (m *StandardMeter) Snapshot() Meter { - copiedSnapshot := MeterSnapshot{ - count: atomic.LoadInt64(&m.snapshot.count), - rate1: atomic.LoadUint64(&m.snapshot.rate1), - rate5: atomic.LoadUint64(&m.snapshot.rate5), - rate15: atomic.LoadUint64(&m.snapshot.rate15), - rateMean: atomic.LoadUint64(&m.snapshot.rateMean), - } - return &copiedSnapshot -} - -func (m *StandardMeter) updateSnapshot() { - rate1 := math.Float64bits(m.a1.Rate()) - rate5 := math.Float64bits(m.a5.Rate()) - rate15 := math.Float64bits(m.a15.Rate()) - rateMean := math.Float64bits(float64(m.Count()) / time.Since(m.startTime).Seconds()) - - atomic.StoreUint64(&m.snapshot.rate1, rate1) - atomic.StoreUint64(&m.snapshot.rate5, rate5) - atomic.StoreUint64(&m.snapshot.rate15, rate15) - atomic.StoreUint64(&m.snapshot.rateMean, rateMean) -} - -func (m *StandardMeter) tick() { - m.a1.Tick() - m.a5.Tick() - m.a15.Tick() - m.updateSnapshot() -} - -// meterArbiter ticks meters every 5s from a single goroutine. -// meters are references in a set for future stopping. -type meterArbiter struct { - sync.RWMutex - started bool - meters map[*StandardMeter]struct{} - ticker *time.Ticker -} - -var arbiter = meterArbiter{ticker: time.NewTicker(5e9), meters: make(map[*StandardMeter]struct{})} - -// Ticks meters on the scheduled interval -func (ma *meterArbiter) tick() { - for { - select { - case <-ma.ticker.C: - ma.tickMeters() - } - } -} - -func (ma *meterArbiter) tickMeters() { - ma.RLock() - defer ma.RUnlock() - for meter := range ma.meters { - meter.tick() - } -} diff --git a/vendor/github.com/rcrowley/go-metrics/metrics.go b/vendor/github.com/rcrowley/go-metrics/metrics.go deleted file mode 100644 index b97a49ed123..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/metrics.go +++ /dev/null @@ -1,13 +0,0 @@ -// Go port of Coda Hale's Metrics library -// -// -// -// Coda Hale's original work: -package metrics - -// UseNilMetrics is checked by the constructor functions for all of the -// standard metrics. If it is true, the metric returned is a stub. -// -// This global kill-switch helps quantify the observer effect and makes -// for less cluttered pprof profiles. -var UseNilMetrics bool = false diff --git a/vendor/github.com/rcrowley/go-metrics/opentsdb.go b/vendor/github.com/rcrowley/go-metrics/opentsdb.go deleted file mode 100644 index 266b6c93d21..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/opentsdb.go +++ /dev/null @@ -1,119 +0,0 @@ -package metrics - -import ( - "bufio" - "fmt" - "log" - "net" - "os" - "strings" - "time" -) - -var shortHostName string = "" - -// OpenTSDBConfig provides a container with configuration parameters for -// the OpenTSDB exporter -type OpenTSDBConfig struct { - Addr *net.TCPAddr // Network address to connect to - Registry Registry // Registry to be exported - FlushInterval time.Duration // Flush interval - DurationUnit time.Duration // Time conversion unit for durations - Prefix string // Prefix to be prepended to metric names -} - -// OpenTSDB is a blocking exporter function which reports metrics in r -// to a TSDB server located at addr, flushing them every d duration -// and prepending metric names with prefix. -func OpenTSDB(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) { - OpenTSDBWithConfig(OpenTSDBConfig{ - Addr: addr, - Registry: r, - FlushInterval: d, - DurationUnit: time.Nanosecond, - Prefix: prefix, - }) -} - -// OpenTSDBWithConfig is a blocking exporter function just like OpenTSDB, -// but it takes a OpenTSDBConfig instead. -func OpenTSDBWithConfig(c OpenTSDBConfig) { - for _ = range time.Tick(c.FlushInterval) { - if err := openTSDB(&c); nil != err { - log.Println(err) - } - } -} - -func getShortHostname() string { - if shortHostName == "" { - host, _ := os.Hostname() - if index := strings.Index(host, "."); index > 0 { - shortHostName = host[:index] - } else { - shortHostName = host - } - } - return shortHostName -} - -func openTSDB(c *OpenTSDBConfig) error { - shortHostname := getShortHostname() - now := time.Now().Unix() - du := float64(c.DurationUnit) - conn, err := net.DialTCP("tcp", nil, c.Addr) - if nil != err { - return err - } - defer conn.Close() - w := bufio.NewWriter(conn) - c.Registry.Each(func(name string, i interface{}) { - switch metric := i.(type) { - case Counter: - fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, metric.Count(), shortHostname) - case Gauge: - fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname) - case GaugeFloat64: - fmt.Fprintf(w, "put %s.%s.value %d %f host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname) - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, h.Count(), shortHostname) - fmt.Fprintf(w, "put %s.%s.min %d %d host=%s\n", c.Prefix, name, now, h.Min(), shortHostname) - fmt.Fprintf(w, "put %s.%s.max %d %d host=%s\n", c.Prefix, name, now, h.Max(), shortHostname) - fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, h.Mean(), shortHostname) - fmt.Fprintf(w, "put %s.%s.std-dev %d %.2f host=%s\n", c.Prefix, name, now, h.StdDev(), shortHostname) - fmt.Fprintf(w, "put %s.%s.50-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[0], shortHostname) - fmt.Fprintf(w, "put %s.%s.75-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[1], shortHostname) - fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2], shortHostname) - fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3], shortHostname) - fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4], shortHostname) - case Meter: - m := metric.Snapshot() - fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, m.Count(), shortHostname) - fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate1(), shortHostname) - fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate5(), shortHostname) - fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate15(), shortHostname) - fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, m.RateMean(), shortHostname) - case Timer: - t := metric.Snapshot() - ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, t.Count(), shortHostname) - fmt.Fprintf(w, "put %s.%s.min %d %d host=%s\n", c.Prefix, name, now, t.Min()/int64(du), shortHostname) - fmt.Fprintf(w, "put %s.%s.max %d %d host=%s\n", c.Prefix, name, now, t.Max()/int64(du), shortHostname) - fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, t.Mean()/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.std-dev %d %.2f host=%s\n", c.Prefix, name, now, t.StdDev()/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.50-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[0]/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.75-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[1]/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2]/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3]/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4]/du, shortHostname) - fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate1(), shortHostname) - fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate5(), shortHostname) - fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate15(), shortHostname) - fmt.Fprintf(w, "put %s.%s.mean-rate %d %.2f host=%s\n", c.Prefix, name, now, t.RateMean(), shortHostname) - } - w.Flush() - }) - return nil -} diff --git a/vendor/github.com/rcrowley/go-metrics/registry.go b/vendor/github.com/rcrowley/go-metrics/registry.go deleted file mode 100644 index b3bab64e15b..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/registry.go +++ /dev/null @@ -1,363 +0,0 @@ -package metrics - -import ( - "fmt" - "reflect" - "strings" - "sync" -) - -// DuplicateMetric is the error returned by Registry.Register when a metric -// already exists. If you mean to Register that metric you must first -// Unregister the existing metric. -type DuplicateMetric string - -func (err DuplicateMetric) Error() string { - return fmt.Sprintf("duplicate metric: %s", string(err)) -} - -// A Registry holds references to a set of metrics by name and can iterate -// over them, calling callback functions provided by the user. -// -// This is an interface so as to encourage other structs to implement -// the Registry API as appropriate. -type Registry interface { - - // Call the given function for each registered metric. - Each(func(string, interface{})) - - // Get the metric by the given name or nil if none is registered. - Get(string) interface{} - - // GetAll metrics in the Registry. - GetAll() map[string]map[string]interface{} - - // Gets an existing metric or registers the given one. - // The interface can be the metric to register if not found in registry, - // or a function returning the metric for lazy instantiation. - GetOrRegister(string, interface{}) interface{} - - // Register the given metric under the given name. - Register(string, interface{}) error - - // Run all registered healthchecks. - RunHealthchecks() - - // Unregister the metric with the given name. - Unregister(string) - - // Unregister all metrics. (Mostly for testing.) - UnregisterAll() -} - -// The standard implementation of a Registry is a mutex-protected map -// of names to metrics. -type StandardRegistry struct { - metrics map[string]interface{} - mutex sync.RWMutex -} - -// Create a new registry. -func NewRegistry() Registry { - return &StandardRegistry{metrics: make(map[string]interface{})} -} - -// Call the given function for each registered metric. -func (r *StandardRegistry) Each(f func(string, interface{})) { - for name, i := range r.registered() { - f(name, i) - } -} - -// Get the metric by the given name or nil if none is registered. -func (r *StandardRegistry) Get(name string) interface{} { - r.mutex.RLock() - defer r.mutex.RUnlock() - return r.metrics[name] -} - -// Gets an existing metric or creates and registers a new one. Threadsafe -// alternative to calling Get and Register on failure. -// The interface can be the metric to register if not found in registry, -// or a function returning the metric for lazy instantiation. -func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} { - // access the read lock first which should be re-entrant - r.mutex.RLock() - metric, ok := r.metrics[name] - r.mutex.RUnlock() - if ok { - return metric - } - - // only take the write lock if we'll be modifying the metrics map - r.mutex.Lock() - defer r.mutex.Unlock() - if metric, ok := r.metrics[name]; ok { - return metric - } - if v := reflect.ValueOf(i); v.Kind() == reflect.Func { - i = v.Call(nil)[0].Interface() - } - r.register(name, i) - return i -} - -// Register the given metric under the given name. Returns a DuplicateMetric -// if a metric by the given name is already registered. -func (r *StandardRegistry) Register(name string, i interface{}) error { - r.mutex.Lock() - defer r.mutex.Unlock() - return r.register(name, i) -} - -// Run all registered healthchecks. -func (r *StandardRegistry) RunHealthchecks() { - r.mutex.RLock() - defer r.mutex.RUnlock() - for _, i := range r.metrics { - if h, ok := i.(Healthcheck); ok { - h.Check() - } - } -} - -// GetAll metrics in the Registry -func (r *StandardRegistry) GetAll() map[string]map[string]interface{} { - data := make(map[string]map[string]interface{}) - r.Each(func(name string, i interface{}) { - values := make(map[string]interface{}) - switch metric := i.(type) { - case Counter: - values["count"] = metric.Count() - case Gauge: - values["value"] = metric.Value() - case GaugeFloat64: - values["value"] = metric.Value() - case Healthcheck: - values["error"] = nil - metric.Check() - if err := metric.Error(); nil != err { - values["error"] = metric.Error().Error() - } - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - values["count"] = h.Count() - values["min"] = h.Min() - values["max"] = h.Max() - values["mean"] = h.Mean() - values["stddev"] = h.StdDev() - values["median"] = ps[0] - values["75%"] = ps[1] - values["95%"] = ps[2] - values["99%"] = ps[3] - values["99.9%"] = ps[4] - case Meter: - m := metric.Snapshot() - values["count"] = m.Count() - values["1m.rate"] = m.Rate1() - values["5m.rate"] = m.Rate5() - values["15m.rate"] = m.Rate15() - values["mean.rate"] = m.RateMean() - case Timer: - t := metric.Snapshot() - ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - values["count"] = t.Count() - values["min"] = t.Min() - values["max"] = t.Max() - values["mean"] = t.Mean() - values["stddev"] = t.StdDev() - values["median"] = ps[0] - values["75%"] = ps[1] - values["95%"] = ps[2] - values["99%"] = ps[3] - values["99.9%"] = ps[4] - values["1m.rate"] = t.Rate1() - values["5m.rate"] = t.Rate5() - values["15m.rate"] = t.Rate15() - values["mean.rate"] = t.RateMean() - } - data[name] = values - }) - return data -} - -// Unregister the metric with the given name. -func (r *StandardRegistry) Unregister(name string) { - r.mutex.Lock() - defer r.mutex.Unlock() - r.stop(name) - delete(r.metrics, name) -} - -// Unregister all metrics. (Mostly for testing.) -func (r *StandardRegistry) UnregisterAll() { - r.mutex.Lock() - defer r.mutex.Unlock() - for name, _ := range r.metrics { - r.stop(name) - delete(r.metrics, name) - } -} - -func (r *StandardRegistry) register(name string, i interface{}) error { - if _, ok := r.metrics[name]; ok { - return DuplicateMetric(name) - } - switch i.(type) { - case Counter, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer: - r.metrics[name] = i - } - return nil -} - -func (r *StandardRegistry) registered() map[string]interface{} { - r.mutex.Lock() - defer r.mutex.Unlock() - metrics := make(map[string]interface{}, len(r.metrics)) - for name, i := range r.metrics { - metrics[name] = i - } - return metrics -} - -func (r *StandardRegistry) stop(name string) { - if i, ok := r.metrics[name]; ok { - if s, ok := i.(Stoppable); ok { - s.Stop() - } - } -} - -// Stoppable defines the metrics which has to be stopped. -type Stoppable interface { - Stop() -} - -type PrefixedRegistry struct { - underlying Registry - prefix string -} - -func NewPrefixedRegistry(prefix string) Registry { - return &PrefixedRegistry{ - underlying: NewRegistry(), - prefix: prefix, - } -} - -func NewPrefixedChildRegistry(parent Registry, prefix string) Registry { - return &PrefixedRegistry{ - underlying: parent, - prefix: prefix, - } -} - -// Call the given function for each registered metric. -func (r *PrefixedRegistry) Each(fn func(string, interface{})) { - wrappedFn := func(prefix string) func(string, interface{}) { - return func(name string, iface interface{}) { - if strings.HasPrefix(name, prefix) { - fn(name, iface) - } else { - return - } - } - } - - baseRegistry, prefix := findPrefix(r, "") - baseRegistry.Each(wrappedFn(prefix)) -} - -func findPrefix(registry Registry, prefix string) (Registry, string) { - switch r := registry.(type) { - case *PrefixedRegistry: - return findPrefix(r.underlying, r.prefix+prefix) - case *StandardRegistry: - return r, prefix - } - return nil, "" -} - -// Get the metric by the given name or nil if none is registered. -func (r *PrefixedRegistry) Get(name string) interface{} { - realName := r.prefix + name - return r.underlying.Get(realName) -} - -// Gets an existing metric or registers the given one. -// The interface can be the metric to register if not found in registry, -// or a function returning the metric for lazy instantiation. -func (r *PrefixedRegistry) GetOrRegister(name string, metric interface{}) interface{} { - realName := r.prefix + name - return r.underlying.GetOrRegister(realName, metric) -} - -// Register the given metric under the given name. The name will be prefixed. -func (r *PrefixedRegistry) Register(name string, metric interface{}) error { - realName := r.prefix + name - return r.underlying.Register(realName, metric) -} - -// Run all registered healthchecks. -func (r *PrefixedRegistry) RunHealthchecks() { - r.underlying.RunHealthchecks() -} - -// GetAll metrics in the Registry -func (r *PrefixedRegistry) GetAll() map[string]map[string]interface{} { - return r.underlying.GetAll() -} - -// Unregister the metric with the given name. The name will be prefixed. -func (r *PrefixedRegistry) Unregister(name string) { - realName := r.prefix + name - r.underlying.Unregister(realName) -} - -// Unregister all metrics. (Mostly for testing.) -func (r *PrefixedRegistry) UnregisterAll() { - r.underlying.UnregisterAll() -} - -var DefaultRegistry Registry = NewRegistry() - -// Call the given function for each registered metric. -func Each(f func(string, interface{})) { - DefaultRegistry.Each(f) -} - -// Get the metric by the given name or nil if none is registered. -func Get(name string) interface{} { - return DefaultRegistry.Get(name) -} - -// Gets an existing metric or creates and registers a new one. Threadsafe -// alternative to calling Get and Register on failure. -func GetOrRegister(name string, i interface{}) interface{} { - return DefaultRegistry.GetOrRegister(name, i) -} - -// Register the given metric under the given name. Returns a DuplicateMetric -// if a metric by the given name is already registered. -func Register(name string, i interface{}) error { - return DefaultRegistry.Register(name, i) -} - -// Register the given metric under the given name. Panics if a metric by the -// given name is already registered. -func MustRegister(name string, i interface{}) { - if err := Register(name, i); err != nil { - panic(err) - } -} - -// Run all registered healthchecks. -func RunHealthchecks() { - DefaultRegistry.RunHealthchecks() -} - -// Unregister the metric with the given name. -func Unregister(name string) { - DefaultRegistry.Unregister(name) -} diff --git a/vendor/github.com/rcrowley/go-metrics/runtime.go b/vendor/github.com/rcrowley/go-metrics/runtime.go deleted file mode 100644 index 11c6b785a0f..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/runtime.go +++ /dev/null @@ -1,212 +0,0 @@ -package metrics - -import ( - "runtime" - "runtime/pprof" - "time" -) - -var ( - memStats runtime.MemStats - runtimeMetrics struct { - MemStats struct { - Alloc Gauge - BuckHashSys Gauge - DebugGC Gauge - EnableGC Gauge - Frees Gauge - HeapAlloc Gauge - HeapIdle Gauge - HeapInuse Gauge - HeapObjects Gauge - HeapReleased Gauge - HeapSys Gauge - LastGC Gauge - Lookups Gauge - Mallocs Gauge - MCacheInuse Gauge - MCacheSys Gauge - MSpanInuse Gauge - MSpanSys Gauge - NextGC Gauge - NumGC Gauge - GCCPUFraction GaugeFloat64 - PauseNs Histogram - PauseTotalNs Gauge - StackInuse Gauge - StackSys Gauge - Sys Gauge - TotalAlloc Gauge - } - NumCgoCall Gauge - NumGoroutine Gauge - NumThread Gauge - ReadMemStats Timer - } - frees uint64 - lookups uint64 - mallocs uint64 - numGC uint32 - numCgoCalls int64 - - threadCreateProfile = pprof.Lookup("threadcreate") -) - -// Capture new values for the Go runtime statistics exported in -// runtime.MemStats. This is designed to be called as a goroutine. -func CaptureRuntimeMemStats(r Registry, d time.Duration) { - for _ = range time.Tick(d) { - CaptureRuntimeMemStatsOnce(r) - } -} - -// Capture new values for the Go runtime statistics exported in -// runtime.MemStats. This is designed to be called in a background -// goroutine. Giving a registry which has not been given to -// RegisterRuntimeMemStats will panic. -// -// Be very careful with this because runtime.ReadMemStats calls the C -// functions runtime·semacquire(&runtime·worldsema) and runtime·stoptheworld() -// and that last one does what it says on the tin. -func CaptureRuntimeMemStatsOnce(r Registry) { - t := time.Now() - runtime.ReadMemStats(&memStats) // This takes 50-200us. - runtimeMetrics.ReadMemStats.UpdateSince(t) - - runtimeMetrics.MemStats.Alloc.Update(int64(memStats.Alloc)) - runtimeMetrics.MemStats.BuckHashSys.Update(int64(memStats.BuckHashSys)) - if memStats.DebugGC { - runtimeMetrics.MemStats.DebugGC.Update(1) - } else { - runtimeMetrics.MemStats.DebugGC.Update(0) - } - if memStats.EnableGC { - runtimeMetrics.MemStats.EnableGC.Update(1) - } else { - runtimeMetrics.MemStats.EnableGC.Update(0) - } - - runtimeMetrics.MemStats.Frees.Update(int64(memStats.Frees - frees)) - runtimeMetrics.MemStats.HeapAlloc.Update(int64(memStats.HeapAlloc)) - runtimeMetrics.MemStats.HeapIdle.Update(int64(memStats.HeapIdle)) - runtimeMetrics.MemStats.HeapInuse.Update(int64(memStats.HeapInuse)) - runtimeMetrics.MemStats.HeapObjects.Update(int64(memStats.HeapObjects)) - runtimeMetrics.MemStats.HeapReleased.Update(int64(memStats.HeapReleased)) - runtimeMetrics.MemStats.HeapSys.Update(int64(memStats.HeapSys)) - runtimeMetrics.MemStats.LastGC.Update(int64(memStats.LastGC)) - runtimeMetrics.MemStats.Lookups.Update(int64(memStats.Lookups - lookups)) - runtimeMetrics.MemStats.Mallocs.Update(int64(memStats.Mallocs - mallocs)) - runtimeMetrics.MemStats.MCacheInuse.Update(int64(memStats.MCacheInuse)) - runtimeMetrics.MemStats.MCacheSys.Update(int64(memStats.MCacheSys)) - runtimeMetrics.MemStats.MSpanInuse.Update(int64(memStats.MSpanInuse)) - runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys)) - runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC)) - runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC)) - runtimeMetrics.MemStats.GCCPUFraction.Update(gcCPUFraction(&memStats)) - - // - i := numGC % uint32(len(memStats.PauseNs)) - ii := memStats.NumGC % uint32(len(memStats.PauseNs)) - if memStats.NumGC-numGC >= uint32(len(memStats.PauseNs)) { - for i = 0; i < uint32(len(memStats.PauseNs)); i++ { - runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) - } - } else { - if i > ii { - for ; i < uint32(len(memStats.PauseNs)); i++ { - runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) - } - i = 0 - } - for ; i < ii; i++ { - runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) - } - } - frees = memStats.Frees - lookups = memStats.Lookups - mallocs = memStats.Mallocs - numGC = memStats.NumGC - - runtimeMetrics.MemStats.PauseTotalNs.Update(int64(memStats.PauseTotalNs)) - runtimeMetrics.MemStats.StackInuse.Update(int64(memStats.StackInuse)) - runtimeMetrics.MemStats.StackSys.Update(int64(memStats.StackSys)) - runtimeMetrics.MemStats.Sys.Update(int64(memStats.Sys)) - runtimeMetrics.MemStats.TotalAlloc.Update(int64(memStats.TotalAlloc)) - - currentNumCgoCalls := numCgoCall() - runtimeMetrics.NumCgoCall.Update(currentNumCgoCalls - numCgoCalls) - numCgoCalls = currentNumCgoCalls - - runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine())) - - runtimeMetrics.NumThread.Update(int64(threadCreateProfile.Count())) -} - -// Register runtimeMetrics for the Go runtime statistics exported in runtime and -// specifically runtime.MemStats. The runtimeMetrics are named by their -// fully-qualified Go symbols, i.e. runtime.MemStats.Alloc. -func RegisterRuntimeMemStats(r Registry) { - runtimeMetrics.MemStats.Alloc = NewGauge() - runtimeMetrics.MemStats.BuckHashSys = NewGauge() - runtimeMetrics.MemStats.DebugGC = NewGauge() - runtimeMetrics.MemStats.EnableGC = NewGauge() - runtimeMetrics.MemStats.Frees = NewGauge() - runtimeMetrics.MemStats.HeapAlloc = NewGauge() - runtimeMetrics.MemStats.HeapIdle = NewGauge() - runtimeMetrics.MemStats.HeapInuse = NewGauge() - runtimeMetrics.MemStats.HeapObjects = NewGauge() - runtimeMetrics.MemStats.HeapReleased = NewGauge() - runtimeMetrics.MemStats.HeapSys = NewGauge() - runtimeMetrics.MemStats.LastGC = NewGauge() - runtimeMetrics.MemStats.Lookups = NewGauge() - runtimeMetrics.MemStats.Mallocs = NewGauge() - runtimeMetrics.MemStats.MCacheInuse = NewGauge() - runtimeMetrics.MemStats.MCacheSys = NewGauge() - runtimeMetrics.MemStats.MSpanInuse = NewGauge() - runtimeMetrics.MemStats.MSpanSys = NewGauge() - runtimeMetrics.MemStats.NextGC = NewGauge() - runtimeMetrics.MemStats.NumGC = NewGauge() - runtimeMetrics.MemStats.GCCPUFraction = NewGaugeFloat64() - runtimeMetrics.MemStats.PauseNs = NewHistogram(NewExpDecaySample(1028, 0.015)) - runtimeMetrics.MemStats.PauseTotalNs = NewGauge() - runtimeMetrics.MemStats.StackInuse = NewGauge() - runtimeMetrics.MemStats.StackSys = NewGauge() - runtimeMetrics.MemStats.Sys = NewGauge() - runtimeMetrics.MemStats.TotalAlloc = NewGauge() - runtimeMetrics.NumCgoCall = NewGauge() - runtimeMetrics.NumGoroutine = NewGauge() - runtimeMetrics.NumThread = NewGauge() - runtimeMetrics.ReadMemStats = NewTimer() - - r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc) - r.Register("runtime.MemStats.BuckHashSys", runtimeMetrics.MemStats.BuckHashSys) - r.Register("runtime.MemStats.DebugGC", runtimeMetrics.MemStats.DebugGC) - r.Register("runtime.MemStats.EnableGC", runtimeMetrics.MemStats.EnableGC) - r.Register("runtime.MemStats.Frees", runtimeMetrics.MemStats.Frees) - r.Register("runtime.MemStats.HeapAlloc", runtimeMetrics.MemStats.HeapAlloc) - r.Register("runtime.MemStats.HeapIdle", runtimeMetrics.MemStats.HeapIdle) - r.Register("runtime.MemStats.HeapInuse", runtimeMetrics.MemStats.HeapInuse) - r.Register("runtime.MemStats.HeapObjects", runtimeMetrics.MemStats.HeapObjects) - r.Register("runtime.MemStats.HeapReleased", runtimeMetrics.MemStats.HeapReleased) - r.Register("runtime.MemStats.HeapSys", runtimeMetrics.MemStats.HeapSys) - r.Register("runtime.MemStats.LastGC", runtimeMetrics.MemStats.LastGC) - r.Register("runtime.MemStats.Lookups", runtimeMetrics.MemStats.Lookups) - r.Register("runtime.MemStats.Mallocs", runtimeMetrics.MemStats.Mallocs) - r.Register("runtime.MemStats.MCacheInuse", runtimeMetrics.MemStats.MCacheInuse) - r.Register("runtime.MemStats.MCacheSys", runtimeMetrics.MemStats.MCacheSys) - r.Register("runtime.MemStats.MSpanInuse", runtimeMetrics.MemStats.MSpanInuse) - r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys) - r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC) - r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC) - r.Register("runtime.MemStats.GCCPUFraction", runtimeMetrics.MemStats.GCCPUFraction) - r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs) - r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs) - r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse) - r.Register("runtime.MemStats.StackSys", runtimeMetrics.MemStats.StackSys) - r.Register("runtime.MemStats.Sys", runtimeMetrics.MemStats.Sys) - r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc) - r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall) - r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine) - r.Register("runtime.NumThread", runtimeMetrics.NumThread) - r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats) -} diff --git a/vendor/github.com/rcrowley/go-metrics/runtime_cgo.go b/vendor/github.com/rcrowley/go-metrics/runtime_cgo.go deleted file mode 100644 index e3391f4e89f..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/runtime_cgo.go +++ /dev/null @@ -1,10 +0,0 @@ -// +build cgo -// +build !appengine - -package metrics - -import "runtime" - -func numCgoCall() int64 { - return runtime.NumCgoCall() -} diff --git a/vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go b/vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go deleted file mode 100644 index ca12c05bac7..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build go1.5 - -package metrics - -import "runtime" - -func gcCPUFraction(memStats *runtime.MemStats) float64 { - return memStats.GCCPUFraction -} diff --git a/vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go b/vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go deleted file mode 100644 index 616a3b4751b..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go +++ /dev/null @@ -1,7 +0,0 @@ -// +build !cgo appengine - -package metrics - -func numCgoCall() int64 { - return 0 -} diff --git a/vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go b/vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go deleted file mode 100644 index be96aa6f1be..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go +++ /dev/null @@ -1,9 +0,0 @@ -// +build !go1.5 - -package metrics - -import "runtime" - -func gcCPUFraction(memStats *runtime.MemStats) float64 { - return 0 -} diff --git a/vendor/github.com/rcrowley/go-metrics/sample.go b/vendor/github.com/rcrowley/go-metrics/sample.go deleted file mode 100644 index fecee5ef68b..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/sample.go +++ /dev/null @@ -1,616 +0,0 @@ -package metrics - -import ( - "math" - "math/rand" - "sort" - "sync" - "time" -) - -const rescaleThreshold = time.Hour - -// Samples maintain a statistically-significant selection of values from -// a stream. -type Sample interface { - Clear() - Count() int64 - Max() int64 - Mean() float64 - Min() int64 - Percentile(float64) float64 - Percentiles([]float64) []float64 - Size() int - Snapshot() Sample - StdDev() float64 - Sum() int64 - Update(int64) - Values() []int64 - Variance() float64 -} - -// ExpDecaySample is an exponentially-decaying sample using a forward-decaying -// priority reservoir. See Cormode et al's "Forward Decay: A Practical Time -// Decay Model for Streaming Systems". -// -// -type ExpDecaySample struct { - alpha float64 - count int64 - mutex sync.Mutex - reservoirSize int - t0, t1 time.Time - values *expDecaySampleHeap -} - -// NewExpDecaySample constructs a new exponentially-decaying sample with the -// given reservoir size and alpha. -func NewExpDecaySample(reservoirSize int, alpha float64) Sample { - if UseNilMetrics { - return NilSample{} - } - s := &ExpDecaySample{ - alpha: alpha, - reservoirSize: reservoirSize, - t0: time.Now(), - values: newExpDecaySampleHeap(reservoirSize), - } - s.t1 = s.t0.Add(rescaleThreshold) - return s -} - -// Clear clears all samples. -func (s *ExpDecaySample) Clear() { - s.mutex.Lock() - defer s.mutex.Unlock() - s.count = 0 - s.t0 = time.Now() - s.t1 = s.t0.Add(rescaleThreshold) - s.values.Clear() -} - -// Count returns the number of samples recorded, which may exceed the -// reservoir size. -func (s *ExpDecaySample) Count() int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return s.count -} - -// Max returns the maximum value in the sample, which may not be the maximum -// value ever to be part of the sample. -func (s *ExpDecaySample) Max() int64 { - return SampleMax(s.Values()) -} - -// Mean returns the mean of the values in the sample. -func (s *ExpDecaySample) Mean() float64 { - return SampleMean(s.Values()) -} - -// Min returns the minimum value in the sample, which may not be the minimum -// value ever to be part of the sample. -func (s *ExpDecaySample) Min() int64 { - return SampleMin(s.Values()) -} - -// Percentile returns an arbitrary percentile of values in the sample. -func (s *ExpDecaySample) Percentile(p float64) float64 { - return SamplePercentile(s.Values(), p) -} - -// Percentiles returns a slice of arbitrary percentiles of values in the -// sample. -func (s *ExpDecaySample) Percentiles(ps []float64) []float64 { - return SamplePercentiles(s.Values(), ps) -} - -// Size returns the size of the sample, which is at most the reservoir size. -func (s *ExpDecaySample) Size() int { - s.mutex.Lock() - defer s.mutex.Unlock() - return s.values.Size() -} - -// Snapshot returns a read-only copy of the sample. -func (s *ExpDecaySample) Snapshot() Sample { - s.mutex.Lock() - defer s.mutex.Unlock() - vals := s.values.Values() - values := make([]int64, len(vals)) - for i, v := range vals { - values[i] = v.v - } - return &SampleSnapshot{ - count: s.count, - values: values, - } -} - -// StdDev returns the standard deviation of the values in the sample. -func (s *ExpDecaySample) StdDev() float64 { - return SampleStdDev(s.Values()) -} - -// Sum returns the sum of the values in the sample. -func (s *ExpDecaySample) Sum() int64 { - return SampleSum(s.Values()) -} - -// Update samples a new value. -func (s *ExpDecaySample) Update(v int64) { - s.update(time.Now(), v) -} - -// Values returns a copy of the values in the sample. -func (s *ExpDecaySample) Values() []int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - vals := s.values.Values() - values := make([]int64, len(vals)) - for i, v := range vals { - values[i] = v.v - } - return values -} - -// Variance returns the variance of the values in the sample. -func (s *ExpDecaySample) Variance() float64 { - return SampleVariance(s.Values()) -} - -// update samples a new value at a particular timestamp. This is a method all -// its own to facilitate testing. -func (s *ExpDecaySample) update(t time.Time, v int64) { - s.mutex.Lock() - defer s.mutex.Unlock() - s.count++ - if s.values.Size() == s.reservoirSize { - s.values.Pop() - } - s.values.Push(expDecaySample{ - k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / rand.Float64(), - v: v, - }) - if t.After(s.t1) { - values := s.values.Values() - t0 := s.t0 - s.values.Clear() - s.t0 = t - s.t1 = s.t0.Add(rescaleThreshold) - for _, v := range values { - v.k = v.k * math.Exp(-s.alpha*s.t0.Sub(t0).Seconds()) - s.values.Push(v) - } - } -} - -// NilSample is a no-op Sample. -type NilSample struct{} - -// Clear is a no-op. -func (NilSample) Clear() {} - -// Count is a no-op. -func (NilSample) Count() int64 { return 0 } - -// Max is a no-op. -func (NilSample) Max() int64 { return 0 } - -// Mean is a no-op. -func (NilSample) Mean() float64 { return 0.0 } - -// Min is a no-op. -func (NilSample) Min() int64 { return 0 } - -// Percentile is a no-op. -func (NilSample) Percentile(p float64) float64 { return 0.0 } - -// Percentiles is a no-op. -func (NilSample) Percentiles(ps []float64) []float64 { - return make([]float64, len(ps)) -} - -// Size is a no-op. -func (NilSample) Size() int { return 0 } - -// Sample is a no-op. -func (NilSample) Snapshot() Sample { return NilSample{} } - -// StdDev is a no-op. -func (NilSample) StdDev() float64 { return 0.0 } - -// Sum is a no-op. -func (NilSample) Sum() int64 { return 0 } - -// Update is a no-op. -func (NilSample) Update(v int64) {} - -// Values is a no-op. -func (NilSample) Values() []int64 { return []int64{} } - -// Variance is a no-op. -func (NilSample) Variance() float64 { return 0.0 } - -// SampleMax returns the maximum value of the slice of int64. -func SampleMax(values []int64) int64 { - if 0 == len(values) { - return 0 - } - var max int64 = math.MinInt64 - for _, v := range values { - if max < v { - max = v - } - } - return max -} - -// SampleMean returns the mean value of the slice of int64. -func SampleMean(values []int64) float64 { - if 0 == len(values) { - return 0.0 - } - return float64(SampleSum(values)) / float64(len(values)) -} - -// SampleMin returns the minimum value of the slice of int64. -func SampleMin(values []int64) int64 { - if 0 == len(values) { - return 0 - } - var min int64 = math.MaxInt64 - for _, v := range values { - if min > v { - min = v - } - } - return min -} - -// SamplePercentiles returns an arbitrary percentile of the slice of int64. -func SamplePercentile(values int64Slice, p float64) float64 { - return SamplePercentiles(values, []float64{p})[0] -} - -// SamplePercentiles returns a slice of arbitrary percentiles of the slice of -// int64. -func SamplePercentiles(values int64Slice, ps []float64) []float64 { - scores := make([]float64, len(ps)) - size := len(values) - if size > 0 { - sort.Sort(values) - for i, p := range ps { - pos := p * float64(size+1) - if pos < 1.0 { - scores[i] = float64(values[0]) - } else if pos >= float64(size) { - scores[i] = float64(values[size-1]) - } else { - lower := float64(values[int(pos)-1]) - upper := float64(values[int(pos)]) - scores[i] = lower + (pos-math.Floor(pos))*(upper-lower) - } - } - } - return scores -} - -// SampleSnapshot is a read-only copy of another Sample. -type SampleSnapshot struct { - count int64 - values []int64 -} - -func NewSampleSnapshot(count int64, values []int64) *SampleSnapshot { - return &SampleSnapshot{ - count: count, - values: values, - } -} - -// Clear panics. -func (*SampleSnapshot) Clear() { - panic("Clear called on a SampleSnapshot") -} - -// Count returns the count of inputs at the time the snapshot was taken. -func (s *SampleSnapshot) Count() int64 { return s.count } - -// Max returns the maximal value at the time the snapshot was taken. -func (s *SampleSnapshot) Max() int64 { return SampleMax(s.values) } - -// Mean returns the mean value at the time the snapshot was taken. -func (s *SampleSnapshot) Mean() float64 { return SampleMean(s.values) } - -// Min returns the minimal value at the time the snapshot was taken. -func (s *SampleSnapshot) Min() int64 { return SampleMin(s.values) } - -// Percentile returns an arbitrary percentile of values at the time the -// snapshot was taken. -func (s *SampleSnapshot) Percentile(p float64) float64 { - return SamplePercentile(s.values, p) -} - -// Percentiles returns a slice of arbitrary percentiles of values at the time -// the snapshot was taken. -func (s *SampleSnapshot) Percentiles(ps []float64) []float64 { - return SamplePercentiles(s.values, ps) -} - -// Size returns the size of the sample at the time the snapshot was taken. -func (s *SampleSnapshot) Size() int { return len(s.values) } - -// Snapshot returns the snapshot. -func (s *SampleSnapshot) Snapshot() Sample { return s } - -// StdDev returns the standard deviation of values at the time the snapshot was -// taken. -func (s *SampleSnapshot) StdDev() float64 { return SampleStdDev(s.values) } - -// Sum returns the sum of values at the time the snapshot was taken. -func (s *SampleSnapshot) Sum() int64 { return SampleSum(s.values) } - -// Update panics. -func (*SampleSnapshot) Update(int64) { - panic("Update called on a SampleSnapshot") -} - -// Values returns a copy of the values in the sample. -func (s *SampleSnapshot) Values() []int64 { - values := make([]int64, len(s.values)) - copy(values, s.values) - return values -} - -// Variance returns the variance of values at the time the snapshot was taken. -func (s *SampleSnapshot) Variance() float64 { return SampleVariance(s.values) } - -// SampleStdDev returns the standard deviation of the slice of int64. -func SampleStdDev(values []int64) float64 { - return math.Sqrt(SampleVariance(values)) -} - -// SampleSum returns the sum of the slice of int64. -func SampleSum(values []int64) int64 { - var sum int64 - for _, v := range values { - sum += v - } - return sum -} - -// SampleVariance returns the variance of the slice of int64. -func SampleVariance(values []int64) float64 { - if 0 == len(values) { - return 0.0 - } - m := SampleMean(values) - var sum float64 - for _, v := range values { - d := float64(v) - m - sum += d * d - } - return sum / float64(len(values)) -} - -// A uniform sample using Vitter's Algorithm R. -// -// -type UniformSample struct { - count int64 - mutex sync.Mutex - reservoirSize int - values []int64 -} - -// NewUniformSample constructs a new uniform sample with the given reservoir -// size. -func NewUniformSample(reservoirSize int) Sample { - if UseNilMetrics { - return NilSample{} - } - return &UniformSample{ - reservoirSize: reservoirSize, - values: make([]int64, 0, reservoirSize), - } -} - -// Clear clears all samples. -func (s *UniformSample) Clear() { - s.mutex.Lock() - defer s.mutex.Unlock() - s.count = 0 - s.values = make([]int64, 0, s.reservoirSize) -} - -// Count returns the number of samples recorded, which may exceed the -// reservoir size. -func (s *UniformSample) Count() int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return s.count -} - -// Max returns the maximum value in the sample, which may not be the maximum -// value ever to be part of the sample. -func (s *UniformSample) Max() int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SampleMax(s.values) -} - -// Mean returns the mean of the values in the sample. -func (s *UniformSample) Mean() float64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SampleMean(s.values) -} - -// Min returns the minimum value in the sample, which may not be the minimum -// value ever to be part of the sample. -func (s *UniformSample) Min() int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SampleMin(s.values) -} - -// Percentile returns an arbitrary percentile of values in the sample. -func (s *UniformSample) Percentile(p float64) float64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SamplePercentile(s.values, p) -} - -// Percentiles returns a slice of arbitrary percentiles of values in the -// sample. -func (s *UniformSample) Percentiles(ps []float64) []float64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SamplePercentiles(s.values, ps) -} - -// Size returns the size of the sample, which is at most the reservoir size. -func (s *UniformSample) Size() int { - s.mutex.Lock() - defer s.mutex.Unlock() - return len(s.values) -} - -// Snapshot returns a read-only copy of the sample. -func (s *UniformSample) Snapshot() Sample { - s.mutex.Lock() - defer s.mutex.Unlock() - values := make([]int64, len(s.values)) - copy(values, s.values) - return &SampleSnapshot{ - count: s.count, - values: values, - } -} - -// StdDev returns the standard deviation of the values in the sample. -func (s *UniformSample) StdDev() float64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SampleStdDev(s.values) -} - -// Sum returns the sum of the values in the sample. -func (s *UniformSample) Sum() int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SampleSum(s.values) -} - -// Update samples a new value. -func (s *UniformSample) Update(v int64) { - s.mutex.Lock() - defer s.mutex.Unlock() - s.count++ - if len(s.values) < s.reservoirSize { - s.values = append(s.values, v) - } else { - r := rand.Int63n(s.count) - if r < int64(len(s.values)) { - s.values[int(r)] = v - } - } -} - -// Values returns a copy of the values in the sample. -func (s *UniformSample) Values() []int64 { - s.mutex.Lock() - defer s.mutex.Unlock() - values := make([]int64, len(s.values)) - copy(values, s.values) - return values -} - -// Variance returns the variance of the values in the sample. -func (s *UniformSample) Variance() float64 { - s.mutex.Lock() - defer s.mutex.Unlock() - return SampleVariance(s.values) -} - -// expDecaySample represents an individual sample in a heap. -type expDecaySample struct { - k float64 - v int64 -} - -func newExpDecaySampleHeap(reservoirSize int) *expDecaySampleHeap { - return &expDecaySampleHeap{make([]expDecaySample, 0, reservoirSize)} -} - -// expDecaySampleHeap is a min-heap of expDecaySamples. -// The internal implementation is copied from the standard library's container/heap -type expDecaySampleHeap struct { - s []expDecaySample -} - -func (h *expDecaySampleHeap) Clear() { - h.s = h.s[:0] -} - -func (h *expDecaySampleHeap) Push(s expDecaySample) { - n := len(h.s) - h.s = h.s[0 : n+1] - h.s[n] = s - h.up(n) -} - -func (h *expDecaySampleHeap) Pop() expDecaySample { - n := len(h.s) - 1 - h.s[0], h.s[n] = h.s[n], h.s[0] - h.down(0, n) - - n = len(h.s) - s := h.s[n-1] - h.s = h.s[0 : n-1] - return s -} - -func (h *expDecaySampleHeap) Size() int { - return len(h.s) -} - -func (h *expDecaySampleHeap) Values() []expDecaySample { - return h.s -} - -func (h *expDecaySampleHeap) up(j int) { - for { - i := (j - 1) / 2 // parent - if i == j || !(h.s[j].k < h.s[i].k) { - break - } - h.s[i], h.s[j] = h.s[j], h.s[i] - j = i - } -} - -func (h *expDecaySampleHeap) down(i, n int) { - for { - j1 := 2*i + 1 - if j1 >= n || j1 < 0 { // j1 < 0 after int overflow - break - } - j := j1 // left child - if j2 := j1 + 1; j2 < n && !(h.s[j1].k < h.s[j2].k) { - j = j2 // = 2*i + 2 // right child - } - if !(h.s[j].k < h.s[i].k) { - break - } - h.s[i], h.s[j] = h.s[j], h.s[i] - i = j - } -} - -type int64Slice []int64 - -func (p int64Slice) Len() int { return len(p) } -func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] } -func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/vendor/github.com/rcrowley/go-metrics/syslog.go b/vendor/github.com/rcrowley/go-metrics/syslog.go deleted file mode 100644 index 693f190855c..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/syslog.go +++ /dev/null @@ -1,78 +0,0 @@ -// +build !windows - -package metrics - -import ( - "fmt" - "log/syslog" - "time" -) - -// Output each metric in the given registry to syslog periodically using -// the given syslogger. -func Syslog(r Registry, d time.Duration, w *syslog.Writer) { - for _ = range time.Tick(d) { - r.Each(func(name string, i interface{}) { - switch metric := i.(type) { - case Counter: - w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Count())) - case Gauge: - w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Value())) - case GaugeFloat64: - w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Value())) - case Healthcheck: - metric.Check() - w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error())) - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - w.Info(fmt.Sprintf( - "histogram %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f", - name, - h.Count(), - h.Min(), - h.Max(), - h.Mean(), - h.StdDev(), - ps[0], - ps[1], - ps[2], - ps[3], - ps[4], - )) - case Meter: - m := metric.Snapshot() - w.Info(fmt.Sprintf( - "meter %s: count: %d 1-min: %.2f 5-min: %.2f 15-min: %.2f mean: %.2f", - name, - m.Count(), - m.Rate1(), - m.Rate5(), - m.Rate15(), - m.RateMean(), - )) - case Timer: - t := metric.Snapshot() - ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - w.Info(fmt.Sprintf( - "timer %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f 1-min: %.2f 5-min: %.2f 15-min: %.2f mean-rate: %.2f", - name, - t.Count(), - t.Min(), - t.Max(), - t.Mean(), - t.StdDev(), - ps[0], - ps[1], - ps[2], - ps[3], - ps[4], - t.Rate1(), - t.Rate5(), - t.Rate15(), - t.RateMean(), - )) - } - }) - } -} diff --git a/vendor/github.com/rcrowley/go-metrics/timer.go b/vendor/github.com/rcrowley/go-metrics/timer.go deleted file mode 100644 index d6ec4c6260f..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/timer.go +++ /dev/null @@ -1,329 +0,0 @@ -package metrics - -import ( - "sync" - "time" -) - -// Timers capture the duration and rate of events. -type Timer interface { - Count() int64 - Max() int64 - Mean() float64 - Min() int64 - Percentile(float64) float64 - Percentiles([]float64) []float64 - Rate1() float64 - Rate5() float64 - Rate15() float64 - RateMean() float64 - Snapshot() Timer - StdDev() float64 - Stop() - Sum() int64 - Time(func()) - Update(time.Duration) - UpdateSince(time.Time) - Variance() float64 -} - -// GetOrRegisterTimer returns an existing Timer or constructs and registers a -// new StandardTimer. -// Be sure to unregister the meter from the registry once it is of no use to -// allow for garbage collection. -func GetOrRegisterTimer(name string, r Registry) Timer { - if nil == r { - r = DefaultRegistry - } - return r.GetOrRegister(name, NewTimer).(Timer) -} - -// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter. -// Be sure to call Stop() once the timer is of no use to allow for garbage collection. -func NewCustomTimer(h Histogram, m Meter) Timer { - if UseNilMetrics { - return NilTimer{} - } - return &StandardTimer{ - histogram: h, - meter: m, - } -} - -// NewRegisteredTimer constructs and registers a new StandardTimer. -// Be sure to unregister the meter from the registry once it is of no use to -// allow for garbage collection. -func NewRegisteredTimer(name string, r Registry) Timer { - c := NewTimer() - if nil == r { - r = DefaultRegistry - } - r.Register(name, c) - return c -} - -// NewTimer constructs a new StandardTimer using an exponentially-decaying -// sample with the same reservoir size and alpha as UNIX load averages. -// Be sure to call Stop() once the timer is of no use to allow for garbage collection. -func NewTimer() Timer { - if UseNilMetrics { - return NilTimer{} - } - return &StandardTimer{ - histogram: NewHistogram(NewExpDecaySample(1028, 0.015)), - meter: NewMeter(), - } -} - -// NilTimer is a no-op Timer. -type NilTimer struct { - h Histogram - m Meter -} - -// Count is a no-op. -func (NilTimer) Count() int64 { return 0 } - -// Max is a no-op. -func (NilTimer) Max() int64 { return 0 } - -// Mean is a no-op. -func (NilTimer) Mean() float64 { return 0.0 } - -// Min is a no-op. -func (NilTimer) Min() int64 { return 0 } - -// Percentile is a no-op. -func (NilTimer) Percentile(p float64) float64 { return 0.0 } - -// Percentiles is a no-op. -func (NilTimer) Percentiles(ps []float64) []float64 { - return make([]float64, len(ps)) -} - -// Rate1 is a no-op. -func (NilTimer) Rate1() float64 { return 0.0 } - -// Rate5 is a no-op. -func (NilTimer) Rate5() float64 { return 0.0 } - -// Rate15 is a no-op. -func (NilTimer) Rate15() float64 { return 0.0 } - -// RateMean is a no-op. -func (NilTimer) RateMean() float64 { return 0.0 } - -// Snapshot is a no-op. -func (NilTimer) Snapshot() Timer { return NilTimer{} } - -// StdDev is a no-op. -func (NilTimer) StdDev() float64 { return 0.0 } - -// Stop is a no-op. -func (NilTimer) Stop() {} - -// Sum is a no-op. -func (NilTimer) Sum() int64 { return 0 } - -// Time is a no-op. -func (NilTimer) Time(func()) {} - -// Update is a no-op. -func (NilTimer) Update(time.Duration) {} - -// UpdateSince is a no-op. -func (NilTimer) UpdateSince(time.Time) {} - -// Variance is a no-op. -func (NilTimer) Variance() float64 { return 0.0 } - -// StandardTimer is the standard implementation of a Timer and uses a Histogram -// and Meter. -type StandardTimer struct { - histogram Histogram - meter Meter - mutex sync.Mutex -} - -// Count returns the number of events recorded. -func (t *StandardTimer) Count() int64 { - return t.histogram.Count() -} - -// Max returns the maximum value in the sample. -func (t *StandardTimer) Max() int64 { - return t.histogram.Max() -} - -// Mean returns the mean of the values in the sample. -func (t *StandardTimer) Mean() float64 { - return t.histogram.Mean() -} - -// Min returns the minimum value in the sample. -func (t *StandardTimer) Min() int64 { - return t.histogram.Min() -} - -// Percentile returns an arbitrary percentile of the values in the sample. -func (t *StandardTimer) Percentile(p float64) float64 { - return t.histogram.Percentile(p) -} - -// Percentiles returns a slice of arbitrary percentiles of the values in the -// sample. -func (t *StandardTimer) Percentiles(ps []float64) []float64 { - return t.histogram.Percentiles(ps) -} - -// Rate1 returns the one-minute moving average rate of events per second. -func (t *StandardTimer) Rate1() float64 { - return t.meter.Rate1() -} - -// Rate5 returns the five-minute moving average rate of events per second. -func (t *StandardTimer) Rate5() float64 { - return t.meter.Rate5() -} - -// Rate15 returns the fifteen-minute moving average rate of events per second. -func (t *StandardTimer) Rate15() float64 { - return t.meter.Rate15() -} - -// RateMean returns the meter's mean rate of events per second. -func (t *StandardTimer) RateMean() float64 { - return t.meter.RateMean() -} - -// Snapshot returns a read-only copy of the timer. -func (t *StandardTimer) Snapshot() Timer { - t.mutex.Lock() - defer t.mutex.Unlock() - return &TimerSnapshot{ - histogram: t.histogram.Snapshot().(*HistogramSnapshot), - meter: t.meter.Snapshot().(*MeterSnapshot), - } -} - -// StdDev returns the standard deviation of the values in the sample. -func (t *StandardTimer) StdDev() float64 { - return t.histogram.StdDev() -} - -// Stop stops the meter. -func (t *StandardTimer) Stop() { - t.meter.Stop() -} - -// Sum returns the sum in the sample. -func (t *StandardTimer) Sum() int64 { - return t.histogram.Sum() -} - -// Record the duration of the execution of the given function. -func (t *StandardTimer) Time(f func()) { - ts := time.Now() - f() - t.Update(time.Since(ts)) -} - -// Record the duration of an event. -func (t *StandardTimer) Update(d time.Duration) { - t.mutex.Lock() - defer t.mutex.Unlock() - t.histogram.Update(int64(d)) - t.meter.Mark(1) -} - -// Record the duration of an event that started at a time and ends now. -func (t *StandardTimer) UpdateSince(ts time.Time) { - t.mutex.Lock() - defer t.mutex.Unlock() - t.histogram.Update(int64(time.Since(ts))) - t.meter.Mark(1) -} - -// Variance returns the variance of the values in the sample. -func (t *StandardTimer) Variance() float64 { - return t.histogram.Variance() -} - -// TimerSnapshot is a read-only copy of another Timer. -type TimerSnapshot struct { - histogram *HistogramSnapshot - meter *MeterSnapshot -} - -// Count returns the number of events recorded at the time the snapshot was -// taken. -func (t *TimerSnapshot) Count() int64 { return t.histogram.Count() } - -// Max returns the maximum value at the time the snapshot was taken. -func (t *TimerSnapshot) Max() int64 { return t.histogram.Max() } - -// Mean returns the mean value at the time the snapshot was taken. -func (t *TimerSnapshot) Mean() float64 { return t.histogram.Mean() } - -// Min returns the minimum value at the time the snapshot was taken. -func (t *TimerSnapshot) Min() int64 { return t.histogram.Min() } - -// Percentile returns an arbitrary percentile of sampled values at the time the -// snapshot was taken. -func (t *TimerSnapshot) Percentile(p float64) float64 { - return t.histogram.Percentile(p) -} - -// Percentiles returns a slice of arbitrary percentiles of sampled values at -// the time the snapshot was taken. -func (t *TimerSnapshot) Percentiles(ps []float64) []float64 { - return t.histogram.Percentiles(ps) -} - -// Rate1 returns the one-minute moving average rate of events per second at the -// time the snapshot was taken. -func (t *TimerSnapshot) Rate1() float64 { return t.meter.Rate1() } - -// Rate5 returns the five-minute moving average rate of events per second at -// the time the snapshot was taken. -func (t *TimerSnapshot) Rate5() float64 { return t.meter.Rate5() } - -// Rate15 returns the fifteen-minute moving average rate of events per second -// at the time the snapshot was taken. -func (t *TimerSnapshot) Rate15() float64 { return t.meter.Rate15() } - -// RateMean returns the meter's mean rate of events per second at the time the -// snapshot was taken. -func (t *TimerSnapshot) RateMean() float64 { return t.meter.RateMean() } - -// Snapshot returns the snapshot. -func (t *TimerSnapshot) Snapshot() Timer { return t } - -// StdDev returns the standard deviation of the values at the time the snapshot -// was taken. -func (t *TimerSnapshot) StdDev() float64 { return t.histogram.StdDev() } - -// Stop is a no-op. -func (t *TimerSnapshot) Stop() {} - -// Sum returns the sum at the time the snapshot was taken. -func (t *TimerSnapshot) Sum() int64 { return t.histogram.Sum() } - -// Time panics. -func (*TimerSnapshot) Time(func()) { - panic("Time called on a TimerSnapshot") -} - -// Update panics. -func (*TimerSnapshot) Update(time.Duration) { - panic("Update called on a TimerSnapshot") -} - -// UpdateSince panics. -func (*TimerSnapshot) UpdateSince(time.Time) { - panic("UpdateSince called on a TimerSnapshot") -} - -// Variance returns the variance of the values at the time the snapshot was -// taken. -func (t *TimerSnapshot) Variance() float64 { return t.histogram.Variance() } diff --git a/vendor/github.com/rcrowley/go-metrics/writer.go b/vendor/github.com/rcrowley/go-metrics/writer.go deleted file mode 100644 index 091e971d2e6..00000000000 --- a/vendor/github.com/rcrowley/go-metrics/writer.go +++ /dev/null @@ -1,100 +0,0 @@ -package metrics - -import ( - "fmt" - "io" - "sort" - "time" -) - -// Write sorts writes each metric in the given registry periodically to the -// given io.Writer. -func Write(r Registry, d time.Duration, w io.Writer) { - for _ = range time.Tick(d) { - WriteOnce(r, w) - } -} - -// WriteOnce sorts and writes metrics in the given registry to the given -// io.Writer. -func WriteOnce(r Registry, w io.Writer) { - var namedMetrics namedMetricSlice - r.Each(func(name string, i interface{}) { - namedMetrics = append(namedMetrics, namedMetric{name, i}) - }) - - sort.Sort(namedMetrics) - for _, namedMetric := range namedMetrics { - switch metric := namedMetric.m.(type) { - case Counter: - fmt.Fprintf(w, "counter %s\n", namedMetric.name) - fmt.Fprintf(w, " count: %9d\n", metric.Count()) - case Gauge: - fmt.Fprintf(w, "gauge %s\n", namedMetric.name) - fmt.Fprintf(w, " value: %9d\n", metric.Value()) - case GaugeFloat64: - fmt.Fprintf(w, "gauge %s\n", namedMetric.name) - fmt.Fprintf(w, " value: %f\n", metric.Value()) - case Healthcheck: - metric.Check() - fmt.Fprintf(w, "healthcheck %s\n", namedMetric.name) - fmt.Fprintf(w, " error: %v\n", metric.Error()) - case Histogram: - h := metric.Snapshot() - ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - fmt.Fprintf(w, "histogram %s\n", namedMetric.name) - fmt.Fprintf(w, " count: %9d\n", h.Count()) - fmt.Fprintf(w, " min: %9d\n", h.Min()) - fmt.Fprintf(w, " max: %9d\n", h.Max()) - fmt.Fprintf(w, " mean: %12.2f\n", h.Mean()) - fmt.Fprintf(w, " stddev: %12.2f\n", h.StdDev()) - fmt.Fprintf(w, " median: %12.2f\n", ps[0]) - fmt.Fprintf(w, " 75%%: %12.2f\n", ps[1]) - fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2]) - fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3]) - fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4]) - case Meter: - m := metric.Snapshot() - fmt.Fprintf(w, "meter %s\n", namedMetric.name) - fmt.Fprintf(w, " count: %9d\n", m.Count()) - fmt.Fprintf(w, " 1-min rate: %12.2f\n", m.Rate1()) - fmt.Fprintf(w, " 5-min rate: %12.2f\n", m.Rate5()) - fmt.Fprintf(w, " 15-min rate: %12.2f\n", m.Rate15()) - fmt.Fprintf(w, " mean rate: %12.2f\n", m.RateMean()) - case Timer: - t := metric.Snapshot() - ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) - fmt.Fprintf(w, "timer %s\n", namedMetric.name) - fmt.Fprintf(w, " count: %9d\n", t.Count()) - fmt.Fprintf(w, " min: %9d\n", t.Min()) - fmt.Fprintf(w, " max: %9d\n", t.Max()) - fmt.Fprintf(w, " mean: %12.2f\n", t.Mean()) - fmt.Fprintf(w, " stddev: %12.2f\n", t.StdDev()) - fmt.Fprintf(w, " median: %12.2f\n", ps[0]) - fmt.Fprintf(w, " 75%%: %12.2f\n", ps[1]) - fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2]) - fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3]) - fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4]) - fmt.Fprintf(w, " 1-min rate: %12.2f\n", t.Rate1()) - fmt.Fprintf(w, " 5-min rate: %12.2f\n", t.Rate5()) - fmt.Fprintf(w, " 15-min rate: %12.2f\n", t.Rate15()) - fmt.Fprintf(w, " mean rate: %12.2f\n", t.RateMean()) - } - } -} - -type namedMetric struct { - name string - m interface{} -} - -// namedMetricSlice is a slice of namedMetrics that implements sort.Interface. -type namedMetricSlice []namedMetric - -func (nms namedMetricSlice) Len() int { return len(nms) } - -func (nms namedMetricSlice) Swap(i, j int) { nms[i], nms[j] = nms[j], nms[i] } - -func (nms namedMetricSlice) Less(i, j int) bool { - return nms[i].name < nms[j].name -} diff --git a/vendor/go.opencensus.io/exporter/stackdriver/propagation/http.go b/vendor/go.opencensus.io/exporter/stackdriver/propagation/http.go deleted file mode 100644 index 7cc02a1104b..00000000000 --- a/vendor/go.opencensus.io/exporter/stackdriver/propagation/http.go +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright 2018, OpenCensus Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package propagation implement X-Cloud-Trace-Context header propagation used -// by Google Cloud products. -package propagation // import "go.opencensus.io/exporter/stackdriver/propagation" - -import ( - "encoding/binary" - "encoding/hex" - "fmt" - "net/http" - "strconv" - "strings" - - "go.opencensus.io/trace" - "go.opencensus.io/trace/propagation" -) - -const ( - httpHeaderMaxSize = 200 - httpHeader = `X-Cloud-Trace-Context` -) - -var _ propagation.HTTPFormat = (*HTTPFormat)(nil) - -// HTTPFormat implements propagation.HTTPFormat to propagate -// traces in HTTP headers for Google Cloud Platform and Stackdriver Trace. -type HTTPFormat struct{} - -// SpanContextFromRequest extracts a Stackdriver Trace span context from incoming requests. -func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) { - h := req.Header.Get(httpHeader) - // See https://cloud.google.com/trace/docs/faq for the header HTTPFormat. - // Return if the header is empty or missing, or if the header is unreasonably - // large, to avoid making unnecessary copies of a large string. - if h == "" || len(h) > httpHeaderMaxSize { - return trace.SpanContext{}, false - } - - // Parse the trace id field. - slash := strings.Index(h, `/`) - if slash == -1 { - return trace.SpanContext{}, false - } - tid, h := h[:slash], h[slash+1:] - - buf, err := hex.DecodeString(tid) - if err != nil { - return trace.SpanContext{}, false - } - copy(sc.TraceID[:], buf) - - // Parse the span id field. - spanstr := h - semicolon := strings.Index(h, `;`) - if semicolon != -1 { - spanstr, h = h[:semicolon], h[semicolon+1:] - } - sid, err := strconv.ParseUint(spanstr, 10, 64) - if err != nil { - return trace.SpanContext{}, false - } - binary.BigEndian.PutUint64(sc.SpanID[:], sid) - - // Parse the options field, options field is optional. - if !strings.HasPrefix(h, "o=") { - return sc, true - } - o, err := strconv.ParseUint(h[2:], 10, 64) - if err != nil { - return trace.SpanContext{}, false - } - sc.TraceOptions = trace.TraceOptions(o) - return sc, true -} - -// SpanContextToRequest modifies the given request to include a Stackdriver Trace header. -func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) { - sid := binary.BigEndian.Uint64(sc.SpanID[:]) - header := fmt.Sprintf("%s/%d;o=%d", hex.EncodeToString(sc.TraceID[:]), sid, int64(sc.TraceOptions)) - req.Header.Set(httpHeader, header) -} diff --git a/vendor/go.opencensus.io/plugin/ocgrpc/client.go b/vendor/go.opencensus.io/plugin/ocgrpc/client.go deleted file mode 100644 index 37d238f1499..00000000000 --- a/vendor/go.opencensus.io/plugin/ocgrpc/client.go +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2018, OpenCensus Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ocgrpc - -import ( - "go.opencensus.io/trace" - "golang.org/x/net/context" - - "google.golang.org/grpc/stats" -) - -// ClientHandler implements a gRPC stats.Handler for recording OpenCensus stats and -// traces. Use with gRPC clients only. -type ClientHandler struct { - // StartOptions allows configuring the StartOptions used to create new spans. - // - // StartOptions.SpanKind will always be set to trace.SpanKindClient - // for spans started by this handler. - StartOptions trace.StartOptions -} - -func (c *ClientHandler) HandleConn(ctx context.Context, cs stats.ConnStats) { - // no-op -} - -// TagConn exists to satisfy gRPC stats.Handler. -func (c *ClientHandler) TagConn(ctx context.Context, cti *stats.ConnTagInfo) context.Context { - // no-op - return ctx -} - -// HandleRPC implements per-RPC tracing and stats instrumentation. -func (c *ClientHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) { - traceHandleRPC(ctx, rs) - statsHandleRPC(ctx, rs) -} - -// TagRPC implements per-RPC context management. -func (c *ClientHandler) TagRPC(ctx context.Context, rti *stats.RPCTagInfo) context.Context { - ctx = c.traceTagRPC(ctx, rti) - ctx = c.statsTagRPC(ctx, rti) - return ctx -} diff --git a/vendor/go.opencensus.io/plugin/ocgrpc/client_metrics.go b/vendor/go.opencensus.io/plugin/ocgrpc/client_metrics.go deleted file mode 100644 index b8efacfb3fd..00000000000 --- a/vendor/go.opencensus.io/plugin/ocgrpc/client_metrics.go +++ /dev/null @@ -1,116 +0,0 @@ -// Copyright 2017, OpenCensus Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -package ocgrpc - -import ( - "go.opencensus.io/stats" - "go.opencensus.io/stats/view" - "go.opencensus.io/tag" -) - -// The following variables are measures are recorded by ClientHandler: -var ( - ClientSentMessagesPerRPC = stats.Int64("grpc.io/client/sent_messages_per_rpc", "Number of messages sent in the RPC (always 1 for non-streaming RPCs).", stats.UnitDimensionless) - ClientSentBytesPerRPC = stats.Int64("grpc.io/client/sent_bytes_per_rpc", "Total bytes sent across all request messages per RPC.", stats.UnitBytes) - ClientReceivedMessagesPerRPC = stats.Int64("grpc.io/client/received_messages_per_rpc", "Number of response messages received per RPC (always 1 for non-streaming RPCs).", stats.UnitDimensionless) - ClientReceivedBytesPerRPC = stats.Int64("grpc.io/client/received_bytes_per_rpc", "Total bytes received across all response messages per RPC.", stats.UnitBytes) - ClientRoundtripLatency = stats.Float64("grpc.io/client/roundtrip_latency", "Time between first byte of request sent to last byte of response received, or terminal error.", stats.UnitMilliseconds) - ClientServerLatency = stats.Float64("grpc.io/client/server_latency", `Propagated from the server and should have the same value as "grpc.io/server/latency".`, stats.UnitMilliseconds) -) - -// Predefined views may be subscribed to collect data for the above measures. -// As always, you may also define your own custom views over measures collected by this -// package. These are declared as a convenience only; none are subscribed by -// default. -var ( - ClientSentBytesPerRPCView = &view.View{ - Measure: ClientSentBytesPerRPC, - Name: "grpc.io/client/sent_bytes_per_rpc", - Description: "Distribution of bytes sent per RPC, by method.", - TagKeys: []tag.Key{KeyClientMethod}, - Aggregation: DefaultBytesDistribution, - } - - ClientReceivedBytesPerRPCView = &view.View{ - Measure: ClientReceivedBytesPerRPC, - Name: "grpc.io/client/received_bytes_per_rpc", - Description: "Distribution of bytes received per RPC, by method.", - TagKeys: []tag.Key{KeyClientMethod}, - Aggregation: DefaultBytesDistribution, - } - - ClientRoundtripLatencyView = &view.View{ - Measure: ClientRoundtripLatency, - Name: "grpc.io/client/roundtrip_latency", - Description: "Distribution of round-trip latency, by method.", - TagKeys: []tag.Key{KeyClientMethod}, - Aggregation: DefaultMillisecondsDistribution, - } - - ClientCompletedRPCsView = &view.View{ - Measure: ClientRoundtripLatency, - Name: "grpc.io/client/completed_rpcs", - Description: "Count of RPCs by method and status.", - TagKeys: []tag.Key{KeyClientMethod, KeyClientStatus}, - Aggregation: view.Count(), - } - - ClientSentMessagesPerRPCView = &view.View{ - Measure: ClientSentMessagesPerRPC, - Name: "grpc.io/client/sent_messages_per_rpc", - Description: "Distribution of sent messages count per RPC, by method.", - TagKeys: []tag.Key{KeyClientMethod}, - Aggregation: DefaultMessageCountDistribution, - } - - ClientReceivedMessagesPerRPCView = &view.View{ - Measure: ClientReceivedMessagesPerRPC, - Name: "grpc.io/client/received_messages_per_rpc", - Description: "Distribution of received messages count per RPC, by method.", - TagKeys: []tag.Key{KeyClientMethod}, - Aggregation: DefaultMessageCountDistribution, - } - - ClientServerLatencyView = &view.View{ - Measure: ClientServerLatency, - Name: "grpc.io/client/server_latency", - Description: "Distribution of server latency as viewed by client, by method.", - TagKeys: []tag.Key{KeyClientMethod}, - Aggregation: DefaultMillisecondsDistribution, - } - - // Deprecated: This view is going to be removed, if you need it please define it - // yourself. - ClientRequestCountView = &view.View{ - Name: "Count of request messages per client RPC", - TagKeys: []tag.Key{KeyClientMethod}, - Measure: ClientRoundtripLatency, - Aggregation: view.Count(), - } -) - -// DefaultClientViews are the default client views provided by this package. -var DefaultClientViews = []*view.View{ - ClientSentBytesPerRPCView, - ClientReceivedBytesPerRPCView, - ClientRoundtripLatencyView, - ClientCompletedRPCsView, -} - -// TODO(jbd): Add roundtrip_latency, uncompressed_request_bytes, uncompressed_response_bytes, request_count, response_count. -// TODO(acetechnologist): This is temporary and will need to be replaced by a -// mechanism to load these defaults from a common repository/config shared by -// all supported languages. Likely a serialized protobuf of these defaults. diff --git a/vendor/go.opencensus.io/plugin/ocgrpc/client_stats_handler.go b/vendor/go.opencensus.io/plugin/ocgrpc/client_stats_handler.go deleted file mode 100644 index 303c607f632..00000000000 --- a/vendor/go.opencensus.io/plugin/ocgrpc/client_stats_handler.go +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2017, OpenCensus Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -package ocgrpc - -import ( - "time" - - "go.opencensus.io/tag" - "golang.org/x/net/context" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/stats" -) - -// statsTagRPC gets the tag.Map populated by the application code, serializes -// its tags into the GRPC metadata in order to be sent to the server. -func (h *ClientHandler) statsTagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context { - startTime := time.Now() - if info == nil { - if grpclog.V(2) { - grpclog.Infof("clientHandler.TagRPC called with nil info.", info.FullMethodName) - } - return ctx - } - - d := &rpcData{ - startTime: startTime, - method: info.FullMethodName, - } - ts := tag.FromContext(ctx) - if ts != nil { - encoded := tag.Encode(ts) - ctx = stats.SetTags(ctx, encoded) - } - - return context.WithValue(ctx, rpcDataKey, d) -} diff --git a/vendor/go.opencensus.io/plugin/ocgrpc/doc.go b/vendor/go.opencensus.io/plugin/ocgrpc/doc.go deleted file mode 100644 index 1370323fb71..00000000000 --- a/vendor/go.opencensus.io/plugin/ocgrpc/doc.go +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2017, OpenCensus Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Package ocgrpc contains OpenCensus stats and trace -// integrations for gRPC. -// -// Use ServerHandler for servers and ClientHandler for clients. -package ocgrpc // import "go.opencensus.io/plugin/ocgrpc" diff --git a/vendor/go.opencensus.io/plugin/ocgrpc/server.go b/vendor/go.opencensus.io/plugin/ocgrpc/server.go deleted file mode 100644 index b67b3e2be2a..00000000000 --- a/vendor/go.opencensus.io/plugin/ocgrpc/server.go +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2018, OpenCensus Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ocgrpc - -import ( - "go.opencensus.io/trace" - "golang.org/x/net/context" - - "google.golang.org/grpc/stats" -) - -// ServerHandler implements gRPC stats.Handler recording OpenCensus stats and -// traces. Use with gRPC servers. -// -// When installed (see Example), tracing metadata is read from inbound RPCs -// by default. If no tracing metadata is present, or if the tracing metadata is -// present but the SpanContext isn't sampled, then a new trace may be started -// (as determined by Sampler). -type ServerHandler struct { - // IsPublicEndpoint may be set to true to always start a new trace around - // each RPC. Any SpanContext in the RPC metadata will be added as a linked - // span instead of making it the parent of the span created around the - // server RPC. - // - // Be aware that if you leave this false (the default) on a public-facing - // server, callers will be able to send tracing metadata in gRPC headers - // and trigger traces in your backend. - IsPublicEndpoint bool - - // StartOptions to use for to spans started around RPCs handled by this server. - // - // These will apply even if there is tracing metadata already - // present on the inbound RPC but the SpanContext is not sampled. This - // ensures that each service has some opportunity to be traced. If you would - // like to not add any additional traces for this gRPC service, set: - // - // StartOptions.Sampler = trace.ProbabilitySampler(0.0) - // - // StartOptions.SpanKind will always be set to trace.SpanKindServer - // for spans started by this handler. - StartOptions trace.StartOptions -} - -var _ stats.Handler = (*ServerHandler)(nil) - -// HandleConn exists to satisfy gRPC stats.Handler. -func (s *ServerHandler) HandleConn(ctx context.Context, cs stats.ConnStats) { - // no-op -} - -// TagConn exists to satisfy gRPC stats.Handler. -func (s *ServerHandler) TagConn(ctx context.Context, cti *stats.ConnTagInfo) context.Context { - // no-op - return ctx -} - -// HandleRPC implements per-RPC tracing and stats instrumentation. -func (s *ServerHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) { - traceHandleRPC(ctx, rs) - statsHandleRPC(ctx, rs) -} - -// TagRPC implements per-RPC context management. -func (s *ServerHandler) TagRPC(ctx context.Context, rti *stats.RPCTagInfo) context.Context { - ctx = s.traceTagRPC(ctx, rti) - ctx = s.statsTagRPC(ctx, rti) - return ctx -} diff --git a/vendor/go.opencensus.io/plugin/ocgrpc/server_metrics.go b/vendor/go.opencensus.io/plugin/ocgrpc/server_metrics.go deleted file mode 100644 index 02323f8712e..00000000000 --- a/vendor/go.opencensus.io/plugin/ocgrpc/server_metrics.go +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2017, OpenCensus Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -package ocgrpc - -import ( - "go.opencensus.io/stats" - "go.opencensus.io/stats/view" - "go.opencensus.io/tag" -) - -// The following variables are measures are recorded by ServerHandler: -var ( - ServerReceivedMessagesPerRPC = stats.Int64("grpc.io/server/received_messages_per_rpc", "Number of messages received in each RPC. Has value 1 for non-streaming RPCs.", stats.UnitDimensionless) - ServerReceivedBytesPerRPC = stats.Int64("grpc.io/server/received_bytes_per_rpc", "Total bytes received across all messages per RPC.", stats.UnitBytes) - ServerSentMessagesPerRPC = stats.Int64("grpc.io/server/sent_messages_per_rpc", "Number of messages sent in each RPC. Has value 1 for non-streaming RPCs.", stats.UnitDimensionless) - ServerSentBytesPerRPC = stats.Int64("grpc.io/server/sent_bytes_per_rpc", "Total bytes sent in across all response messages per RPC.", stats.UnitBytes) - ServerLatency = stats.Float64("grpc.io/server/server_latency", "Time between first byte of request received to last byte of response sent, or terminal error.", stats.UnitMilliseconds) -) - -// TODO(acetechnologist): This is temporary and will need to be replaced by a -// mechanism to load these defaults from a common repository/config shared by -// all supported languages. Likely a serialized protobuf of these defaults. - -// Predefined views may be subscribed to collect data for the above measures. -// As always, you may also define your own custom views over measures collected by this -// package. These are declared as a convenience only; none are subscribed by -// default. -var ( - ServerReceivedBytesPerRPCView = &view.View{ - Name: "grpc.io/server/received_bytes_per_rpc", - Description: "Distribution of received bytes per RPC, by method.", - Measure: ServerReceivedBytesPerRPC, - TagKeys: []tag.Key{KeyServerMethod}, - Aggregation: DefaultBytesDistribution, - } - - ServerSentBytesPerRPCView = &view.View{ - Name: "grpc.io/server/sent_bytes_per_rpc", - Description: "Distribution of total sent bytes per RPC, by method.", - Measure: ServerSentBytesPerRPC, - TagKeys: []tag.Key{KeyServerMethod}, - Aggregation: DefaultBytesDistribution, - } - - ServerLatencyView = &view.View{ - Name: "grpc.io/server/server_latency", - Description: "Distribution of server latency in milliseconds, by method.", - TagKeys: []tag.Key{KeyServerMethod}, - Measure: ServerLatency, - Aggregation: DefaultMillisecondsDistribution, - } - - ServerCompletedRPCsView = &view.View{ - Name: "grpc.io/server/completed_rpcs", - Description: "Count of RPCs by method and status.", - TagKeys: []tag.Key{KeyServerMethod, KeyServerStatus}, - Measure: ServerLatency, - Aggregation: view.Count(), - } - - ServerReceivedMessagesPerRPCView = &view.View{ - Name: "grpc.io/server/received_messages_per_rpc", - Description: "Distribution of messages received count per RPC, by method.", - TagKeys: []tag.Key{KeyServerMethod}, - Measure: ServerReceivedMessagesPerRPC, - Aggregation: DefaultMessageCountDistribution, - } - - ServerSentMessagesPerRPCView = &view.View{ - Name: "grpc.io/server/sent_messages_per_rpc", - Description: "Distribution of messages sent count per RPC, by method.", - TagKeys: []tag.Key{KeyServerMethod}, - Measure: ServerSentMessagesPerRPC, - Aggregation: DefaultMessageCountDistribution, - } -) - -// DefaultServerViews are the default server views provided by this package. -var DefaultServerViews = []*view.View{ - ServerReceivedBytesPerRPCView, - ServerSentBytesPerRPCView, - ServerLatencyView, - ServerCompletedRPCsView, -} diff --git a/vendor/go.opencensus.io/plugin/ocgrpc/server_stats_handler.go b/vendor/go.opencensus.io/plugin/ocgrpc/server_stats_handler.go deleted file mode 100644 index 7847c1a912e..00000000000 --- a/vendor/go.opencensus.io/plugin/ocgrpc/server_stats_handler.go +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2017, OpenCensus Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -package ocgrpc - -import ( - "time" - - "golang.org/x/net/context" - - "go.opencensus.io/tag" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/stats" -) - -// statsTagRPC gets the metadata from gRPC context, extracts the encoded tags from -// it and creates a new tag.Map and puts them into the returned context. -func (h *ServerHandler) statsTagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context { - startTime := time.Now() - if info == nil { - if grpclog.V(2) { - grpclog.Infof("opencensus: TagRPC called with nil info.") - } - return ctx - } - d := &rpcData{ - startTime: startTime, - method: info.FullMethodName, - } - propagated := h.extractPropagatedTags(ctx) - ctx = tag.NewContext(ctx, propagated) - ctx, _ = tag.New(ctx, tag.Upsert(KeyServerMethod, methodName(info.FullMethodName))) - return context.WithValue(ctx, rpcDataKey, d) -} - -// extractPropagatedTags creates a new tag map containing the tags extracted from the -// gRPC metadata. -func (h *ServerHandler) extractPropagatedTags(ctx context.Context) *tag.Map { - buf := stats.Tags(ctx) - if buf == nil { - return nil - } - propagated, err := tag.Decode(buf) - if err != nil { - if grpclog.V(2) { - grpclog.Warningf("opencensus: Failed to decode tags from gRPC metadata failed to decode: %v", err) - } - return nil - } - return propagated -} diff --git a/vendor/go.opencensus.io/plugin/ocgrpc/stats_common.go b/vendor/go.opencensus.io/plugin/ocgrpc/stats_common.go deleted file mode 100644 index acb626e126c..00000000000 --- a/vendor/go.opencensus.io/plugin/ocgrpc/stats_common.go +++ /dev/null @@ -1,199 +0,0 @@ -// Copyright 2017, OpenCensus Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. -// - -package ocgrpc - -import ( - "context" - "strconv" - "strings" - "sync/atomic" - "time" - - ocstats "go.opencensus.io/stats" - "go.opencensus.io/stats/view" - "go.opencensus.io/tag" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/stats" - "google.golang.org/grpc/status" -) - -type grpcInstrumentationKey string - -// rpcData holds the instrumentation RPC data that is needed between the start -// and end of an call. It holds the info that this package needs to keep track -// of between the various GRPC events. -type rpcData struct { - // reqCount and respCount has to be the first words - // in order to be 64-aligned on 32-bit architectures. - sentCount, sentBytes, recvCount, recvBytes int64 // access atomically - - // startTime represents the time at which TagRPC was invoked at the - // beginning of an RPC. It is an appoximation of the time when the - // application code invoked GRPC code. - startTime time.Time - method string -} - -// The following variables define the default hard-coded auxiliary data used by -// both the default GRPC client and GRPC server metrics. -var ( - DefaultBytesDistribution = view.Distribution(0, 1024, 2048, 4096, 16384, 65536, 262144, 1048576, 4194304, 16777216, 67108864, 268435456, 1073741824, 4294967296) - DefaultMillisecondsDistribution = view.Distribution(0, 0.01, 0.05, 0.1, 0.3, 0.6, 0.8, 1, 2, 3, 4, 5, 6, 8, 10, 13, 16, 20, 25, 30, 40, 50, 65, 80, 100, 130, 160, 200, 250, 300, 400, 500, 650, 800, 1000, 2000, 5000, 10000, 20000, 50000, 100000) - DefaultMessageCountDistribution = view.Distribution(0, 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536) -) - -var ( - KeyServerMethod, _ = tag.NewKey("grpc_server_method") - KeyClientMethod, _ = tag.NewKey("grpc_client_method") - KeyServerStatus, _ = tag.NewKey("grpc_server_status") - KeyClientStatus, _ = tag.NewKey("grpc_client_status") -) - -var ( - rpcDataKey = grpcInstrumentationKey("opencensus-rpcData") -) - -func methodName(fullname string) string { - return strings.TrimLeft(fullname, "/") -} - -// statsHandleRPC processes the RPC events. -func statsHandleRPC(ctx context.Context, s stats.RPCStats) { - switch st := s.(type) { - case *stats.Begin, *stats.OutHeader, *stats.InHeader, *stats.InTrailer, *stats.OutTrailer: - // do nothing for client - case *stats.OutPayload: - handleRPCOutPayload(ctx, st) - case *stats.InPayload: - handleRPCInPayload(ctx, st) - case *stats.End: - handleRPCEnd(ctx, st) - default: - grpclog.Infof("unexpected stats: %T", st) - } -} - -func handleRPCOutPayload(ctx context.Context, s *stats.OutPayload) { - d, ok := ctx.Value(rpcDataKey).(*rpcData) - if !ok { - if grpclog.V(2) { - grpclog.Infoln("Failed to retrieve *rpcData from context.") - } - return - } - - atomic.AddInt64(&d.sentBytes, int64(s.Length)) - atomic.AddInt64(&d.sentCount, 1) -} - -func handleRPCInPayload(ctx context.Context, s *stats.InPayload) { - d, ok := ctx.Value(rpcDataKey).(*rpcData) - if !ok { - if grpclog.V(2) { - grpclog.Infoln("Failed to retrieve *rpcData from context.") - } - return - } - - atomic.AddInt64(&d.recvBytes, int64(s.Length)) - atomic.AddInt64(&d.recvCount, 1) -} - -func handleRPCEnd(ctx context.Context, s *stats.End) { - d, ok := ctx.Value(rpcDataKey).(*rpcData) - if !ok { - if grpclog.V(2) { - grpclog.Infoln("Failed to retrieve *rpcData from context.") - } - return - } - - elapsedTime := time.Since(d.startTime) - - var st string - if s.Error != nil { - s, ok := status.FromError(s.Error) - if ok { - st = statusCodeToString(s) - } - } else { - st = "OK" - } - - latencyMillis := float64(elapsedTime) / float64(time.Millisecond) - if s.Client { - ctx, _ = tag.New(ctx, - tag.Upsert(KeyClientMethod, methodName(d.method)), - tag.Upsert(KeyClientStatus, st)) - ocstats.Record(ctx, - ClientSentBytesPerRPC.M(atomic.LoadInt64(&d.sentBytes)), - ClientSentMessagesPerRPC.M(atomic.LoadInt64(&d.sentCount)), - ClientReceivedMessagesPerRPC.M(atomic.LoadInt64(&d.recvCount)), - ClientReceivedBytesPerRPC.M(atomic.LoadInt64(&d.recvBytes)), - ClientRoundtripLatency.M(latencyMillis)) - } else { - ctx, _ = tag.New(ctx, tag.Upsert(KeyServerStatus, st)) - ocstats.Record(ctx, - ServerSentBytesPerRPC.M(atomic.LoadInt64(&d.sentBytes)), - ServerSentMessagesPerRPC.M(atomic.LoadInt64(&d.sentCount)), - ServerReceivedMessagesPerRPC.M(atomic.LoadInt64(&d.recvCount)), - ServerReceivedBytesPerRPC.M(atomic.LoadInt64(&d.recvBytes)), - ServerLatency.M(latencyMillis)) - } -} - -func statusCodeToString(s *status.Status) string { - // see https://github.com/grpc/grpc/blob/master/doc/statuscodes.md - switch c := s.Code(); c { - case codes.OK: - return "OK" - case codes.Canceled: - return "CANCELLED" - case codes.Unknown: - return "UNKNOWN" - case codes.InvalidArgument: - return "INVALID_ARGUMENT" - case codes.DeadlineExceeded: - return "DEADLINE_EXCEEDED" - case codes.NotFound: - return "NOT_FOUND" - case codes.AlreadyExists: - return "ALREADY_EXISTS" - case codes.PermissionDenied: - return "PERMISSION_DENIED" - case codes.ResourceExhausted: - return "RESOURCE_EXHAUSTED" - case codes.FailedPrecondition: - return "FAILED_PRECONDITION" - case codes.Aborted: - return "ABORTED" - case codes.OutOfRange: - return "OUT_OF_RANGE" - case codes.Unimplemented: - return "UNIMPLEMENTED" - case codes.Internal: - return "INTERNAL" - case codes.Unavailable: - return "UNAVAILABLE" - case codes.DataLoss: - return "DATA_LOSS" - case codes.Unauthenticated: - return "UNAUTHENTICATED" - default: - return "CODE_" + strconv.FormatInt(int64(c), 10) - } -} diff --git a/vendor/go.opencensus.io/plugin/ocgrpc/trace_common.go b/vendor/go.opencensus.io/plugin/ocgrpc/trace_common.go deleted file mode 100644 index 720f381c275..00000000000 --- a/vendor/go.opencensus.io/plugin/ocgrpc/trace_common.go +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2017, OpenCensus Authors -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package ocgrpc - -import ( - "strings" - - "google.golang.org/grpc/codes" - - "go.opencensus.io/trace" - "go.opencensus.io/trace/propagation" - "golang.org/x/net/context" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/stats" - "google.golang.org/grpc/status" -) - -const traceContextKey = "grpc-trace-bin" - -// TagRPC creates a new trace span for the client side of the RPC. -// -// It returns ctx with the new trace span added and a serialization of the -// SpanContext added to the outgoing gRPC metadata. -func (c *ClientHandler) traceTagRPC(ctx context.Context, rti *stats.RPCTagInfo) context.Context { - name := strings.TrimPrefix(rti.FullMethodName, "/") - name = strings.Replace(name, "/", ".", -1) - ctx, span := trace.StartSpan(ctx, name, - trace.WithSampler(c.StartOptions.Sampler), - trace.WithSpanKind(trace.SpanKindClient)) // span is ended by traceHandleRPC - traceContextBinary := propagation.Binary(span.SpanContext()) - return metadata.AppendToOutgoingContext(ctx, traceContextKey, string(traceContextBinary)) -} - -// TagRPC creates a new trace span for the server side of the RPC. -// -// It checks the incoming gRPC metadata in ctx for a SpanContext, and if -// it finds one, uses that SpanContext as the parent context of the new span. -// -// It returns ctx, with the new trace span added. -func (s *ServerHandler) traceTagRPC(ctx context.Context, rti *stats.RPCTagInfo) context.Context { - md, _ := metadata.FromIncomingContext(ctx) - name := strings.TrimPrefix(rti.FullMethodName, "/") - name = strings.Replace(name, "/", ".", -1) - traceContext := md[traceContextKey] - var ( - parent trace.SpanContext - haveParent bool - ) - if len(traceContext) > 0 { - // Metadata with keys ending in -bin are actually binary. They are base64 - // encoded before being put on the wire, see: - // https://github.com/grpc/grpc-go/blob/08d6261/Documentation/grpc-metadata.md#storing-binary-data-in-metadata - traceContextBinary := []byte(traceContext[0]) - parent, haveParent = propagation.FromBinary(traceContextBinary) - if haveParent && !s.IsPublicEndpoint { - ctx, _ := trace.StartSpanWithRemoteParent(ctx, name, parent, - trace.WithSpanKind(trace.SpanKindServer), - trace.WithSampler(s.StartOptions.Sampler), - ) - return ctx - } - } - ctx, span := trace.StartSpan(ctx, name, - trace.WithSpanKind(trace.SpanKindServer), - trace.WithSampler(s.StartOptions.Sampler)) - if haveParent { - span.AddLink(trace.Link{TraceID: parent.TraceID, SpanID: parent.SpanID, Type: trace.LinkTypeChild}) - } - return ctx -} - -func traceHandleRPC(ctx context.Context, rs stats.RPCStats) { - span := trace.FromContext(ctx) - // TODO: compressed and uncompressed sizes are not populated in every message. - switch rs := rs.(type) { - case *stats.Begin: - span.AddAttributes( - trace.BoolAttribute("Client", rs.Client), - trace.BoolAttribute("FailFast", rs.FailFast)) - case *stats.InPayload: - span.AddMessageReceiveEvent(0 /* TODO: messageID */, int64(rs.Length), int64(rs.WireLength)) - case *stats.OutPayload: - span.AddMessageSendEvent(0, int64(rs.Length), int64(rs.WireLength)) - case *stats.End: - if rs.Error != nil { - s, ok := status.FromError(rs.Error) - if ok { - span.SetStatus(trace.Status{Code: int32(s.Code()), Message: s.Message()}) - } else { - span.SetStatus(trace.Status{Code: int32(codes.Internal), Message: rs.Error.Error()}) - } - } - span.End() - } -} diff --git a/vendor/golang.org/x/net/internal/timeseries/timeseries.go b/vendor/golang.org/x/net/internal/timeseries/timeseries.go deleted file mode 100644 index 685f0e7ea23..00000000000 --- a/vendor/golang.org/x/net/internal/timeseries/timeseries.go +++ /dev/null @@ -1,525 +0,0 @@ -// Copyright 2015 The Go 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 timeseries implements a time series structure for stats collection. -package timeseries // import "golang.org/x/net/internal/timeseries" - -import ( - "fmt" - "log" - "time" -) - -const ( - timeSeriesNumBuckets = 64 - minuteHourSeriesNumBuckets = 60 -) - -var timeSeriesResolutions = []time.Duration{ - 1 * time.Second, - 10 * time.Second, - 1 * time.Minute, - 10 * time.Minute, - 1 * time.Hour, - 6 * time.Hour, - 24 * time.Hour, // 1 day - 7 * 24 * time.Hour, // 1 week - 4 * 7 * 24 * time.Hour, // 4 weeks - 16 * 7 * 24 * time.Hour, // 16 weeks -} - -var minuteHourSeriesResolutions = []time.Duration{ - 1 * time.Second, - 1 * time.Minute, -} - -// An Observable is a kind of data that can be aggregated in a time series. -type Observable interface { - Multiply(ratio float64) // Multiplies the data in self by a given ratio - Add(other Observable) // Adds the data from a different observation to self - Clear() // Clears the observation so it can be reused. - CopyFrom(other Observable) // Copies the contents of a given observation to self -} - -// Float attaches the methods of Observable to a float64. -type Float float64 - -// NewFloat returns a Float. -func NewFloat() Observable { - f := Float(0) - return &f -} - -// String returns the float as a string. -func (f *Float) String() string { return fmt.Sprintf("%g", f.Value()) } - -// Value returns the float's value. -func (f *Float) Value() float64 { return float64(*f) } - -func (f *Float) Multiply(ratio float64) { *f *= Float(ratio) } - -func (f *Float) Add(other Observable) { - o := other.(*Float) - *f += *o -} - -func (f *Float) Clear() { *f = 0 } - -func (f *Float) CopyFrom(other Observable) { - o := other.(*Float) - *f = *o -} - -// A Clock tells the current time. -type Clock interface { - Time() time.Time -} - -type defaultClock int - -var defaultClockInstance defaultClock - -func (defaultClock) Time() time.Time { return time.Now() } - -// Information kept per level. Each level consists of a circular list of -// observations. The start of the level may be derived from end and the -// len(buckets) * sizeInMillis. -type tsLevel struct { - oldest int // index to oldest bucketed Observable - newest int // index to newest bucketed Observable - end time.Time // end timestamp for this level - size time.Duration // duration of the bucketed Observable - buckets []Observable // collections of observations - provider func() Observable // used for creating new Observable -} - -func (l *tsLevel) Clear() { - l.oldest = 0 - l.newest = len(l.buckets) - 1 - l.end = time.Time{} - for i := range l.buckets { - if l.buckets[i] != nil { - l.buckets[i].Clear() - l.buckets[i] = nil - } - } -} - -func (l *tsLevel) InitLevel(size time.Duration, numBuckets int, f func() Observable) { - l.size = size - l.provider = f - l.buckets = make([]Observable, numBuckets) -} - -// Keeps a sequence of levels. Each level is responsible for storing data at -// a given resolution. For example, the first level stores data at a one -// minute resolution while the second level stores data at a one hour -// resolution. - -// Each level is represented by a sequence of buckets. Each bucket spans an -// interval equal to the resolution of the level. New observations are added -// to the last bucket. -type timeSeries struct { - provider func() Observable // make more Observable - numBuckets int // number of buckets in each level - levels []*tsLevel // levels of bucketed Observable - lastAdd time.Time // time of last Observable tracked - total Observable // convenient aggregation of all Observable - clock Clock // Clock for getting current time - pending Observable // observations not yet bucketed - pendingTime time.Time // what time are we keeping in pending - dirty bool // if there are pending observations -} - -// init initializes a level according to the supplied criteria. -func (ts *timeSeries) init(resolutions []time.Duration, f func() Observable, numBuckets int, clock Clock) { - ts.provider = f - ts.numBuckets = numBuckets - ts.clock = clock - ts.levels = make([]*tsLevel, len(resolutions)) - - for i := range resolutions { - if i > 0 && resolutions[i-1] >= resolutions[i] { - log.Print("timeseries: resolutions must be monotonically increasing") - break - } - newLevel := new(tsLevel) - newLevel.InitLevel(resolutions[i], ts.numBuckets, ts.provider) - ts.levels[i] = newLevel - } - - ts.Clear() -} - -// Clear removes all observations from the time series. -func (ts *timeSeries) Clear() { - ts.lastAdd = time.Time{} - ts.total = ts.resetObservation(ts.total) - ts.pending = ts.resetObservation(ts.pending) - ts.pendingTime = time.Time{} - ts.dirty = false - - for i := range ts.levels { - ts.levels[i].Clear() - } -} - -// Add records an observation at the current time. -func (ts *timeSeries) Add(observation Observable) { - ts.AddWithTime(observation, ts.clock.Time()) -} - -// AddWithTime records an observation at the specified time. -func (ts *timeSeries) AddWithTime(observation Observable, t time.Time) { - - smallBucketDuration := ts.levels[0].size - - if t.After(ts.lastAdd) { - ts.lastAdd = t - } - - if t.After(ts.pendingTime) { - ts.advance(t) - ts.mergePendingUpdates() - ts.pendingTime = ts.levels[0].end - ts.pending.CopyFrom(observation) - ts.dirty = true - } else if t.After(ts.pendingTime.Add(-1 * smallBucketDuration)) { - // The observation is close enough to go into the pending bucket. - // This compensates for clock skewing and small scheduling delays - // by letting the update stay in the fast path. - ts.pending.Add(observation) - ts.dirty = true - } else { - ts.mergeValue(observation, t) - } -} - -// mergeValue inserts the observation at the specified time in the past into all levels. -func (ts *timeSeries) mergeValue(observation Observable, t time.Time) { - for _, level := range ts.levels { - index := (ts.numBuckets - 1) - int(level.end.Sub(t)/level.size) - if 0 <= index && index < ts.numBuckets { - bucketNumber := (level.oldest + index) % ts.numBuckets - if level.buckets[bucketNumber] == nil { - level.buckets[bucketNumber] = level.provider() - } - level.buckets[bucketNumber].Add(observation) - } - } - ts.total.Add(observation) -} - -// mergePendingUpdates applies the pending updates into all levels. -func (ts *timeSeries) mergePendingUpdates() { - if ts.dirty { - ts.mergeValue(ts.pending, ts.pendingTime) - ts.pending = ts.resetObservation(ts.pending) - ts.dirty = false - } -} - -// advance cycles the buckets at each level until the latest bucket in -// each level can hold the time specified. -func (ts *timeSeries) advance(t time.Time) { - if !t.After(ts.levels[0].end) { - return - } - for i := 0; i < len(ts.levels); i++ { - level := ts.levels[i] - if !level.end.Before(t) { - break - } - - // If the time is sufficiently far, just clear the level and advance - // directly. - if !t.Before(level.end.Add(level.size * time.Duration(ts.numBuckets))) { - for _, b := range level.buckets { - ts.resetObservation(b) - } - level.end = time.Unix(0, (t.UnixNano()/level.size.Nanoseconds())*level.size.Nanoseconds()) - } - - for t.After(level.end) { - level.end = level.end.Add(level.size) - level.newest = level.oldest - level.oldest = (level.oldest + 1) % ts.numBuckets - ts.resetObservation(level.buckets[level.newest]) - } - - t = level.end - } -} - -// Latest returns the sum of the num latest buckets from the level. -func (ts *timeSeries) Latest(level, num int) Observable { - now := ts.clock.Time() - if ts.levels[0].end.Before(now) { - ts.advance(now) - } - - ts.mergePendingUpdates() - - result := ts.provider() - l := ts.levels[level] - index := l.newest - - for i := 0; i < num; i++ { - if l.buckets[index] != nil { - result.Add(l.buckets[index]) - } - if index == 0 { - index = ts.numBuckets - } - index-- - } - - return result -} - -// LatestBuckets returns a copy of the num latest buckets from level. -func (ts *timeSeries) LatestBuckets(level, num int) []Observable { - if level < 0 || level > len(ts.levels) { - log.Print("timeseries: bad level argument: ", level) - return nil - } - if num < 0 || num >= ts.numBuckets { - log.Print("timeseries: bad num argument: ", num) - return nil - } - - results := make([]Observable, num) - now := ts.clock.Time() - if ts.levels[0].end.Before(now) { - ts.advance(now) - } - - ts.mergePendingUpdates() - - l := ts.levels[level] - index := l.newest - - for i := 0; i < num; i++ { - result := ts.provider() - results[i] = result - if l.buckets[index] != nil { - result.CopyFrom(l.buckets[index]) - } - - if index == 0 { - index = ts.numBuckets - } - index -= 1 - } - return results -} - -// ScaleBy updates observations by scaling by factor. -func (ts *timeSeries) ScaleBy(factor float64) { - for _, l := range ts.levels { - for i := 0; i < ts.numBuckets; i++ { - l.buckets[i].Multiply(factor) - } - } - - ts.total.Multiply(factor) - ts.pending.Multiply(factor) -} - -// Range returns the sum of observations added over the specified time range. -// If start or finish times don't fall on bucket boundaries of the same -// level, then return values are approximate answers. -func (ts *timeSeries) Range(start, finish time.Time) Observable { - return ts.ComputeRange(start, finish, 1)[0] -} - -// Recent returns the sum of observations from the last delta. -func (ts *timeSeries) Recent(delta time.Duration) Observable { - now := ts.clock.Time() - return ts.Range(now.Add(-delta), now) -} - -// Total returns the total of all observations. -func (ts *timeSeries) Total() Observable { - ts.mergePendingUpdates() - return ts.total -} - -// ComputeRange computes a specified number of values into a slice using -// the observations recorded over the specified time period. The return -// values are approximate if the start or finish times don't fall on the -// bucket boundaries at the same level or if the number of buckets spanning -// the range is not an integral multiple of num. -func (ts *timeSeries) ComputeRange(start, finish time.Time, num int) []Observable { - if start.After(finish) { - log.Printf("timeseries: start > finish, %v>%v", start, finish) - return nil - } - - if num < 0 { - log.Printf("timeseries: num < 0, %v", num) - return nil - } - - results := make([]Observable, num) - - for _, l := range ts.levels { - if !start.Before(l.end.Add(-l.size * time.Duration(ts.numBuckets))) { - ts.extract(l, start, finish, num, results) - return results - } - } - - // Failed to find a level that covers the desired range. So just - // extract from the last level, even if it doesn't cover the entire - // desired range. - ts.extract(ts.levels[len(ts.levels)-1], start, finish, num, results) - - return results -} - -// RecentList returns the specified number of values in slice over the most -// recent time period of the specified range. -func (ts *timeSeries) RecentList(delta time.Duration, num int) []Observable { - if delta < 0 { - return nil - } - now := ts.clock.Time() - return ts.ComputeRange(now.Add(-delta), now, num) -} - -// extract returns a slice of specified number of observations from a given -// level over a given range. -func (ts *timeSeries) extract(l *tsLevel, start, finish time.Time, num int, results []Observable) { - ts.mergePendingUpdates() - - srcInterval := l.size - dstInterval := finish.Sub(start) / time.Duration(num) - dstStart := start - srcStart := l.end.Add(-srcInterval * time.Duration(ts.numBuckets)) - - srcIndex := 0 - - // Where should scanning start? - if dstStart.After(srcStart) { - advance := dstStart.Sub(srcStart) / srcInterval - srcIndex += int(advance) - srcStart = srcStart.Add(advance * srcInterval) - } - - // The i'th value is computed as show below. - // interval = (finish/start)/num - // i'th value = sum of observation in range - // [ start + i * interval, - // start + (i + 1) * interval ) - for i := 0; i < num; i++ { - results[i] = ts.resetObservation(results[i]) - dstEnd := dstStart.Add(dstInterval) - for srcIndex < ts.numBuckets && srcStart.Before(dstEnd) { - srcEnd := srcStart.Add(srcInterval) - if srcEnd.After(ts.lastAdd) { - srcEnd = ts.lastAdd - } - - if !srcEnd.Before(dstStart) { - srcValue := l.buckets[(srcIndex+l.oldest)%ts.numBuckets] - if !srcStart.Before(dstStart) && !srcEnd.After(dstEnd) { - // dst completely contains src. - if srcValue != nil { - results[i].Add(srcValue) - } - } else { - // dst partially overlaps src. - overlapStart := maxTime(srcStart, dstStart) - overlapEnd := minTime(srcEnd, dstEnd) - base := srcEnd.Sub(srcStart) - fraction := overlapEnd.Sub(overlapStart).Seconds() / base.Seconds() - - used := ts.provider() - if srcValue != nil { - used.CopyFrom(srcValue) - } - used.Multiply(fraction) - results[i].Add(used) - } - - if srcEnd.After(dstEnd) { - break - } - } - srcIndex++ - srcStart = srcStart.Add(srcInterval) - } - dstStart = dstStart.Add(dstInterval) - } -} - -// resetObservation clears the content so the struct may be reused. -func (ts *timeSeries) resetObservation(observation Observable) Observable { - if observation == nil { - observation = ts.provider() - } else { - observation.Clear() - } - return observation -} - -// TimeSeries tracks data at granularities from 1 second to 16 weeks. -type TimeSeries struct { - timeSeries -} - -// NewTimeSeries creates a new TimeSeries using the function provided for creating new Observable. -func NewTimeSeries(f func() Observable) *TimeSeries { - return NewTimeSeriesWithClock(f, defaultClockInstance) -} - -// NewTimeSeriesWithClock creates a new TimeSeries using the function provided for creating new Observable and the clock for -// assigning timestamps. -func NewTimeSeriesWithClock(f func() Observable, clock Clock) *TimeSeries { - ts := new(TimeSeries) - ts.timeSeries.init(timeSeriesResolutions, f, timeSeriesNumBuckets, clock) - return ts -} - -// MinuteHourSeries tracks data at granularities of 1 minute and 1 hour. -type MinuteHourSeries struct { - timeSeries -} - -// NewMinuteHourSeries creates a new MinuteHourSeries using the function provided for creating new Observable. -func NewMinuteHourSeries(f func() Observable) *MinuteHourSeries { - return NewMinuteHourSeriesWithClock(f, defaultClockInstance) -} - -// NewMinuteHourSeriesWithClock creates a new MinuteHourSeries using the function provided for creating new Observable and the clock for -// assigning timestamps. -func NewMinuteHourSeriesWithClock(f func() Observable, clock Clock) *MinuteHourSeries { - ts := new(MinuteHourSeries) - ts.timeSeries.init(minuteHourSeriesResolutions, f, - minuteHourSeriesNumBuckets, clock) - return ts -} - -func (ts *MinuteHourSeries) Minute() Observable { - return ts.timeSeries.Latest(0, 60) -} - -func (ts *MinuteHourSeries) Hour() Observable { - return ts.timeSeries.Latest(1, 60) -} - -func minTime(a, b time.Time) time.Time { - if a.Before(b) { - return a - } - return b -} - -func maxTime(a, b time.Time) time.Time { - if a.After(b) { - return a - } - return b -} diff --git a/vendor/golang.org/x/net/trace/events.go b/vendor/golang.org/x/net/trace/events.go deleted file mode 100644 index c646a6952e5..00000000000 --- a/vendor/golang.org/x/net/trace/events.go +++ /dev/null @@ -1,532 +0,0 @@ -// Copyright 2015 The Go 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 trace - -import ( - "bytes" - "fmt" - "html/template" - "io" - "log" - "net/http" - "runtime" - "sort" - "strconv" - "strings" - "sync" - "sync/atomic" - "text/tabwriter" - "time" -) - -const maxEventsPerLog = 100 - -type bucket struct { - MaxErrAge time.Duration - String string -} - -var buckets = []bucket{ - {0, "total"}, - {10 * time.Second, "errs<10s"}, - {1 * time.Minute, "errs<1m"}, - {10 * time.Minute, "errs<10m"}, - {1 * time.Hour, "errs<1h"}, - {10 * time.Hour, "errs<10h"}, - {24000 * time.Hour, "errors"}, -} - -// RenderEvents renders the HTML page typically served at /debug/events. -// It does not do any auth checking. The request may be nil. -// -// Most users will use the Events handler. -func RenderEvents(w http.ResponseWriter, req *http.Request, sensitive bool) { - now := time.Now() - data := &struct { - Families []string // family names - Buckets []bucket - Counts [][]int // eventLog count per family/bucket - - // Set when a bucket has been selected. - Family string - Bucket int - EventLogs eventLogs - Expanded bool - }{ - Buckets: buckets, - } - - data.Families = make([]string, 0, len(families)) - famMu.RLock() - for name := range families { - data.Families = append(data.Families, name) - } - famMu.RUnlock() - sort.Strings(data.Families) - - // Count the number of eventLogs in each family for each error age. - data.Counts = make([][]int, len(data.Families)) - for i, name := range data.Families { - // TODO(sameer): move this loop under the family lock. - f := getEventFamily(name) - data.Counts[i] = make([]int, len(data.Buckets)) - for j, b := range data.Buckets { - data.Counts[i][j] = f.Count(now, b.MaxErrAge) - } - } - - if req != nil { - var ok bool - data.Family, data.Bucket, ok = parseEventsArgs(req) - if !ok { - // No-op - } else { - data.EventLogs = getEventFamily(data.Family).Copy(now, buckets[data.Bucket].MaxErrAge) - } - if data.EventLogs != nil { - defer data.EventLogs.Free() - sort.Sort(data.EventLogs) - } - if exp, err := strconv.ParseBool(req.FormValue("exp")); err == nil { - data.Expanded = exp - } - } - - famMu.RLock() - defer famMu.RUnlock() - if err := eventsTmpl().Execute(w, data); err != nil { - log.Printf("net/trace: Failed executing template: %v", err) - } -} - -func parseEventsArgs(req *http.Request) (fam string, b int, ok bool) { - fam, bStr := req.FormValue("fam"), req.FormValue("b") - if fam == "" || bStr == "" { - return "", 0, false - } - b, err := strconv.Atoi(bStr) - if err != nil || b < 0 || b >= len(buckets) { - return "", 0, false - } - return fam, b, true -} - -// An EventLog provides a log of events associated with a specific object. -type EventLog interface { - // Printf formats its arguments with fmt.Sprintf and adds the - // result to the event log. - Printf(format string, a ...interface{}) - - // Errorf is like Printf, but it marks this event as an error. - Errorf(format string, a ...interface{}) - - // Finish declares that this event log is complete. - // The event log should not be used after calling this method. - Finish() -} - -// NewEventLog returns a new EventLog with the specified family name -// and title. -func NewEventLog(family, title string) EventLog { - el := newEventLog() - el.ref() - el.Family, el.Title = family, title - el.Start = time.Now() - el.events = make([]logEntry, 0, maxEventsPerLog) - el.stack = make([]uintptr, 32) - n := runtime.Callers(2, el.stack) - el.stack = el.stack[:n] - - getEventFamily(family).add(el) - return el -} - -func (el *eventLog) Finish() { - getEventFamily(el.Family).remove(el) - el.unref() // matches ref in New -} - -var ( - famMu sync.RWMutex - families = make(map[string]*eventFamily) // family name => family -) - -func getEventFamily(fam string) *eventFamily { - famMu.Lock() - defer famMu.Unlock() - f := families[fam] - if f == nil { - f = &eventFamily{} - families[fam] = f - } - return f -} - -type eventFamily struct { - mu sync.RWMutex - eventLogs eventLogs -} - -func (f *eventFamily) add(el *eventLog) { - f.mu.Lock() - f.eventLogs = append(f.eventLogs, el) - f.mu.Unlock() -} - -func (f *eventFamily) remove(el *eventLog) { - f.mu.Lock() - defer f.mu.Unlock() - for i, el0 := range f.eventLogs { - if el == el0 { - copy(f.eventLogs[i:], f.eventLogs[i+1:]) - f.eventLogs = f.eventLogs[:len(f.eventLogs)-1] - return - } - } -} - -func (f *eventFamily) Count(now time.Time, maxErrAge time.Duration) (n int) { - f.mu.RLock() - defer f.mu.RUnlock() - for _, el := range f.eventLogs { - if el.hasRecentError(now, maxErrAge) { - n++ - } - } - return -} - -func (f *eventFamily) Copy(now time.Time, maxErrAge time.Duration) (els eventLogs) { - f.mu.RLock() - defer f.mu.RUnlock() - els = make(eventLogs, 0, len(f.eventLogs)) - for _, el := range f.eventLogs { - if el.hasRecentError(now, maxErrAge) { - el.ref() - els = append(els, el) - } - } - return -} - -type eventLogs []*eventLog - -// Free calls unref on each element of the list. -func (els eventLogs) Free() { - for _, el := range els { - el.unref() - } -} - -// eventLogs may be sorted in reverse chronological order. -func (els eventLogs) Len() int { return len(els) } -func (els eventLogs) Less(i, j int) bool { return els[i].Start.After(els[j].Start) } -func (els eventLogs) Swap(i, j int) { els[i], els[j] = els[j], els[i] } - -// A logEntry is a timestamped log entry in an event log. -type logEntry struct { - When time.Time - Elapsed time.Duration // since previous event in log - NewDay bool // whether this event is on a different day to the previous event - What string - IsErr bool -} - -// WhenString returns a string representation of the elapsed time of the event. -// It will include the date if midnight was crossed. -func (e logEntry) WhenString() string { - if e.NewDay { - return e.When.Format("2006/01/02 15:04:05.000000") - } - return e.When.Format("15:04:05.000000") -} - -// An eventLog represents an active event log. -type eventLog struct { - // Family is the top-level grouping of event logs to which this belongs. - Family string - - // Title is the title of this event log. - Title string - - // Timing information. - Start time.Time - - // Call stack where this event log was created. - stack []uintptr - - // Append-only sequence of events. - // - // TODO(sameer): change this to a ring buffer to avoid the array copy - // when we hit maxEventsPerLog. - mu sync.RWMutex - events []logEntry - LastErrorTime time.Time - discarded int - - refs int32 // how many buckets this is in -} - -func (el *eventLog) reset() { - // Clear all but the mutex. Mutexes may not be copied, even when unlocked. - el.Family = "" - el.Title = "" - el.Start = time.Time{} - el.stack = nil - el.events = nil - el.LastErrorTime = time.Time{} - el.discarded = 0 - el.refs = 0 -} - -func (el *eventLog) hasRecentError(now time.Time, maxErrAge time.Duration) bool { - if maxErrAge == 0 { - return true - } - el.mu.RLock() - defer el.mu.RUnlock() - return now.Sub(el.LastErrorTime) < maxErrAge -} - -// delta returns the elapsed time since the last event or the log start, -// and whether it spans midnight. -// L >= el.mu -func (el *eventLog) delta(t time.Time) (time.Duration, bool) { - if len(el.events) == 0 { - return t.Sub(el.Start), false - } - prev := el.events[len(el.events)-1].When - return t.Sub(prev), prev.Day() != t.Day() - -} - -func (el *eventLog) Printf(format string, a ...interface{}) { - el.printf(false, format, a...) -} - -func (el *eventLog) Errorf(format string, a ...interface{}) { - el.printf(true, format, a...) -} - -func (el *eventLog) printf(isErr bool, format string, a ...interface{}) { - e := logEntry{When: time.Now(), IsErr: isErr, What: fmt.Sprintf(format, a...)} - el.mu.Lock() - e.Elapsed, e.NewDay = el.delta(e.When) - if len(el.events) < maxEventsPerLog { - el.events = append(el.events, e) - } else { - // Discard the oldest event. - if el.discarded == 0 { - // el.discarded starts at two to count for the event it - // is replacing, plus the next one that we are about to - // drop. - el.discarded = 2 - } else { - el.discarded++ - } - // TODO(sameer): if this causes allocations on a critical path, - // change eventLog.What to be a fmt.Stringer, as in trace.go. - el.events[0].What = fmt.Sprintf("(%d events discarded)", el.discarded) - // The timestamp of the discarded meta-event should be - // the time of the last event it is representing. - el.events[0].When = el.events[1].When - copy(el.events[1:], el.events[2:]) - el.events[maxEventsPerLog-1] = e - } - if e.IsErr { - el.LastErrorTime = e.When - } - el.mu.Unlock() -} - -func (el *eventLog) ref() { - atomic.AddInt32(&el.refs, 1) -} - -func (el *eventLog) unref() { - if atomic.AddInt32(&el.refs, -1) == 0 { - freeEventLog(el) - } -} - -func (el *eventLog) When() string { - return el.Start.Format("2006/01/02 15:04:05.000000") -} - -func (el *eventLog) ElapsedTime() string { - elapsed := time.Since(el.Start) - return fmt.Sprintf("%.6f", elapsed.Seconds()) -} - -func (el *eventLog) Stack() string { - buf := new(bytes.Buffer) - tw := tabwriter.NewWriter(buf, 1, 8, 1, '\t', 0) - printStackRecord(tw, el.stack) - tw.Flush() - return buf.String() -} - -// printStackRecord prints the function + source line information -// for a single stack trace. -// Adapted from runtime/pprof/pprof.go. -func printStackRecord(w io.Writer, stk []uintptr) { - for _, pc := range stk { - f := runtime.FuncForPC(pc) - if f == nil { - continue - } - file, line := f.FileLine(pc) - name := f.Name() - // Hide runtime.goexit and any runtime functions at the beginning. - if strings.HasPrefix(name, "runtime.") { - continue - } - fmt.Fprintf(w, "# %s\t%s:%d\n", name, file, line) - } -} - -func (el *eventLog) Events() []logEntry { - el.mu.RLock() - defer el.mu.RUnlock() - return el.events -} - -// freeEventLogs is a freelist of *eventLog -var freeEventLogs = make(chan *eventLog, 1000) - -// newEventLog returns a event log ready to use. -func newEventLog() *eventLog { - select { - case el := <-freeEventLogs: - return el - default: - return new(eventLog) - } -} - -// freeEventLog adds el to freeEventLogs if there's room. -// This is non-blocking. -func freeEventLog(el *eventLog) { - el.reset() - select { - case freeEventLogs <- el: - default: - } -} - -var eventsTmplCache *template.Template -var eventsTmplOnce sync.Once - -func eventsTmpl() *template.Template { - eventsTmplOnce.Do(func() { - eventsTmplCache = template.Must(template.New("events").Funcs(template.FuncMap{ - "elapsed": elapsed, - "trimSpace": strings.TrimSpace, - }).Parse(eventsHTML)) - }) - return eventsTmplCache -} - -const eventsHTML = ` - - - events - - - - -

/debug/events

- - - {{range $i, $fam := .Families}} - - - - {{range $j, $bucket := $.Buckets}} - {{$n := index $.Counts $i $j}} - - {{end}} - - {{end}} -
{{$fam}} - {{if $n}}{{end}} - [{{$n}} {{$bucket.String}}] - {{if $n}}{{end}} -
- -{{if $.EventLogs}} -
-

Family: {{$.Family}}

- -{{if $.Expanded}}{{end}} -[Summary]{{if $.Expanded}}{{end}} - -{{if not $.Expanded}}{{end}} -[Expanded]{{if not $.Expanded}}{{end}} - - - - {{range $el := $.EventLogs}} - - - - - {{if $.Expanded}} - - - - - - {{range $el.Events}} - - - - - - {{end}} - {{end}} - {{end}} -
WhenElapsed
{{$el.When}}{{$el.ElapsedTime}}{{$el.Title}} -
{{$el.Stack|trimSpace}}
{{.WhenString}}{{elapsed .Elapsed}}.{{if .IsErr}}E{{else}}.{{end}}. {{.What}}
-{{end}} - - -` diff --git a/vendor/golang.org/x/net/trace/histogram.go b/vendor/golang.org/x/net/trace/histogram.go deleted file mode 100644 index 9bf4286c794..00000000000 --- a/vendor/golang.org/x/net/trace/histogram.go +++ /dev/null @@ -1,365 +0,0 @@ -// Copyright 2015 The Go 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 trace - -// This file implements histogramming for RPC statistics collection. - -import ( - "bytes" - "fmt" - "html/template" - "log" - "math" - "sync" - - "golang.org/x/net/internal/timeseries" -) - -const ( - bucketCount = 38 -) - -// histogram keeps counts of values in buckets that are spaced -// out in powers of 2: 0-1, 2-3, 4-7... -// histogram implements timeseries.Observable -type histogram struct { - sum int64 // running total of measurements - sumOfSquares float64 // square of running total - buckets []int64 // bucketed values for histogram - value int // holds a single value as an optimization - valueCount int64 // number of values recorded for single value -} - -// AddMeasurement records a value measurement observation to the histogram. -func (h *histogram) addMeasurement(value int64) { - // TODO: assert invariant - h.sum += value - h.sumOfSquares += float64(value) * float64(value) - - bucketIndex := getBucket(value) - - if h.valueCount == 0 || (h.valueCount > 0 && h.value == bucketIndex) { - h.value = bucketIndex - h.valueCount++ - } else { - h.allocateBuckets() - h.buckets[bucketIndex]++ - } -} - -func (h *histogram) allocateBuckets() { - if h.buckets == nil { - h.buckets = make([]int64, bucketCount) - h.buckets[h.value] = h.valueCount - h.value = 0 - h.valueCount = -1 - } -} - -func log2(i int64) int { - n := 0 - for ; i >= 0x100; i >>= 8 { - n += 8 - } - for ; i > 0; i >>= 1 { - n += 1 - } - return n -} - -func getBucket(i int64) (index int) { - index = log2(i) - 1 - if index < 0 { - index = 0 - } - if index >= bucketCount { - index = bucketCount - 1 - } - return -} - -// Total returns the number of recorded observations. -func (h *histogram) total() (total int64) { - if h.valueCount >= 0 { - total = h.valueCount - } - for _, val := range h.buckets { - total += int64(val) - } - return -} - -// Average returns the average value of recorded observations. -func (h *histogram) average() float64 { - t := h.total() - if t == 0 { - return 0 - } - return float64(h.sum) / float64(t) -} - -// Variance returns the variance of recorded observations. -func (h *histogram) variance() float64 { - t := float64(h.total()) - if t == 0 { - return 0 - } - s := float64(h.sum) / t - return h.sumOfSquares/t - s*s -} - -// StandardDeviation returns the standard deviation of recorded observations. -func (h *histogram) standardDeviation() float64 { - return math.Sqrt(h.variance()) -} - -// PercentileBoundary estimates the value that the given fraction of recorded -// observations are less than. -func (h *histogram) percentileBoundary(percentile float64) int64 { - total := h.total() - - // Corner cases (make sure result is strictly less than Total()) - if total == 0 { - return 0 - } else if total == 1 { - return int64(h.average()) - } - - percentOfTotal := round(float64(total) * percentile) - var runningTotal int64 - - for i := range h.buckets { - value := h.buckets[i] - runningTotal += value - if runningTotal == percentOfTotal { - // We hit an exact bucket boundary. If the next bucket has data, it is a - // good estimate of the value. If the bucket is empty, we interpolate the - // midpoint between the next bucket's boundary and the next non-zero - // bucket. If the remaining buckets are all empty, then we use the - // boundary for the next bucket as the estimate. - j := uint8(i + 1) - min := bucketBoundary(j) - if runningTotal < total { - for h.buckets[j] == 0 { - j++ - } - } - max := bucketBoundary(j) - return min + round(float64(max-min)/2) - } else if runningTotal > percentOfTotal { - // The value is in this bucket. Interpolate the value. - delta := runningTotal - percentOfTotal - percentBucket := float64(value-delta) / float64(value) - bucketMin := bucketBoundary(uint8(i)) - nextBucketMin := bucketBoundary(uint8(i + 1)) - bucketSize := nextBucketMin - bucketMin - return bucketMin + round(percentBucket*float64(bucketSize)) - } - } - return bucketBoundary(bucketCount - 1) -} - -// Median returns the estimated median of the observed values. -func (h *histogram) median() int64 { - return h.percentileBoundary(0.5) -} - -// Add adds other to h. -func (h *histogram) Add(other timeseries.Observable) { - o := other.(*histogram) - if o.valueCount == 0 { - // Other histogram is empty - } else if h.valueCount >= 0 && o.valueCount > 0 && h.value == o.value { - // Both have a single bucketed value, aggregate them - h.valueCount += o.valueCount - } else { - // Two different values necessitate buckets in this histogram - h.allocateBuckets() - if o.valueCount >= 0 { - h.buckets[o.value] += o.valueCount - } else { - for i := range h.buckets { - h.buckets[i] += o.buckets[i] - } - } - } - h.sumOfSquares += o.sumOfSquares - h.sum += o.sum -} - -// Clear resets the histogram to an empty state, removing all observed values. -func (h *histogram) Clear() { - h.buckets = nil - h.value = 0 - h.valueCount = 0 - h.sum = 0 - h.sumOfSquares = 0 -} - -// CopyFrom copies from other, which must be a *histogram, into h. -func (h *histogram) CopyFrom(other timeseries.Observable) { - o := other.(*histogram) - if o.valueCount == -1 { - h.allocateBuckets() - copy(h.buckets, o.buckets) - } - h.sum = o.sum - h.sumOfSquares = o.sumOfSquares - h.value = o.value - h.valueCount = o.valueCount -} - -// Multiply scales the histogram by the specified ratio. -func (h *histogram) Multiply(ratio float64) { - if h.valueCount == -1 { - for i := range h.buckets { - h.buckets[i] = int64(float64(h.buckets[i]) * ratio) - } - } else { - h.valueCount = int64(float64(h.valueCount) * ratio) - } - h.sum = int64(float64(h.sum) * ratio) - h.sumOfSquares = h.sumOfSquares * ratio -} - -// New creates a new histogram. -func (h *histogram) New() timeseries.Observable { - r := new(histogram) - r.Clear() - return r -} - -func (h *histogram) String() string { - return fmt.Sprintf("%d, %f, %d, %d, %v", - h.sum, h.sumOfSquares, h.value, h.valueCount, h.buckets) -} - -// round returns the closest int64 to the argument -func round(in float64) int64 { - return int64(math.Floor(in + 0.5)) -} - -// bucketBoundary returns the first value in the bucket. -func bucketBoundary(bucket uint8) int64 { - if bucket == 0 { - return 0 - } - return 1 << bucket -} - -// bucketData holds data about a specific bucket for use in distTmpl. -type bucketData struct { - Lower, Upper int64 - N int64 - Pct, CumulativePct float64 - GraphWidth int -} - -// data holds data about a Distribution for use in distTmpl. -type data struct { - Buckets []*bucketData - Count, Median int64 - Mean, StandardDeviation float64 -} - -// maxHTMLBarWidth is the maximum width of the HTML bar for visualizing buckets. -const maxHTMLBarWidth = 350.0 - -// newData returns data representing h for use in distTmpl. -func (h *histogram) newData() *data { - // Force the allocation of buckets to simplify the rendering implementation - h.allocateBuckets() - // We scale the bars on the right so that the largest bar is - // maxHTMLBarWidth pixels in width. - maxBucket := int64(0) - for _, n := range h.buckets { - if n > maxBucket { - maxBucket = n - } - } - total := h.total() - barsizeMult := maxHTMLBarWidth / float64(maxBucket) - var pctMult float64 - if total == 0 { - pctMult = 1.0 - } else { - pctMult = 100.0 / float64(total) - } - - buckets := make([]*bucketData, len(h.buckets)) - runningTotal := int64(0) - for i, n := range h.buckets { - if n == 0 { - continue - } - runningTotal += n - var upperBound int64 - if i < bucketCount-1 { - upperBound = bucketBoundary(uint8(i + 1)) - } else { - upperBound = math.MaxInt64 - } - buckets[i] = &bucketData{ - Lower: bucketBoundary(uint8(i)), - Upper: upperBound, - N: n, - Pct: float64(n) * pctMult, - CumulativePct: float64(runningTotal) * pctMult, - GraphWidth: int(float64(n) * barsizeMult), - } - } - return &data{ - Buckets: buckets, - Count: total, - Median: h.median(), - Mean: h.average(), - StandardDeviation: h.standardDeviation(), - } -} - -func (h *histogram) html() template.HTML { - buf := new(bytes.Buffer) - if err := distTmpl().Execute(buf, h.newData()); err != nil { - buf.Reset() - log.Printf("net/trace: couldn't execute template: %v", err) - } - return template.HTML(buf.String()) -} - -var distTmplCache *template.Template -var distTmplOnce sync.Once - -func distTmpl() *template.Template { - distTmplOnce.Do(func() { - // Input: data - distTmplCache = template.Must(template.New("distTmpl").Parse(` - - - - - - - -
Count: {{.Count}}Mean: {{printf "%.0f" .Mean}}StdDev: {{printf "%.0f" .StandardDeviation}}Median: {{.Median}}
-
- -{{range $b := .Buckets}} -{{if $b}} - - - - - - - - - -{{end}} -{{end}} -
[{{.Lower}},{{.Upper}}){{.N}}{{printf "%#.3f" .Pct}}%{{printf "%#.3f" .CumulativePct}}%
-`)) - }) - return distTmplCache -} diff --git a/vendor/golang.org/x/net/trace/trace.go b/vendor/golang.org/x/net/trace/trace.go deleted file mode 100644 index a46ee0eaa31..00000000000 --- a/vendor/golang.org/x/net/trace/trace.go +++ /dev/null @@ -1,1103 +0,0 @@ -// Copyright 2015 The Go 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 trace implements tracing of requests and long-lived objects. -It exports HTTP interfaces on /debug/requests and /debug/events. - -A trace.Trace provides tracing for short-lived objects, usually requests. -A request handler might be implemented like this: - - func fooHandler(w http.ResponseWriter, req *http.Request) { - tr := trace.New("mypkg.Foo", req.URL.Path) - defer tr.Finish() - ... - tr.LazyPrintf("some event %q happened", str) - ... - if err := somethingImportant(); err != nil { - tr.LazyPrintf("somethingImportant failed: %v", err) - tr.SetError() - } - } - -The /debug/requests HTTP endpoint organizes the traces by family, -errors, and duration. It also provides histogram of request duration -for each family. - -A trace.EventLog provides tracing for long-lived objects, such as RPC -connections. - - // A Fetcher fetches URL paths for a single domain. - type Fetcher struct { - domain string - events trace.EventLog - } - - func NewFetcher(domain string) *Fetcher { - return &Fetcher{ - domain, - trace.NewEventLog("mypkg.Fetcher", domain), - } - } - - func (f *Fetcher) Fetch(path string) (string, error) { - resp, err := http.Get("http://" + f.domain + "/" + path) - if err != nil { - f.events.Errorf("Get(%q) = %v", path, err) - return "", err - } - f.events.Printf("Get(%q) = %s", path, resp.Status) - ... - } - - func (f *Fetcher) Close() error { - f.events.Finish() - return nil - } - -The /debug/events HTTP endpoint organizes the event logs by family and -by time since the last error. The expanded view displays recent log -entries and the log's call stack. -*/ -package trace // import "golang.org/x/net/trace" - -import ( - "bytes" - "fmt" - "html/template" - "io" - "log" - "net" - "net/http" - "runtime" - "sort" - "strconv" - "sync" - "sync/atomic" - "time" - - "golang.org/x/net/internal/timeseries" -) - -// DebugUseAfterFinish controls whether to debug uses of Trace values after finishing. -// FOR DEBUGGING ONLY. This will slow down the program. -var DebugUseAfterFinish = false - -// AuthRequest determines whether a specific request is permitted to load the -// /debug/requests or /debug/events pages. -// -// It returns two bools; the first indicates whether the page may be viewed at all, -// and the second indicates whether sensitive events will be shown. -// -// AuthRequest may be replaced by a program to customize its authorization requirements. -// -// The default AuthRequest function returns (true, true) if and only if the request -// comes from localhost/127.0.0.1/[::1]. -var AuthRequest = func(req *http.Request) (any, sensitive bool) { - // RemoteAddr is commonly in the form "IP" or "IP:port". - // If it is in the form "IP:port", split off the port. - host, _, err := net.SplitHostPort(req.RemoteAddr) - if err != nil { - host = req.RemoteAddr - } - switch host { - case "localhost", "127.0.0.1", "::1": - return true, true - default: - return false, false - } -} - -func init() { - // TODO(jbd): Serve Traces from /debug/traces in the future? - // There is no requirement for a request to be present to have traces. - http.HandleFunc("/debug/requests", Traces) - http.HandleFunc("/debug/events", Events) -} - -// Traces responds with traces from the program. -// The package initialization registers it in http.DefaultServeMux -// at /debug/requests. -// -// It performs authorization by running AuthRequest. -func Traces(w http.ResponseWriter, req *http.Request) { - any, sensitive := AuthRequest(req) - if !any { - http.Error(w, "not allowed", http.StatusUnauthorized) - return - } - w.Header().Set("Content-Type", "text/html; charset=utf-8") - Render(w, req, sensitive) -} - -// Events responds with a page of events collected by EventLogs. -// The package initialization registers it in http.DefaultServeMux -// at /debug/events. -// -// It performs authorization by running AuthRequest. -func Events(w http.ResponseWriter, req *http.Request) { - any, sensitive := AuthRequest(req) - if !any { - http.Error(w, "not allowed", http.StatusUnauthorized) - return - } - w.Header().Set("Content-Type", "text/html; charset=utf-8") - RenderEvents(w, req, sensitive) -} - -// Render renders the HTML page typically served at /debug/requests. -// It does not do any auth checking. The request may be nil. -// -// Most users will use the Traces handler. -func Render(w io.Writer, req *http.Request, sensitive bool) { - data := &struct { - Families []string - ActiveTraceCount map[string]int - CompletedTraces map[string]*family - - // Set when a bucket has been selected. - Traces traceList - Family string - Bucket int - Expanded bool - Traced bool - Active bool - ShowSensitive bool // whether to show sensitive events - - Histogram template.HTML - HistogramWindow string // e.g. "last minute", "last hour", "all time" - - // If non-zero, the set of traces is a partial set, - // and this is the total number. - Total int - }{ - CompletedTraces: completedTraces, - } - - data.ShowSensitive = sensitive - if req != nil { - // Allow show_sensitive=0 to force hiding of sensitive data for testing. - // This only goes one way; you can't use show_sensitive=1 to see things. - if req.FormValue("show_sensitive") == "0" { - data.ShowSensitive = false - } - - if exp, err := strconv.ParseBool(req.FormValue("exp")); err == nil { - data.Expanded = exp - } - if exp, err := strconv.ParseBool(req.FormValue("rtraced")); err == nil { - data.Traced = exp - } - } - - completedMu.RLock() - data.Families = make([]string, 0, len(completedTraces)) - for fam := range completedTraces { - data.Families = append(data.Families, fam) - } - completedMu.RUnlock() - sort.Strings(data.Families) - - // We are careful here to minimize the time spent locking activeMu, - // since that lock is required every time an RPC starts and finishes. - data.ActiveTraceCount = make(map[string]int, len(data.Families)) - activeMu.RLock() - for fam, s := range activeTraces { - data.ActiveTraceCount[fam] = s.Len() - } - activeMu.RUnlock() - - var ok bool - data.Family, data.Bucket, ok = parseArgs(req) - switch { - case !ok: - // No-op - case data.Bucket == -1: - data.Active = true - n := data.ActiveTraceCount[data.Family] - data.Traces = getActiveTraces(data.Family) - if len(data.Traces) < n { - data.Total = n - } - case data.Bucket < bucketsPerFamily: - if b := lookupBucket(data.Family, data.Bucket); b != nil { - data.Traces = b.Copy(data.Traced) - } - default: - if f := getFamily(data.Family, false); f != nil { - var obs timeseries.Observable - f.LatencyMu.RLock() - switch o := data.Bucket - bucketsPerFamily; o { - case 0: - obs = f.Latency.Minute() - data.HistogramWindow = "last minute" - case 1: - obs = f.Latency.Hour() - data.HistogramWindow = "last hour" - case 2: - obs = f.Latency.Total() - data.HistogramWindow = "all time" - } - f.LatencyMu.RUnlock() - if obs != nil { - data.Histogram = obs.(*histogram).html() - } - } - } - - if data.Traces != nil { - defer data.Traces.Free() - sort.Sort(data.Traces) - } - - completedMu.RLock() - defer completedMu.RUnlock() - if err := pageTmpl().ExecuteTemplate(w, "Page", data); err != nil { - log.Printf("net/trace: Failed executing template: %v", err) - } -} - -func parseArgs(req *http.Request) (fam string, b int, ok bool) { - if req == nil { - return "", 0, false - } - fam, bStr := req.FormValue("fam"), req.FormValue("b") - if fam == "" || bStr == "" { - return "", 0, false - } - b, err := strconv.Atoi(bStr) - if err != nil || b < -1 { - return "", 0, false - } - - return fam, b, true -} - -func lookupBucket(fam string, b int) *traceBucket { - f := getFamily(fam, false) - if f == nil || b < 0 || b >= len(f.Buckets) { - return nil - } - return f.Buckets[b] -} - -type contextKeyT string - -var contextKey = contextKeyT("golang.org/x/net/trace.Trace") - -// Trace represents an active request. -type Trace interface { - // LazyLog adds x to the event log. It will be evaluated each time the - // /debug/requests page is rendered. Any memory referenced by x will be - // pinned until the trace is finished and later discarded. - LazyLog(x fmt.Stringer, sensitive bool) - - // LazyPrintf evaluates its arguments with fmt.Sprintf each time the - // /debug/requests page is rendered. Any memory referenced by a will be - // pinned until the trace is finished and later discarded. - LazyPrintf(format string, a ...interface{}) - - // SetError declares that this trace resulted in an error. - SetError() - - // SetRecycler sets a recycler for the trace. - // f will be called for each event passed to LazyLog at a time when - // it is no longer required, whether while the trace is still active - // and the event is discarded, or when a completed trace is discarded. - SetRecycler(f func(interface{})) - - // SetTraceInfo sets the trace info for the trace. - // This is currently unused. - SetTraceInfo(traceID, spanID uint64) - - // SetMaxEvents sets the maximum number of events that will be stored - // in the trace. This has no effect if any events have already been - // added to the trace. - SetMaxEvents(m int) - - // Finish declares that this trace is complete. - // The trace should not be used after calling this method. - Finish() -} - -type lazySprintf struct { - format string - a []interface{} -} - -func (l *lazySprintf) String() string { - return fmt.Sprintf(l.format, l.a...) -} - -// New returns a new Trace with the specified family and title. -func New(family, title string) Trace { - tr := newTrace() - tr.ref() - tr.Family, tr.Title = family, title - tr.Start = time.Now() - tr.maxEvents = maxEventsPerTrace - tr.events = tr.eventsBuf[:0] - - activeMu.RLock() - s := activeTraces[tr.Family] - activeMu.RUnlock() - if s == nil { - activeMu.Lock() - s = activeTraces[tr.Family] // check again - if s == nil { - s = new(traceSet) - activeTraces[tr.Family] = s - } - activeMu.Unlock() - } - s.Add(tr) - - // Trigger allocation of the completed trace structure for this family. - // This will cause the family to be present in the request page during - // the first trace of this family. We don't care about the return value, - // nor is there any need for this to run inline, so we execute it in its - // own goroutine, but only if the family isn't allocated yet. - completedMu.RLock() - if _, ok := completedTraces[tr.Family]; !ok { - go allocFamily(tr.Family) - } - completedMu.RUnlock() - - return tr -} - -func (tr *trace) Finish() { - elapsed := time.Now().Sub(tr.Start) - tr.mu.Lock() - tr.Elapsed = elapsed - tr.mu.Unlock() - - if DebugUseAfterFinish { - buf := make([]byte, 4<<10) // 4 KB should be enough - n := runtime.Stack(buf, false) - tr.finishStack = buf[:n] - } - - activeMu.RLock() - m := activeTraces[tr.Family] - activeMu.RUnlock() - m.Remove(tr) - - f := getFamily(tr.Family, true) - tr.mu.RLock() // protects tr fields in Cond.match calls - for _, b := range f.Buckets { - if b.Cond.match(tr) { - b.Add(tr) - } - } - tr.mu.RUnlock() - - // Add a sample of elapsed time as microseconds to the family's timeseries - h := new(histogram) - h.addMeasurement(elapsed.Nanoseconds() / 1e3) - f.LatencyMu.Lock() - f.Latency.Add(h) - f.LatencyMu.Unlock() - - tr.unref() // matches ref in New -} - -const ( - bucketsPerFamily = 9 - tracesPerBucket = 10 - maxActiveTraces = 20 // Maximum number of active traces to show. - maxEventsPerTrace = 10 - numHistogramBuckets = 38 -) - -var ( - // The active traces. - activeMu sync.RWMutex - activeTraces = make(map[string]*traceSet) // family -> traces - - // Families of completed traces. - completedMu sync.RWMutex - completedTraces = make(map[string]*family) // family -> traces -) - -type traceSet struct { - mu sync.RWMutex - m map[*trace]bool - - // We could avoid the entire map scan in FirstN by having a slice of all the traces - // ordered by start time, and an index into that from the trace struct, with a periodic - // repack of the slice after enough traces finish; we could also use a skip list or similar. - // However, that would shift some of the expense from /debug/requests time to RPC time, - // which is probably the wrong trade-off. -} - -func (ts *traceSet) Len() int { - ts.mu.RLock() - defer ts.mu.RUnlock() - return len(ts.m) -} - -func (ts *traceSet) Add(tr *trace) { - ts.mu.Lock() - if ts.m == nil { - ts.m = make(map[*trace]bool) - } - ts.m[tr] = true - ts.mu.Unlock() -} - -func (ts *traceSet) Remove(tr *trace) { - ts.mu.Lock() - delete(ts.m, tr) - ts.mu.Unlock() -} - -// FirstN returns the first n traces ordered by time. -func (ts *traceSet) FirstN(n int) traceList { - ts.mu.RLock() - defer ts.mu.RUnlock() - - if n > len(ts.m) { - n = len(ts.m) - } - trl := make(traceList, 0, n) - - // Fast path for when no selectivity is needed. - if n == len(ts.m) { - for tr := range ts.m { - tr.ref() - trl = append(trl, tr) - } - sort.Sort(trl) - return trl - } - - // Pick the oldest n traces. - // This is inefficient. See the comment in the traceSet struct. - for tr := range ts.m { - // Put the first n traces into trl in the order they occur. - // When we have n, sort trl, and thereafter maintain its order. - if len(trl) < n { - tr.ref() - trl = append(trl, tr) - if len(trl) == n { - // This is guaranteed to happen exactly once during this loop. - sort.Sort(trl) - } - continue - } - if tr.Start.After(trl[n-1].Start) { - continue - } - - // Find where to insert this one. - tr.ref() - i := sort.Search(n, func(i int) bool { return trl[i].Start.After(tr.Start) }) - trl[n-1].unref() - copy(trl[i+1:], trl[i:]) - trl[i] = tr - } - - return trl -} - -func getActiveTraces(fam string) traceList { - activeMu.RLock() - s := activeTraces[fam] - activeMu.RUnlock() - if s == nil { - return nil - } - return s.FirstN(maxActiveTraces) -} - -func getFamily(fam string, allocNew bool) *family { - completedMu.RLock() - f := completedTraces[fam] - completedMu.RUnlock() - if f == nil && allocNew { - f = allocFamily(fam) - } - return f -} - -func allocFamily(fam string) *family { - completedMu.Lock() - defer completedMu.Unlock() - f := completedTraces[fam] - if f == nil { - f = newFamily() - completedTraces[fam] = f - } - return f -} - -// family represents a set of trace buckets and associated latency information. -type family struct { - // traces may occur in multiple buckets. - Buckets [bucketsPerFamily]*traceBucket - - // latency time series - LatencyMu sync.RWMutex - Latency *timeseries.MinuteHourSeries -} - -func newFamily() *family { - return &family{ - Buckets: [bucketsPerFamily]*traceBucket{ - {Cond: minCond(0)}, - {Cond: minCond(50 * time.Millisecond)}, - {Cond: minCond(100 * time.Millisecond)}, - {Cond: minCond(200 * time.Millisecond)}, - {Cond: minCond(500 * time.Millisecond)}, - {Cond: minCond(1 * time.Second)}, - {Cond: minCond(10 * time.Second)}, - {Cond: minCond(100 * time.Second)}, - {Cond: errorCond{}}, - }, - Latency: timeseries.NewMinuteHourSeries(func() timeseries.Observable { return new(histogram) }), - } -} - -// traceBucket represents a size-capped bucket of historic traces, -// along with a condition for a trace to belong to the bucket. -type traceBucket struct { - Cond cond - - // Ring buffer implementation of a fixed-size FIFO queue. - mu sync.RWMutex - buf [tracesPerBucket]*trace - start int // < tracesPerBucket - length int // <= tracesPerBucket -} - -func (b *traceBucket) Add(tr *trace) { - b.mu.Lock() - defer b.mu.Unlock() - - i := b.start + b.length - if i >= tracesPerBucket { - i -= tracesPerBucket - } - if b.length == tracesPerBucket { - // "Remove" an element from the bucket. - b.buf[i].unref() - b.start++ - if b.start == tracesPerBucket { - b.start = 0 - } - } - b.buf[i] = tr - if b.length < tracesPerBucket { - b.length++ - } - tr.ref() -} - -// Copy returns a copy of the traces in the bucket. -// If tracedOnly is true, only the traces with trace information will be returned. -// The logs will be ref'd before returning; the caller should call -// the Free method when it is done with them. -// TODO(dsymonds): keep track of traced requests in separate buckets. -func (b *traceBucket) Copy(tracedOnly bool) traceList { - b.mu.RLock() - defer b.mu.RUnlock() - - trl := make(traceList, 0, b.length) - for i, x := 0, b.start; i < b.length; i++ { - tr := b.buf[x] - if !tracedOnly || tr.spanID != 0 { - tr.ref() - trl = append(trl, tr) - } - x++ - if x == b.length { - x = 0 - } - } - return trl -} - -func (b *traceBucket) Empty() bool { - b.mu.RLock() - defer b.mu.RUnlock() - return b.length == 0 -} - -// cond represents a condition on a trace. -type cond interface { - match(t *trace) bool - String() string -} - -type minCond time.Duration - -func (m minCond) match(t *trace) bool { return t.Elapsed >= time.Duration(m) } -func (m minCond) String() string { return fmt.Sprintf("≥%gs", time.Duration(m).Seconds()) } - -type errorCond struct{} - -func (e errorCond) match(t *trace) bool { return t.IsError } -func (e errorCond) String() string { return "errors" } - -type traceList []*trace - -// Free calls unref on each element of the list. -func (trl traceList) Free() { - for _, t := range trl { - t.unref() - } -} - -// traceList may be sorted in reverse chronological order. -func (trl traceList) Len() int { return len(trl) } -func (trl traceList) Less(i, j int) bool { return trl[i].Start.After(trl[j].Start) } -func (trl traceList) Swap(i, j int) { trl[i], trl[j] = trl[j], trl[i] } - -// An event is a timestamped log entry in a trace. -type event struct { - When time.Time - Elapsed time.Duration // since previous event in trace - NewDay bool // whether this event is on a different day to the previous event - Recyclable bool // whether this event was passed via LazyLog - Sensitive bool // whether this event contains sensitive information - What interface{} // string or fmt.Stringer -} - -// WhenString returns a string representation of the elapsed time of the event. -// It will include the date if midnight was crossed. -func (e event) WhenString() string { - if e.NewDay { - return e.When.Format("2006/01/02 15:04:05.000000") - } - return e.When.Format("15:04:05.000000") -} - -// discarded represents a number of discarded events. -// It is stored as *discarded to make it easier to update in-place. -type discarded int - -func (d *discarded) String() string { - return fmt.Sprintf("(%d events discarded)", int(*d)) -} - -// trace represents an active or complete request, -// either sent or received by this program. -type trace struct { - // Family is the top-level grouping of traces to which this belongs. - Family string - - // Title is the title of this trace. - Title string - - // Start time of the this trace. - Start time.Time - - mu sync.RWMutex - events []event // Append-only sequence of events (modulo discards). - maxEvents int - recycler func(interface{}) - IsError bool // Whether this trace resulted in an error. - Elapsed time.Duration // Elapsed time for this trace, zero while active. - traceID uint64 // Trace information if non-zero. - spanID uint64 - - refs int32 // how many buckets this is in - disc discarded // scratch space to avoid allocation - - finishStack []byte // where finish was called, if DebugUseAfterFinish is set - - eventsBuf [4]event // preallocated buffer in case we only log a few events -} - -func (tr *trace) reset() { - // Clear all but the mutex. Mutexes may not be copied, even when unlocked. - tr.Family = "" - tr.Title = "" - tr.Start = time.Time{} - - tr.mu.Lock() - tr.Elapsed = 0 - tr.traceID = 0 - tr.spanID = 0 - tr.IsError = false - tr.maxEvents = 0 - tr.events = nil - tr.recycler = nil - tr.mu.Unlock() - - tr.refs = 0 - tr.disc = 0 - tr.finishStack = nil - for i := range tr.eventsBuf { - tr.eventsBuf[i] = event{} - } -} - -// delta returns the elapsed time since the last event or the trace start, -// and whether it spans midnight. -// L >= tr.mu -func (tr *trace) delta(t time.Time) (time.Duration, bool) { - if len(tr.events) == 0 { - return t.Sub(tr.Start), false - } - prev := tr.events[len(tr.events)-1].When - return t.Sub(prev), prev.Day() != t.Day() -} - -func (tr *trace) addEvent(x interface{}, recyclable, sensitive bool) { - if DebugUseAfterFinish && tr.finishStack != nil { - buf := make([]byte, 4<<10) // 4 KB should be enough - n := runtime.Stack(buf, false) - log.Printf("net/trace: trace used after finish:\nFinished at:\n%s\nUsed at:\n%s", tr.finishStack, buf[:n]) - } - - /* - NOTE TO DEBUGGERS - - If you are here because your program panicked in this code, - it is almost definitely the fault of code using this package, - and very unlikely to be the fault of this code. - - The most likely scenario is that some code elsewhere is using - a trace.Trace after its Finish method is called. - You can temporarily set the DebugUseAfterFinish var - to help discover where that is; do not leave that var set, - since it makes this package much less efficient. - */ - - e := event{When: time.Now(), What: x, Recyclable: recyclable, Sensitive: sensitive} - tr.mu.Lock() - e.Elapsed, e.NewDay = tr.delta(e.When) - if len(tr.events) < tr.maxEvents { - tr.events = append(tr.events, e) - } else { - // Discard the middle events. - di := int((tr.maxEvents - 1) / 2) - if d, ok := tr.events[di].What.(*discarded); ok { - (*d)++ - } else { - // disc starts at two to count for the event it is replacing, - // plus the next one that we are about to drop. - tr.disc = 2 - if tr.recycler != nil && tr.events[di].Recyclable { - go tr.recycler(tr.events[di].What) - } - tr.events[di].What = &tr.disc - } - // The timestamp of the discarded meta-event should be - // the time of the last event it is representing. - tr.events[di].When = tr.events[di+1].When - - if tr.recycler != nil && tr.events[di+1].Recyclable { - go tr.recycler(tr.events[di+1].What) - } - copy(tr.events[di+1:], tr.events[di+2:]) - tr.events[tr.maxEvents-1] = e - } - tr.mu.Unlock() -} - -func (tr *trace) LazyLog(x fmt.Stringer, sensitive bool) { - tr.addEvent(x, true, sensitive) -} - -func (tr *trace) LazyPrintf(format string, a ...interface{}) { - tr.addEvent(&lazySprintf{format, a}, false, false) -} - -func (tr *trace) SetError() { - tr.mu.Lock() - tr.IsError = true - tr.mu.Unlock() -} - -func (tr *trace) SetRecycler(f func(interface{})) { - tr.mu.Lock() - tr.recycler = f - tr.mu.Unlock() -} - -func (tr *trace) SetTraceInfo(traceID, spanID uint64) { - tr.mu.Lock() - tr.traceID, tr.spanID = traceID, spanID - tr.mu.Unlock() -} - -func (tr *trace) SetMaxEvents(m int) { - tr.mu.Lock() - // Always keep at least three events: first, discarded count, last. - if len(tr.events) == 0 && m > 3 { - tr.maxEvents = m - } - tr.mu.Unlock() -} - -func (tr *trace) ref() { - atomic.AddInt32(&tr.refs, 1) -} - -func (tr *trace) unref() { - if atomic.AddInt32(&tr.refs, -1) == 0 { - tr.mu.RLock() - if tr.recycler != nil { - // freeTrace clears tr, so we hold tr.recycler and tr.events here. - go func(f func(interface{}), es []event) { - for _, e := range es { - if e.Recyclable { - f(e.What) - } - } - }(tr.recycler, tr.events) - } - tr.mu.RUnlock() - - freeTrace(tr) - } -} - -func (tr *trace) When() string { - return tr.Start.Format("2006/01/02 15:04:05.000000") -} - -func (tr *trace) ElapsedTime() string { - tr.mu.RLock() - t := tr.Elapsed - tr.mu.RUnlock() - - if t == 0 { - // Active trace. - t = time.Since(tr.Start) - } - return fmt.Sprintf("%.6f", t.Seconds()) -} - -func (tr *trace) Events() []event { - tr.mu.RLock() - defer tr.mu.RUnlock() - return tr.events -} - -var traceFreeList = make(chan *trace, 1000) // TODO(dsymonds): Use sync.Pool? - -// newTrace returns a trace ready to use. -func newTrace() *trace { - select { - case tr := <-traceFreeList: - return tr - default: - return new(trace) - } -} - -// freeTrace adds tr to traceFreeList if there's room. -// This is non-blocking. -func freeTrace(tr *trace) { - if DebugUseAfterFinish { - return // never reuse - } - tr.reset() - select { - case traceFreeList <- tr: - default: - } -} - -func elapsed(d time.Duration) string { - b := []byte(fmt.Sprintf("%.6f", d.Seconds())) - - // For subsecond durations, blank all zeros before decimal point, - // and all zeros between the decimal point and the first non-zero digit. - if d < time.Second { - dot := bytes.IndexByte(b, '.') - for i := 0; i < dot; i++ { - b[i] = ' ' - } - for i := dot + 1; i < len(b); i++ { - if b[i] == '0' { - b[i] = ' ' - } else { - break - } - } - } - - return string(b) -} - -var pageTmplCache *template.Template -var pageTmplOnce sync.Once - -func pageTmpl() *template.Template { - pageTmplOnce.Do(func() { - pageTmplCache = template.Must(template.New("Page").Funcs(template.FuncMap{ - "elapsed": elapsed, - "add": func(a, b int) int { return a + b }, - }).Parse(pageHTML)) - }) - return pageTmplCache -} - -const pageHTML = ` -{{template "Prolog" .}} -{{template "StatusTable" .}} -{{template "Epilog" .}} - -{{define "Prolog"}} - - - /debug/requests - - - - -

/debug/requests

-{{end}} {{/* end of Prolog */}} - -{{define "StatusTable"}} - - {{range $fam := .Families}} - - - - {{$n := index $.ActiveTraceCount $fam}} - - - {{$f := index $.CompletedTraces $fam}} - {{range $i, $b := $f.Buckets}} - {{$empty := $b.Empty}} - - {{end}} - - {{$nb := len $f.Buckets}} - - - - - - {{end}} -
{{$fam}} - {{if $n}}{{end}} - [{{$n}} active] - {{if $n}}{{end}} - - {{if not $empty}}{{end}} - [{{.Cond}}] - {{if not $empty}}{{end}} - - [minute] - - [hour] - - [total] -
-{{end}} {{/* end of StatusTable */}} - -{{define "Epilog"}} -{{if $.Traces}} -
-

Family: {{$.Family}}

- -{{if or $.Expanded $.Traced}} - [Normal/Summary] -{{else}} - [Normal/Summary] -{{end}} - -{{if or (not $.Expanded) $.Traced}} - [Normal/Expanded] -{{else}} - [Normal/Expanded] -{{end}} - -{{if not $.Active}} - {{if or $.Expanded (not $.Traced)}} - [Traced/Summary] - {{else}} - [Traced/Summary] - {{end}} - {{if or (not $.Expanded) (not $.Traced)}} - [Traced/Expanded] - {{else}} - [Traced/Expanded] - {{end}} -{{end}} - -{{if $.Total}} -

Showing {{len $.Traces}} of {{$.Total}} traces.

-{{end}} - - - - - {{range $tr := $.Traces}} - - - - - {{/* TODO: include traceID/spanID */}} - - {{if $.Expanded}} - {{range $tr.Events}} - - - - - - {{end}} - {{end}} - {{end}} -
- {{if $.Active}}Active{{else}}Completed{{end}} Requests -
WhenElapsed (s)
{{$tr.When}}{{$tr.ElapsedTime}}{{$tr.Title}}
{{.WhenString}}{{elapsed .Elapsed}}{{if or $.ShowSensitive (not .Sensitive)}}... {{.What}}{{else}}[redacted]{{end}}
-{{end}} {{/* if $.Traces */}} - -{{if $.Histogram}} -

Latency (µs) of {{$.Family}} over {{$.HistogramWindow}}

-{{$.Histogram}} -{{end}} {{/* if $.Histogram */}} - - - -{{end}} {{/* end of Epilog */}} -` diff --git a/vendor/golang.org/x/net/trace/trace_go16.go b/vendor/golang.org/x/net/trace/trace_go16.go deleted file mode 100644 index d6081911853..00000000000 --- a/vendor/golang.org/x/net/trace/trace_go16.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build !go1.7 - -package trace - -import "golang.org/x/net/context" - -// NewContext returns a copy of the parent context -// and associates it with a Trace. -func NewContext(ctx context.Context, tr Trace) context.Context { - return context.WithValue(ctx, contextKey, tr) -} - -// FromContext returns the Trace bound to the context, if any. -func FromContext(ctx context.Context) (tr Trace, ok bool) { - tr, ok = ctx.Value(contextKey).(Trace) - return -} diff --git a/vendor/golang.org/x/net/trace/trace_go17.go b/vendor/golang.org/x/net/trace/trace_go17.go deleted file mode 100644 index df6e1fba7ca..00000000000 --- a/vendor/golang.org/x/net/trace/trace_go17.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2017 The Go Authors. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// +build go1.7 - -package trace - -import "context" - -// NewContext returns a copy of the parent context -// and associates it with a Trace. -func NewContext(ctx context.Context, tr Trace) context.Context { - return context.WithValue(ctx, contextKey, tr) -} - -// FromContext returns the Trace bound to the context, if any. -func FromContext(ctx context.Context) (tr Trace, ok bool) { - tr, ok = ctx.Value(contextKey).(Trace) - return -} diff --git a/vendor/golang.org/x/sync/semaphore/semaphore.go b/vendor/golang.org/x/sync/semaphore/semaphore.go deleted file mode 100644 index e9d2d79a97f..00000000000 --- a/vendor/golang.org/x/sync/semaphore/semaphore.go +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright 2017 The Go 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 semaphore provides a weighted semaphore implementation. -package semaphore // import "golang.org/x/sync/semaphore" - -import ( - "container/list" - "sync" - - // Use the old context because packages that depend on this one - // (e.g. cloud.google.com/go/...) must run on Go 1.6. - // TODO(jba): update to "context" when possible. - "golang.org/x/net/context" -) - -type waiter struct { - n int64 - ready chan<- struct{} // Closed when semaphore acquired. -} - -// NewWeighted creates a new weighted semaphore with the given -// maximum combined weight for concurrent access. -func NewWeighted(n int64) *Weighted { - w := &Weighted{size: n} - return w -} - -// Weighted provides a way to bound concurrent access to a resource. -// The callers can request access with a given weight. -type Weighted struct { - size int64 - cur int64 - mu sync.Mutex - waiters list.List -} - -// Acquire acquires the semaphore with a weight of n, blocking only until ctx -// is done. On success, returns nil. On failure, returns ctx.Err() and leaves -// the semaphore unchanged. -// -// If ctx is already done, Acquire may still succeed without blocking. -func (s *Weighted) Acquire(ctx context.Context, n int64) error { - s.mu.Lock() - if s.size-s.cur >= n && s.waiters.Len() == 0 { - s.cur += n - s.mu.Unlock() - return nil - } - - if n > s.size { - // Don't make other Acquire calls block on one that's doomed to fail. - s.mu.Unlock() - <-ctx.Done() - return ctx.Err() - } - - ready := make(chan struct{}) - w := waiter{n: n, ready: ready} - elem := s.waiters.PushBack(w) - s.mu.Unlock() - - select { - case <-ctx.Done(): - err := ctx.Err() - s.mu.Lock() - select { - case <-ready: - // Acquired the semaphore after we were canceled. Rather than trying to - // fix up the queue, just pretend we didn't notice the cancelation. - err = nil - default: - s.waiters.Remove(elem) - } - s.mu.Unlock() - return err - - case <-ready: - return nil - } -} - -// TryAcquire acquires the semaphore with a weight of n without blocking. -// On success, returns true. On failure, returns false and leaves the semaphore unchanged. -func (s *Weighted) TryAcquire(n int64) bool { - s.mu.Lock() - success := s.size-s.cur >= n && s.waiters.Len() == 0 - if success { - s.cur += n - } - s.mu.Unlock() - return success -} - -// Release releases the semaphore with a weight of n. -func (s *Weighted) Release(n int64) { - s.mu.Lock() - s.cur -= n - if s.cur < 0 { - s.mu.Unlock() - panic("semaphore: bad release") - } - for { - next := s.waiters.Front() - if next == nil { - break // No more waiters blocked. - } - - w := next.Value.(waiter) - if s.size-s.cur < w.n { - // Not enough tokens for the next waiter. We could keep going (to try to - // find a waiter with a smaller request), but under load that could cause - // starvation for large requests; instead, we leave all remaining waiters - // blocked. - // - // Consider a semaphore used as a read-write lock, with N tokens, N - // readers, and one writer. Each reader can Acquire(1) to obtain a read - // lock. The writer can Acquire(N) to obtain a write lock, excluding all - // of the readers. If we allow the readers to jump ahead in the queue, - // the writer will starve — there is always one token available for every - // reader. - break - } - - s.cur += w.n - s.waiters.Remove(next) - close(w.ready) - } - s.mu.Unlock() -} diff --git a/vendor/google.golang.org/api/AUTHORS b/vendor/google.golang.org/api/AUTHORS deleted file mode 100644 index f73b7257457..00000000000 --- a/vendor/google.golang.org/api/AUTHORS +++ /dev/null @@ -1,10 +0,0 @@ -# This is the official list of authors for copyright purposes. -# This file is distinct from the CONTRIBUTORS files. -# See the latter for an explanation. - -# Names should be added to this file as -# Name or Organization -# The email address is not required for organizations. - -# Please keep the list sorted. -Google Inc. diff --git a/vendor/google.golang.org/api/CONTRIBUTORS b/vendor/google.golang.org/api/CONTRIBUTORS deleted file mode 100644 index b8928e61641..00000000000 --- a/vendor/google.golang.org/api/CONTRIBUTORS +++ /dev/null @@ -1,54 +0,0 @@ -# This is the official list of people who can contribute -# (and typically have contributed) code to the repository. -# The AUTHORS file lists the copyright holders; this file -# lists people. For example, Google employees are listed here -# but not in AUTHORS, because Google holds the copyright. -# -# The submission process automatically checks to make sure -# that people submitting code are listed in this file (by email address). -# -# Names should be added to this file only after verifying that -# the individual or the individual's organization has agreed to -# the appropriate Contributor License Agreement, found here: -# -# https://cla.developers.google.com/about/google-individual -# https://cla.developers.google.com/about/google-corporate -# -# The CLA can be filled out on the web: -# -# https://cla.developers.google.com/ -# -# When adding J Random Contributor's name to this file, -# either J's name or J's organization's name should be -# added to the AUTHORS file, depending on whether the -# individual or corporate CLA was used. - -# Names should be added to this file like so: -# Name -# -# An entry with two email addresses specifies that the -# first address should be used in the submit logs and -# that the second address should be recognized as the -# same person when interacting with Rietveld. - -# Please keep the list sorted. - -Alain Vongsouvanhalainv -Andrew Gerrand -Brad Fitzpatrick -Eric Koleda -Francesc Campoy -Garrick Evans -Glenn Lewis -Ivan Krasin -Jason Hall -Johan Euphrosine -Kostik Shtoyk -Kunpei Sakai -Matthew Whisenhunt -Michael McGreevy -Nick Craig-Wood -Ross Light -Sarah Adams -Scott Van Woudenberg -Takashi Matsuo diff --git a/vendor/google.golang.org/api/LICENSE b/vendor/google.golang.org/api/LICENSE deleted file mode 100644 index 263aa7a0c12..00000000000 --- a/vendor/google.golang.org/api/LICENSE +++ /dev/null @@ -1,27 +0,0 @@ -Copyright (c) 2011 Google Inc. All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/google.golang.org/api/googleapi/internal/uritemplates/LICENSE b/vendor/google.golang.org/api/googleapi/internal/uritemplates/LICENSE deleted file mode 100644 index de9c88cb65c..00000000000 --- a/vendor/google.golang.org/api/googleapi/internal/uritemplates/LICENSE +++ /dev/null @@ -1,18 +0,0 @@ -Copyright (c) 2013 Joshua Tacoma - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/google.golang.org/api/googleapi/transport/apikey.go b/vendor/google.golang.org/api/googleapi/transport/apikey.go deleted file mode 100644 index eca1ea25077..00000000000 --- a/vendor/google.golang.org/api/googleapi/transport/apikey.go +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2012 Google Inc. All rights reserved. -// Use of this source code is governed by a BSD-style -// license that can be found in the LICENSE file. - -// Package transport contains HTTP transports used to make -// authenticated API requests. -package transport - -import ( - "errors" - "net/http" -) - -// APIKey is an HTTP Transport which wraps an underlying transport and -// appends an API Key "key" parameter to the URL of outgoing requests. -type APIKey struct { - // Key is the API Key to set on requests. - Key string - - // Transport is the underlying HTTP transport. - // If nil, http.DefaultTransport is used. - Transport http.RoundTripper -} - -func (t *APIKey) RoundTrip(req *http.Request) (*http.Response, error) { - rt := t.Transport - if rt == nil { - rt = http.DefaultTransport - if rt == nil { - return nil, errors.New("googleapi/transport: no Transport specified or available") - } - } - newReq := *req - args := newReq.URL.Query() - args.Set("key", t.Key) - newReq.URL.RawQuery = args.Encode() - return rt.RoundTrip(&newReq) -} diff --git a/vendor/google.golang.org/api/internal/creds.go b/vendor/google.golang.org/api/internal/creds.go deleted file mode 100644 index c16b7b629be..00000000000 --- a/vendor/google.golang.org/api/internal/creds.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2017 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 internal - -import ( - "fmt" - "io/ioutil" - - "golang.org/x/net/context" - "golang.org/x/oauth2/google" -) - -// Creds returns credential information obtained from DialSettings, or if none, then -// it returns default credential information. -func Creds(ctx context.Context, ds *DialSettings) (*google.DefaultCredentials, error) { - if ds.Credentials != nil { - return ds.Credentials, nil - } - if ds.CredentialsFile != "" { - data, err := ioutil.ReadFile(ds.CredentialsFile) - if err != nil { - return nil, fmt.Errorf("cannot read credentials file: %v", err) - } - return google.CredentialsFromJSON(ctx, data, ds.Scopes...) - } - if ds.TokenSource != nil { - return &google.DefaultCredentials{TokenSource: ds.TokenSource}, nil - } - return google.FindDefaultCredentials(ctx, ds.Scopes...) -} diff --git a/vendor/google.golang.org/api/internal/pool.go b/vendor/google.golang.org/api/internal/pool.go deleted file mode 100644 index 4150feb6bb0..00000000000 --- a/vendor/google.golang.org/api/internal/pool.go +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2016 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 internal - -import ( - "errors" - "google.golang.org/grpc/naming" -) - -// PoolResolver provides a fixed list of addresses to load balance between -// and does not provide further updates. -type PoolResolver struct { - poolSize int - dialOpt *DialSettings - ch chan []*naming.Update -} - -// NewPoolResolver returns a PoolResolver -// This is an EXPERIMENTAL API and may be changed or removed in the future. -func NewPoolResolver(size int, o *DialSettings) *PoolResolver { - return &PoolResolver{poolSize: size, dialOpt: o} -} - -// Resolve returns a Watcher for the endpoint defined by the DialSettings -// provided to NewPoolResolver. -func (r *PoolResolver) Resolve(target string) (naming.Watcher, error) { - if r.dialOpt.Endpoint == "" { - return nil, errors.New("No endpoint configured") - } - addrs := make([]*naming.Update, 0, r.poolSize) - for i := 0; i < r.poolSize; i++ { - addrs = append(addrs, &naming.Update{Op: naming.Add, Addr: r.dialOpt.Endpoint, Metadata: i}) - } - r.ch = make(chan []*naming.Update, 1) - r.ch <- addrs - return r, nil -} - -// Next returns a static list of updates on the first call, -// and blocks indefinitely until Close is called on subsequent calls. -func (r *PoolResolver) Next() ([]*naming.Update, error) { - return <-r.ch, nil -} - -func (r *PoolResolver) Close() { - close(r.ch) -} diff --git a/vendor/google.golang.org/api/internal/settings.go b/vendor/google.golang.org/api/internal/settings.go deleted file mode 100644 index 34dfa5a8219..00000000000 --- a/vendor/google.golang.org/api/internal/settings.go +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2017 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 internal supports the options and transport packages. -package internal - -import ( - "errors" - "net/http" - - "golang.org/x/oauth2" - "golang.org/x/oauth2/google" - "google.golang.org/grpc" -) - -// DialSettings holds information needed to establish a connection with a -// Google API service. -type DialSettings struct { - Endpoint string - Scopes []string - TokenSource oauth2.TokenSource - Credentials *google.DefaultCredentials - CredentialsFile string // if set, Token Source is ignored. - UserAgent string - APIKey string - HTTPClient *http.Client - GRPCDialOpts []grpc.DialOption - GRPCConn *grpc.ClientConn - NoAuth bool -} - -// Validate reports an error if ds is invalid. -func (ds *DialSettings) Validate() error { - hasCreds := ds.APIKey != "" || ds.TokenSource != nil || ds.CredentialsFile != "" || ds.Credentials != nil - if ds.NoAuth && hasCreds { - return errors.New("options.WithoutAuthentication is incompatible with any option that provides credentials") - } - // Credentials should not appear with other options. - // We currently allow TokenSource and CredentialsFile to coexist. - // TODO(jba): make TokenSource & CredentialsFile an error (breaking change). - if ds.Credentials != nil && (ds.APIKey != "" || ds.TokenSource != nil || ds.CredentialsFile != "") { - return errors.New("multiple credential options provided") - } - if ds.HTTPClient != nil && ds.GRPCConn != nil { - return errors.New("WithHTTPClient is incompatible with WithGRPCConn") - } - if ds.HTTPClient != nil && ds.GRPCDialOpts != nil { - return errors.New("WithHTTPClient is incompatible with gRPC dial options") - } - return nil -} diff --git a/vendor/google.golang.org/api/iterator/iterator.go b/vendor/google.golang.org/api/iterator/iterator.go deleted file mode 100644 index e34e5207349..00000000000 --- a/vendor/google.golang.org/api/iterator/iterator.go +++ /dev/null @@ -1,231 +0,0 @@ -// Copyright 2016 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 iterator provides support for standard Google API iterators. -// See https://github.com/GoogleCloudPlatform/gcloud-golang/wiki/Iterator-Guidelines. -package iterator - -import ( - "errors" - "fmt" - "reflect" -) - -// Done is returned by an iterator's Next method when the iteration is -// complete; when there are no more items to return. -var Done = errors.New("no more items in iterator") - -// We don't support mixed calls to Next and NextPage because they play -// with the paging state in incompatible ways. -var errMixed = errors.New("iterator: Next and NextPage called on same iterator") - -// PageInfo contains information about an iterator's paging state. -type PageInfo struct { - // Token is the token used to retrieve the next page of items from the - // API. You may set Token immediately after creating an iterator to - // begin iteration at a particular point. If Token is the empty string, - // the iterator will begin with the first eligible item. - // - // The result of setting Token after the first call to Next is undefined. - // - // After the underlying API method is called to retrieve a page of items, - // Token is set to the next-page token in the response. - Token string - - // MaxSize is the maximum number of items returned by a call to the API. - // Set MaxSize as a hint to optimize the buffering behavior of the iterator. - // If zero, the page size is determined by the underlying service. - // - // Use Pager to retrieve a page of a specific, exact size. - MaxSize int - - // The error state of the iterator. Manipulated by PageInfo.next and Pager. - // This is a latch: it starts as nil, and once set should never change. - err error - - // If true, no more calls to fetch should be made. Set to true when fetch - // returns an empty page token. The iterator is Done when this is true AND - // the buffer is empty. - atEnd bool - - // Function that fetches a page from the underlying service. It should pass - // the pageSize and pageToken arguments to the service, fill the buffer - // with the results from the call, and return the next-page token returned - // by the service. The function must not remove any existing items from the - // buffer. If the underlying RPC takes an int32 page size, pageSize should - // be silently truncated. - fetch func(pageSize int, pageToken string) (nextPageToken string, err error) - - // Function that returns the number of currently buffered items. - bufLen func() int - - // Function that returns the buffer, after setting the buffer variable to nil. - takeBuf func() interface{} - - // Set to true on first call to PageInfo.next or Pager.NextPage. Used to check - // for calls to both Next and NextPage with the same iterator. - nextCalled, nextPageCalled bool -} - -// NewPageInfo exposes internals for iterator implementations. -// It is not a stable interface. -var NewPageInfo = newPageInfo - -// If an iterator can support paging, its iterator-creating method should call -// this (via the NewPageInfo variable above). -// -// The fetch, bufLen and takeBuf arguments provide access to the -// iterator's internal slice of buffered items. They behave as described in -// PageInfo, above. -// -// The return value is the PageInfo.next method bound to the returned PageInfo value. -// (Returning it avoids exporting PageInfo.next.) -func newPageInfo(fetch func(int, string) (string, error), bufLen func() int, takeBuf func() interface{}) (*PageInfo, func() error) { - pi := &PageInfo{ - fetch: fetch, - bufLen: bufLen, - takeBuf: takeBuf, - } - return pi, pi.next -} - -// Remaining returns the number of items available before the iterator makes another API call. -func (pi *PageInfo) Remaining() int { return pi.bufLen() } - -// next provides support for an iterator's Next function. An iterator's Next -// should return the error returned by next if non-nil; else it can assume -// there is at least one item in its buffer, and it should return that item and -// remove it from the buffer. -func (pi *PageInfo) next() error { - pi.nextCalled = true - if pi.err != nil { // Once we get an error, always return it. - // TODO(jba): fix so users can retry on transient errors? Probably not worth it. - return pi.err - } - if pi.nextPageCalled { - pi.err = errMixed - return pi.err - } - // Loop until we get some items or reach the end. - for pi.bufLen() == 0 && !pi.atEnd { - if err := pi.fill(pi.MaxSize); err != nil { - pi.err = err - return pi.err - } - if pi.Token == "" { - pi.atEnd = true - } - } - // Either the buffer is non-empty or pi.atEnd is true (or both). - if pi.bufLen() == 0 { - // The buffer is empty and pi.atEnd is true, i.e. the service has no - // more items. - pi.err = Done - } - return pi.err -} - -// Call the service to fill the buffer, using size and pi.Token. Set pi.Token to the -// next-page token returned by the call. -// If fill returns a non-nil error, the buffer will be empty. -func (pi *PageInfo) fill(size int) error { - tok, err := pi.fetch(size, pi.Token) - if err != nil { - pi.takeBuf() // clear the buffer - return err - } - pi.Token = tok - return nil -} - -// Pageable is implemented by iterators that support paging. -type Pageable interface { - // PageInfo returns paging information associated with the iterator. - PageInfo() *PageInfo -} - -// Pager supports retrieving iterator items a page at a time. -type Pager struct { - pageInfo *PageInfo - pageSize int -} - -// NewPager returns a pager that uses iter. Calls to its NextPage method will -// obtain exactly pageSize items, unless fewer remain. The pageToken argument -// indicates where to start the iteration. Pass the empty string to start at -// the beginning, or pass a token retrieved from a call to Pager.NextPage. -// -// If you use an iterator with a Pager, you must not call Next on the iterator. -func NewPager(iter Pageable, pageSize int, pageToken string) *Pager { - p := &Pager{ - pageInfo: iter.PageInfo(), - pageSize: pageSize, - } - p.pageInfo.Token = pageToken - if pageSize <= 0 { - p.pageInfo.err = errors.New("iterator: page size must be positive") - } - return p -} - -// NextPage retrieves a sequence of items from the iterator and appends them -// to slicep, which must be a pointer to a slice of the iterator's item type. -// Exactly p.pageSize items will be appended, unless fewer remain. -// -// The first return value is the page token to use for the next page of items. -// If empty, there are no more pages. Aside from checking for the end of the -// iteration, the returned page token is only needed if the iteration is to be -// resumed a later time, in another context (possibly another process). -// -// The second return value is non-nil if an error occurred. It will never be -// the special iterator sentinel value Done. To recognize the end of the -// iteration, compare nextPageToken to the empty string. -// -// It is possible for NextPage to return a single zero-length page along with -// an empty page token when there are no more items in the iteration. -func (p *Pager) NextPage(slicep interface{}) (nextPageToken string, err error) { - p.pageInfo.nextPageCalled = true - if p.pageInfo.err != nil { - return "", p.pageInfo.err - } - if p.pageInfo.nextCalled { - p.pageInfo.err = errMixed - return "", p.pageInfo.err - } - if p.pageInfo.bufLen() > 0 { - return "", errors.New("must call NextPage with an empty buffer") - } - // The buffer must be empty here, so takeBuf is a no-op. We call it just to get - // the buffer's type. - wantSliceType := reflect.PtrTo(reflect.ValueOf(p.pageInfo.takeBuf()).Type()) - if slicep == nil { - return "", errors.New("nil passed to Pager.NextPage") - } - vslicep := reflect.ValueOf(slicep) - if vslicep.Type() != wantSliceType { - return "", fmt.Errorf("slicep should be of type %s, got %T", wantSliceType, slicep) - } - for p.pageInfo.bufLen() < p.pageSize { - if err := p.pageInfo.fill(p.pageSize - p.pageInfo.bufLen()); err != nil { - p.pageInfo.err = err - return "", p.pageInfo.err - } - if p.pageInfo.Token == "" { - break - } - } - e := vslicep.Elem() - e.Set(reflect.AppendSlice(e, reflect.ValueOf(p.pageInfo.takeBuf()))) - return p.pageInfo.Token, nil -} diff --git a/vendor/google.golang.org/api/option/credentials_go19.go b/vendor/google.golang.org/api/option/credentials_go19.go deleted file mode 100644 index c08c1149d28..00000000000 --- a/vendor/google.golang.org/api/option/credentials_go19.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018 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. - -// +build go1.9 - -package option - -import ( - "golang.org/x/oauth2/google" - "google.golang.org/api/internal" -) - -type withCreds google.Credentials - -func (w *withCreds) Apply(o *internal.DialSettings) { - o.Credentials = (*google.Credentials)(w) -} - -func WithCredentials(creds *google.Credentials) ClientOption { - return (*withCreds)(creds) -} diff --git a/vendor/google.golang.org/api/option/credentials_notgo19.go b/vendor/google.golang.org/api/option/credentials_notgo19.go deleted file mode 100644 index 90d22900102..00000000000 --- a/vendor/google.golang.org/api/option/credentials_notgo19.go +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2018 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. - -// +build !go1.9 - -package option - -import ( - "golang.org/x/oauth2/google" - "google.golang.org/api/internal" -) - -type withCreds google.DefaultCredentials - -func (w *withCreds) Apply(o *internal.DialSettings) { - o.Credentials = (*google.DefaultCredentials)(w) -} - -func WithCredentials(creds *google.DefaultCredentials) ClientOption { - return (*withCreds)(creds) -} diff --git a/vendor/google.golang.org/api/option/option.go b/vendor/google.golang.org/api/option/option.go deleted file mode 100644 index ffbee329511..00000000000 --- a/vendor/google.golang.org/api/option/option.go +++ /dev/null @@ -1,175 +0,0 @@ -// Copyright 2017 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 option contains options for Google API clients. -package option - -import ( - "net/http" - - "golang.org/x/oauth2" - "google.golang.org/api/internal" - "google.golang.org/grpc" -) - -// A ClientOption is an option for a Google API client. -type ClientOption interface { - Apply(*internal.DialSettings) -} - -// WithTokenSource returns a ClientOption that specifies an OAuth2 token -// source to be used as the basis for authentication. -func WithTokenSource(s oauth2.TokenSource) ClientOption { - return withTokenSource{s} -} - -type withTokenSource struct{ ts oauth2.TokenSource } - -func (w withTokenSource) Apply(o *internal.DialSettings) { - o.TokenSource = w.ts -} - -type withCredFile string - -func (w withCredFile) Apply(o *internal.DialSettings) { - o.CredentialsFile = string(w) -} - -// WithCredentialsFile returns a ClientOption that authenticates -// API calls with the given service account or refresh token JSON -// credentials file. -func WithCredentialsFile(filename string) ClientOption { - return withCredFile(filename) -} - -// WithServiceAccountFile returns a ClientOption that uses a Google service -// account credentials file to authenticate. -// -// Deprecated: Use WithCredentialsFile instead. -func WithServiceAccountFile(filename string) ClientOption { - return WithCredentialsFile(filename) -} - -// WithEndpoint returns a ClientOption that overrides the default endpoint -// to be used for a service. -func WithEndpoint(url string) ClientOption { - return withEndpoint(url) -} - -type withEndpoint string - -func (w withEndpoint) Apply(o *internal.DialSettings) { - o.Endpoint = string(w) -} - -// WithScopes returns a ClientOption that overrides the default OAuth2 scopes -// to be used for a service. -func WithScopes(scope ...string) ClientOption { - return withScopes(scope) -} - -type withScopes []string - -func (w withScopes) Apply(o *internal.DialSettings) { - s := make([]string, len(w)) - copy(s, w) - o.Scopes = s -} - -// WithUserAgent returns a ClientOption that sets the User-Agent. -func WithUserAgent(ua string) ClientOption { - return withUA(ua) -} - -type withUA string - -func (w withUA) Apply(o *internal.DialSettings) { o.UserAgent = string(w) } - -// WithHTTPClient returns a ClientOption that specifies the HTTP client to use -// as the basis of communications. This option may only be used with services -// that support HTTP as their communication transport. When used, the -// WithHTTPClient option takes precedent over all other supplied options. -func WithHTTPClient(client *http.Client) ClientOption { - return withHTTPClient{client} -} - -type withHTTPClient struct{ client *http.Client } - -func (w withHTTPClient) Apply(o *internal.DialSettings) { - o.HTTPClient = w.client -} - -// WithGRPCConn returns a ClientOption that specifies the gRPC client -// connection to use as the basis of communications. This option many only be -// used with services that support gRPC as their communication transport. When -// used, the WithGRPCConn option takes precedent over all other supplied -// options. -func WithGRPCConn(conn *grpc.ClientConn) ClientOption { - return withGRPCConn{conn} -} - -type withGRPCConn struct{ conn *grpc.ClientConn } - -func (w withGRPCConn) Apply(o *internal.DialSettings) { - o.GRPCConn = w.conn -} - -// WithGRPCDialOption returns a ClientOption that appends a new grpc.DialOption -// to an underlying gRPC dial. It does not work with WithGRPCConn. -func WithGRPCDialOption(opt grpc.DialOption) ClientOption { - return withGRPCDialOption{opt} -} - -type withGRPCDialOption struct{ opt grpc.DialOption } - -func (w withGRPCDialOption) Apply(o *internal.DialSettings) { - o.GRPCDialOpts = append(o.GRPCDialOpts, w.opt) -} - -// WithGRPCConnectionPool returns a ClientOption that creates a pool of gRPC -// connections that requests will be balanced between. -// This is an EXPERIMENTAL API and may be changed or removed in the future. -func WithGRPCConnectionPool(size int) ClientOption { - return withGRPCConnectionPool(size) -} - -type withGRPCConnectionPool int - -func (w withGRPCConnectionPool) Apply(o *internal.DialSettings) { - balancer := grpc.RoundRobin(internal.NewPoolResolver(int(w), o)) - o.GRPCDialOpts = append(o.GRPCDialOpts, grpc.WithBalancer(balancer)) -} - -// WithAPIKey returns a ClientOption that specifies an API key to be used -// as the basis for authentication. -func WithAPIKey(apiKey string) ClientOption { - return withAPIKey(apiKey) -} - -type withAPIKey string - -func (w withAPIKey) Apply(o *internal.DialSettings) { o.APIKey = string(w) } - -// WithoutAuthentication returns a ClientOption that specifies that no -// authentication should be used. It is suitable only for testing and for -// accessing public resources, like public Google Cloud Storage buckets. -// It is an error to provide both WithoutAuthentication and any of WithAPIKey, -// WithTokenSource, WithCredentialsFile or WithServiceAccountFile. -func WithoutAuthentication() ClientOption { - return withoutAuthentication{} -} - -type withoutAuthentication struct{} - -func (w withoutAuthentication) Apply(o *internal.DialSettings) { o.NoAuth = true } diff --git a/vendor/google.golang.org/api/support/bundler/bundler.go b/vendor/google.golang.org/api/support/bundler/bundler.go deleted file mode 100644 index 8d8fb7f0471..00000000000 --- a/vendor/google.golang.org/api/support/bundler/bundler.go +++ /dev/null @@ -1,349 +0,0 @@ -// Copyright 2016 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 bundler supports bundling (batching) of items. Bundling amortizes an -// action with fixed costs over multiple items. For example, if an API provides -// an RPC that accepts a list of items as input, but clients would prefer -// adding items one at a time, then a Bundler can accept individual items from -// the client and bundle many of them into a single RPC. -// -// This package is experimental and subject to change without notice. -package bundler - -import ( - "errors" - "math" - "reflect" - "sync" - "time" - - "golang.org/x/net/context" - "golang.org/x/sync/semaphore" -) - -const ( - DefaultDelayThreshold = time.Second - DefaultBundleCountThreshold = 10 - DefaultBundleByteThreshold = 1e6 // 1M - DefaultBufferedByteLimit = 1e9 // 1G -) - -var ( - // ErrOverflow indicates that Bundler's stored bytes exceeds its BufferedByteLimit. - ErrOverflow = errors.New("bundler reached buffered byte limit") - - // ErrOversizedItem indicates that an item's size exceeds the maximum bundle size. - ErrOversizedItem = errors.New("item size exceeds bundle byte limit") -) - -// A Bundler collects items added to it into a bundle until the bundle -// exceeds a given size, then calls a user-provided function to handle the bundle. -type Bundler struct { - // Starting from the time that the first message is added to a bundle, once - // this delay has passed, handle the bundle. The default is DefaultDelayThreshold. - DelayThreshold time.Duration - - // Once a bundle has this many items, handle the bundle. Since only one - // item at a time is added to a bundle, no bundle will exceed this - // threshold, so it also serves as a limit. The default is - // DefaultBundleCountThreshold. - BundleCountThreshold int - - // Once the number of bytes in current bundle reaches this threshold, handle - // the bundle. The default is DefaultBundleByteThreshold. This triggers handling, - // but does not cap the total size of a bundle. - BundleByteThreshold int - - // The maximum size of a bundle, in bytes. Zero means unlimited. - BundleByteLimit int - - // The maximum number of bytes that the Bundler will keep in memory before - // returning ErrOverflow. The default is DefaultBufferedByteLimit. - BufferedByteLimit int - - // The maximum number of handler invocations that can be running at once. - // The default is 1. - HandlerLimit int - - handler func(interface{}) // called to handle a bundle - itemSliceZero reflect.Value // nil (zero value) for slice of items - flushTimer *time.Timer // implements DelayThreshold - - mu sync.Mutex - sem *semaphore.Weighted // enforces BufferedByteLimit - semOnce sync.Once - curBundle bundle // incoming items added to this bundle - - // Each bundle is assigned a unique ticket that determines the order in which the - // handler is called. The ticket is assigned with mu locked, but waiting for tickets - // to be handled is done via mu2 and cond, below. - nextTicket uint64 // next ticket to be assigned - - mu2 sync.Mutex - cond *sync.Cond - nextHandled uint64 // next ticket to be handled - - // In this implementation, active uses space proportional to HandlerLimit, and - // waitUntilAllHandled takes time proportional to HandlerLimit each time an acquire - // or release occurs, so large values of HandlerLimit max may cause performance - // issues. - active map[uint64]bool // tickets of bundles actively being handled -} - -type bundle struct { - items reflect.Value // slice of item type - size int // size in bytes of all items -} - -// NewBundler creates a new Bundler. -// -// itemExample is a value of the type that will be bundled. For example, if you -// want to create bundles of *Entry, you could pass &Entry{} for itemExample. -// -// handler is a function that will be called on each bundle. If itemExample is -// of type T, the argument to handler is of type []T. handler is always called -// sequentially for each bundle, and never in parallel. -// -// Configure the Bundler by setting its thresholds and limits before calling -// any of its methods. -func NewBundler(itemExample interface{}, handler func(interface{})) *Bundler { - b := &Bundler{ - DelayThreshold: DefaultDelayThreshold, - BundleCountThreshold: DefaultBundleCountThreshold, - BundleByteThreshold: DefaultBundleByteThreshold, - BufferedByteLimit: DefaultBufferedByteLimit, - HandlerLimit: 1, - - handler: handler, - itemSliceZero: reflect.Zero(reflect.SliceOf(reflect.TypeOf(itemExample))), - active: map[uint64]bool{}, - } - b.curBundle.items = b.itemSliceZero - b.cond = sync.NewCond(&b.mu2) - return b -} - -func (b *Bundler) initSemaphores() { - // Create the semaphores lazily, because the user may set limits - // after NewBundler. - b.semOnce.Do(func() { - b.sem = semaphore.NewWeighted(int64(b.BufferedByteLimit)) - }) -} - -// Add adds item to the current bundle. It marks the bundle for handling and -// starts a new one if any of the thresholds or limits are exceeded. -// -// If the item's size exceeds the maximum bundle size (Bundler.BundleByteLimit), then -// the item can never be handled. Add returns ErrOversizedItem in this case. -// -// If adding the item would exceed the maximum memory allowed -// (Bundler.BufferedByteLimit) or an AddWait call is blocked waiting for -// memory, Add returns ErrOverflow. -// -// Add never blocks. -func (b *Bundler) Add(item interface{}, size int) error { - // If this item exceeds the maximum size of a bundle, - // we can never send it. - if b.BundleByteLimit > 0 && size > b.BundleByteLimit { - return ErrOversizedItem - } - // If adding this item would exceed our allotted memory - // footprint, we can't accept it. - // (TryAcquire also returns false if anything is waiting on the semaphore, - // so calls to Add and AddWait shouldn't be mixed.) - b.initSemaphores() - if !b.sem.TryAcquire(int64(size)) { - return ErrOverflow - } - b.add(item, size) - return nil -} - -// add adds item to the current bundle. It marks the bundle for handling and -// starts a new one if any of the thresholds or limits are exceeded. -func (b *Bundler) add(item interface{}, size int) { - b.mu.Lock() - defer b.mu.Unlock() - - // If adding this item to the current bundle would cause it to exceed the - // maximum bundle size, close the current bundle and start a new one. - if b.BundleByteLimit > 0 && b.curBundle.size+size > b.BundleByteLimit { - b.startFlushLocked() - } - // Add the item. - b.curBundle.items = reflect.Append(b.curBundle.items, reflect.ValueOf(item)) - b.curBundle.size += size - - // Start a timer to flush the item if one isn't already running. - // startFlushLocked clears the timer and closes the bundle at the same time, - // so we only allocate a new timer for the first item in each bundle. - // (We could try to call Reset on the timer instead, but that would add a lot - // of complexity to the code just to save one small allocation.) - if b.flushTimer == nil { - b.flushTimer = time.AfterFunc(b.DelayThreshold, b.Flush) - } - - // If the current bundle equals the count threshold, close it. - if b.curBundle.items.Len() == b.BundleCountThreshold { - b.startFlushLocked() - } - // If the current bundle equals or exceeds the byte threshold, close it. - if b.curBundle.size >= b.BundleByteThreshold { - b.startFlushLocked() - } -} - -// AddWait adds item to the current bundle. It marks the bundle for handling and -// starts a new one if any of the thresholds or limits are exceeded. -// -// If the item's size exceeds the maximum bundle size (Bundler.BundleByteLimit), then -// the item can never be handled. AddWait returns ErrOversizedItem in this case. -// -// If adding the item would exceed the maximum memory allowed (Bundler.BufferedByteLimit), -// AddWait blocks until space is available or ctx is done. -// -// Calls to Add and AddWait should not be mixed on the same Bundler. -func (b *Bundler) AddWait(ctx context.Context, item interface{}, size int) error { - // If this item exceeds the maximum size of a bundle, - // we can never send it. - if b.BundleByteLimit > 0 && size > b.BundleByteLimit { - return ErrOversizedItem - } - // If adding this item would exceed our allotted memory footprint, block - // until space is available. The semaphore is FIFO, so there will be no - // starvation. - b.initSemaphores() - if err := b.sem.Acquire(ctx, int64(size)); err != nil { - return err - } - // Here, we've reserved space for item. Other goroutines can call AddWait - // and even acquire space, but no one can take away our reservation - // (assuming sem.Release is used correctly). So there is no race condition - // resulting from locking the mutex after sem.Acquire returns. - b.add(item, size) - return nil -} - -// Flush invokes the handler for all remaining items in the Bundler and waits -// for it to return. -func (b *Bundler) Flush() { - b.mu.Lock() - b.startFlushLocked() - // Here, all bundles with tickets < b.nextTicket are - // either finished or active. Those are the ones - // we want to wait for. - t := b.nextTicket - b.mu.Unlock() - b.initSemaphores() - b.waitUntilAllHandled(t) -} - -func (b *Bundler) startFlushLocked() { - if b.flushTimer != nil { - b.flushTimer.Stop() - b.flushTimer = nil - } - if b.curBundle.items.Len() == 0 { - return - } - // Here, both semaphores must have been initialized. - bun := b.curBundle - b.curBundle = bundle{items: b.itemSliceZero} - ticket := b.nextTicket - b.nextTicket++ - go func() { - defer func() { - b.sem.Release(int64(bun.size)) - b.release(ticket) - }() - b.acquire(ticket) - b.handler(bun.items.Interface()) - }() -} - -// acquire blocks until ticket is the next to be served, then returns. In order for N -// acquire calls to return, the tickets must be in the range [0, N). A ticket must -// not be presented to acquire more than once. -func (b *Bundler) acquire(ticket uint64) { - b.mu2.Lock() - defer b.mu2.Unlock() - if ticket < b.nextHandled { - panic("bundler: acquire: arg too small") - } - for !(ticket == b.nextHandled && len(b.active) < b.HandlerLimit) { - b.cond.Wait() - } - // Here, - // ticket == b.nextHandled: the caller is the next one to be handled; - // and len(b.active) < b.HandlerLimit: there is space available. - b.active[ticket] = true - b.nextHandled++ - // Broadcast, not Signal: although at most one acquire waiter can make progress, - // there might be waiters in waitUntilAllHandled. - b.cond.Broadcast() -} - -// If a ticket is used for a call to acquire, it must later be passed to release. A -// ticket must not be presented to release more than once. -func (b *Bundler) release(ticket uint64) { - b.mu2.Lock() - defer b.mu2.Unlock() - if !b.active[ticket] { - panic("bundler: release: not an active ticket") - } - delete(b.active, ticket) - b.cond.Broadcast() -} - -// waitUntilAllHandled blocks until all tickets < n have called release, meaning -// all bundles with tickets < n have been handled. -func (b *Bundler) waitUntilAllHandled(n uint64) { - // Proof of correctness of this function. - // "N is acquired" means acquire(N) has returned. - // "N is released" means release(N) has returned. - // 1. If N is acquired, N-1 is acquired. - // Follows from the loop test in acquire, and the fact - // that nextHandled is incremented by 1. - // 2. If nextHandled >= N, then N-1 is acquired. - // Because we only increment nextHandled to N after N-1 is acquired. - // 3. If nextHandled >= N, then all n < N is acquired. - // Follows from #1 and #2. - // 4. If N is acquired and N is not in active, then N is released. - // Because we put N in active before acquire returns, and only - // remove it when it is released. - // Let min(active) be the smallest member of active, or infinity if active is empty. - // 5. If nextHandled >= N and N <= min(active), then all n < N is released. - // From nextHandled >= N and #3, all n < N is acquired. - // N <= min(active) implies n < min(active) for all n < N. So all n < N is not in active. - // So from #4, all n < N is released. - // The loop test below is the antecedent of #5. - b.mu2.Lock() - defer b.mu2.Unlock() - for !(b.nextHandled >= n && n <= min(b.active)) { - b.cond.Wait() - } -} - -// min returns the minimum value of the set s, or the largest uint64 if -// s is empty. -func min(s map[uint64]bool) uint64 { - var m uint64 = math.MaxUint64 - for n := range s { - if n < m { - m = n - } - } - return m -} diff --git a/vendor/google.golang.org/api/transport/dial.go b/vendor/google.golang.org/api/transport/dial.go deleted file mode 100644 index c4c37191bf0..00000000000 --- a/vendor/google.golang.org/api/transport/dial.go +++ /dev/null @@ -1,49 +0,0 @@ -// 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 transport supports network connections to HTTP and GRPC servers. -// This package is not intended for use by end developers. Use the -// google.golang.org/api/option package to configure API clients. -package transport - -import ( - "net/http" - - "golang.org/x/net/context" - "google.golang.org/grpc" - - "google.golang.org/api/option" - gtransport "google.golang.org/api/transport/grpc" - htransport "google.golang.org/api/transport/http" -) - -// NewHTTPClient returns an HTTP client for use communicating with a Google cloud -// service, configured with the given ClientOptions. It also returns the endpoint -// for the service as specified in the options. -func NewHTTPClient(ctx context.Context, opts ...option.ClientOption) (*http.Client, string, error) { - return htransport.NewClient(ctx, opts...) -} - -// DialGRPC returns a GRPC connection for use communicating with a Google cloud -// service, configured with the given ClientOptions. -func DialGRPC(ctx context.Context, opts ...option.ClientOption) (*grpc.ClientConn, error) { - return gtransport.Dial(ctx, opts...) -} - -// DialGRPCInsecure returns an insecure GRPC connection for use communicating -// with fake or mock Google cloud service implementations, such as emulators. -// The connection is configured with the given ClientOptions. -func DialGRPCInsecure(ctx context.Context, opts ...option.ClientOption) (*grpc.ClientConn, error) { - return gtransport.DialInsecure(ctx, opts...) -} diff --git a/vendor/google.golang.org/api/transport/go19.go b/vendor/google.golang.org/api/transport/go19.go deleted file mode 100644 index 0177e5600cf..00000000000 --- a/vendor/google.golang.org/api/transport/go19.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018 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. - -// +build go1.9 - -package transport - -import ( - "golang.org/x/net/context" - "golang.org/x/oauth2/google" - "google.golang.org/api/internal" - "google.golang.org/api/option" -) - -// Creds constructs a google.Credentials from the information in the options, -// or obtains the default credentials in the same way as google.FindDefaultCredentials. -func Creds(ctx context.Context, opts ...option.ClientOption) (*google.Credentials, error) { - var ds internal.DialSettings - for _, opt := range opts { - opt.Apply(&ds) - } - return internal.Creds(ctx, &ds) -} diff --git a/vendor/google.golang.org/api/transport/grpc/dial.go b/vendor/google.golang.org/api/transport/grpc/dial.go deleted file mode 100644 index 2b8ed6b0b80..00000000000 --- a/vendor/google.golang.org/api/transport/grpc/dial.go +++ /dev/null @@ -1,87 +0,0 @@ -// 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 transport/grpc supports network connections to GRPC servers. -// This package is not intended for use by end developers. Use the -// google.golang.org/api/option package to configure API clients. -package grpc - -import ( - "errors" - - "golang.org/x/net/context" - "google.golang.org/api/internal" - "google.golang.org/api/option" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/oauth" -) - -// Set at init time by dial_appengine.go. If nil, we're not on App Engine. -var appengineDialerHook func(context.Context) grpc.DialOption - -// Dial returns a GRPC connection for use communicating with a Google cloud -// service, configured with the given ClientOptions. -func Dial(ctx context.Context, opts ...option.ClientOption) (*grpc.ClientConn, error) { - return dial(ctx, false, opts) -} - -// DialInsecure returns an insecure GRPC connection for use communicating -// with fake or mock Google cloud service implementations, such as emulators. -// The connection is configured with the given ClientOptions. -func DialInsecure(ctx context.Context, opts ...option.ClientOption) (*grpc.ClientConn, error) { - return dial(ctx, true, opts) -} - -func dial(ctx context.Context, insecure bool, opts []option.ClientOption) (*grpc.ClientConn, error) { - var o internal.DialSettings - for _, opt := range opts { - opt.Apply(&o) - } - if err := o.Validate(); err != nil { - return nil, err - } - if o.HTTPClient != nil { - return nil, errors.New("unsupported HTTP client specified") - } - if o.GRPCConn != nil { - return o.GRPCConn, nil - } - var grpcOpts []grpc.DialOption - if insecure { - grpcOpts = []grpc.DialOption{grpc.WithInsecure()} - } else if !o.NoAuth { - creds, err := internal.Creds(ctx, &o) - if err != nil { - return nil, err - } - grpcOpts = []grpc.DialOption{ - grpc.WithPerRPCCredentials(oauth.TokenSource{creds.TokenSource}), - grpc.WithTransportCredentials(credentials.NewClientTLSFromCert(nil, "")), - } - } - if appengineDialerHook != nil { - // Use the Socket API on App Engine. - grpcOpts = append(grpcOpts, appengineDialerHook(ctx)) - } - // Add tracing, but before the other options, so that clients can override the - // gRPC stats handler. - // This assumes that gRPC options are processed in order, left to right. - grpcOpts = addOCStatsHandler(grpcOpts) - grpcOpts = append(grpcOpts, o.GRPCDialOpts...) - if o.UserAgent != "" { - grpcOpts = append(grpcOpts, grpc.WithUserAgent(o.UserAgent)) - } - return grpc.DialContext(ctx, o.Endpoint, grpcOpts...) -} diff --git a/vendor/google.golang.org/api/transport/grpc/dial_appengine.go b/vendor/google.golang.org/api/transport/grpc/dial_appengine.go deleted file mode 100644 index a40cef2506a..00000000000 --- a/vendor/google.golang.org/api/transport/grpc/dial_appengine.go +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2016 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. - -// +build appengine - -package grpc - -import ( - "net" - "time" - - "golang.org/x/net/context" - "google.golang.org/appengine" - "google.golang.org/appengine/socket" - "google.golang.org/grpc" -) - -func init() { - // NOTE: dev_appserver doesn't currently support SSL. - // When it does, this code can be removed. - if appengine.IsDevAppServer() { - return - } - - appengineDialerHook = func(ctx context.Context) grpc.DialOption { - return grpc.WithDialer(func(addr string, timeout time.Duration) (net.Conn, error) { - return socket.DialTimeout(ctx, "tcp", addr, timeout) - }) - } -} diff --git a/vendor/google.golang.org/api/transport/grpc/go18.go b/vendor/google.golang.org/api/transport/grpc/go18.go deleted file mode 100644 index a4b4a994535..00000000000 --- a/vendor/google.golang.org/api/transport/grpc/go18.go +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2018 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. - -// +build go1.8 - -package grpc - -import ( - "go.opencensus.io/plugin/ocgrpc" - "google.golang.org/grpc" -) - -func addOCStatsHandler(opts []grpc.DialOption) []grpc.DialOption { - return append(opts, grpc.WithStatsHandler(&ocgrpc.ClientHandler{})) -} diff --git a/vendor/google.golang.org/api/transport/grpc/not_go18.go b/vendor/google.golang.org/api/transport/grpc/not_go18.go deleted file mode 100644 index f509d8636c5..00000000000 --- a/vendor/google.golang.org/api/transport/grpc/not_go18.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 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. - -// +build !go1.8 - -package grpc - -import "google.golang.org/grpc" - -func addOCStatsHandler(opts []grpc.DialOption) []grpc.DialOption { return opts } diff --git a/vendor/google.golang.org/api/transport/http/dial.go b/vendor/google.golang.org/api/transport/http/dial.go deleted file mode 100644 index 5184ff512c6..00000000000 --- a/vendor/google.golang.org/api/transport/http/dial.go +++ /dev/null @@ -1,138 +0,0 @@ -// 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 transport/http supports network connections to HTTP servers. -// This package is not intended for use by end developers. Use the -// google.golang.org/api/option package to configure API clients. -package http - -import ( - "errors" - "net/http" - - "golang.org/x/net/context" - "golang.org/x/oauth2" - "google.golang.org/api/googleapi/transport" - "google.golang.org/api/internal" - "google.golang.org/api/option" -) - -// NewClient returns an HTTP client for use communicating with a Google cloud -// service, configured with the given ClientOptions. It also returns the endpoint -// for the service as specified in the options. -func NewClient(ctx context.Context, opts ...option.ClientOption) (*http.Client, string, error) { - settings, err := newSettings(opts) - if err != nil { - return nil, "", err - } - // TODO(cbro): consider injecting the User-Agent even if an explicit HTTP client is provided? - if settings.HTTPClient != nil { - return settings.HTTPClient, settings.Endpoint, nil - } - trans, err := newTransport(ctx, defaultBaseTransport(ctx), settings) - if err != nil { - return nil, "", err - } - return &http.Client{Transport: trans}, settings.Endpoint, nil -} - -// NewTransport creates an http.RoundTripper for use communicating with a Google -// cloud service, configured with the given ClientOptions. Its RoundTrip method delegates to base. -func NewTransport(ctx context.Context, base http.RoundTripper, opts ...option.ClientOption) (http.RoundTripper, error) { - settings, err := newSettings(opts) - if err != nil { - return nil, err - } - if settings.HTTPClient != nil { - return nil, errors.New("transport/http: WithHTTPClient passed to NewTransport") - } - return newTransport(ctx, base, settings) -} - -func newTransport(ctx context.Context, base http.RoundTripper, settings *internal.DialSettings) (http.RoundTripper, error) { - trans := base - trans = userAgentTransport{ - base: trans, - userAgent: settings.UserAgent, - } - trans = addOCTransport(trans) - switch { - case settings.NoAuth: - // Do nothing. - case settings.APIKey != "": - trans = &transport.APIKey{ - Transport: trans, - Key: settings.APIKey, - } - default: - creds, err := internal.Creds(ctx, settings) - if err != nil { - return nil, err - } - trans = &oauth2.Transport{ - Base: trans, - Source: creds.TokenSource, - } - } - return trans, nil -} - -func newSettings(opts []option.ClientOption) (*internal.DialSettings, error) { - var o internal.DialSettings - for _, opt := range opts { - opt.Apply(&o) - } - if err := o.Validate(); err != nil { - return nil, err - } - if o.GRPCConn != nil { - return nil, errors.New("unsupported gRPC connection specified") - } - return &o, nil -} - -type userAgentTransport struct { - userAgent string - base http.RoundTripper -} - -func (t userAgentTransport) RoundTrip(req *http.Request) (*http.Response, error) { - rt := t.base - if rt == nil { - return nil, errors.New("transport: no Transport specified") - } - if t.userAgent == "" { - return rt.RoundTrip(req) - } - newReq := *req - newReq.Header = make(http.Header) - for k, vv := range req.Header { - newReq.Header[k] = vv - } - // TODO(cbro): append to existing User-Agent header? - newReq.Header["User-Agent"] = []string{t.userAgent} - return rt.RoundTrip(&newReq) -} - -// Set at init time by dial_appengine.go. If nil, we're not on App Engine. -var appengineUrlfetchHook func(context.Context) http.RoundTripper - -// defaultBaseTransport returns the base HTTP transport. -// On App Engine, this is urlfetch.Transport, otherwise it's http.DefaultTransport. -func defaultBaseTransport(ctx context.Context) http.RoundTripper { - if appengineUrlfetchHook != nil { - return appengineUrlfetchHook(ctx) - } - return http.DefaultTransport -} diff --git a/vendor/google.golang.org/api/transport/http/dial_appengine.go b/vendor/google.golang.org/api/transport/http/dial_appengine.go deleted file mode 100644 index 0cdef74ac13..00000000000 --- a/vendor/google.golang.org/api/transport/http/dial_appengine.go +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2016 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. - -// +build appengine - -package http - -import ( - "net/http" - - "golang.org/x/net/context" - "google.golang.org/appengine/urlfetch" -) - -func init() { - appengineUrlfetchHook = func(ctx context.Context) http.RoundTripper { - return &urlfetch.Transport{Context: ctx} - } -} diff --git a/vendor/google.golang.org/api/transport/http/go18.go b/vendor/google.golang.org/api/transport/http/go18.go deleted file mode 100644 index 1d4bb8e7fb4..00000000000 --- a/vendor/google.golang.org/api/transport/http/go18.go +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2018 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. - -// +build go1.8 - -package http - -import ( - "net/http" - - "go.opencensus.io/exporter/stackdriver/propagation" - "go.opencensus.io/plugin/ochttp" -) - -func addOCTransport(trans http.RoundTripper) http.RoundTripper { - return &ochttp.Transport{ - Base: trans, - Propagation: &propagation.HTTPFormat{}, - } -} diff --git a/vendor/google.golang.org/api/transport/http/not_go18.go b/vendor/google.golang.org/api/transport/http/not_go18.go deleted file mode 100644 index 628a21a84d6..00000000000 --- a/vendor/google.golang.org/api/transport/http/not_go18.go +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2018 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. - -// +build !go1.8 - -package http - -import "net/http" - -func addOCTransport(trans http.RoundTripper) http.RoundTripper { return trans } diff --git a/vendor/google.golang.org/api/transport/not_go19.go b/vendor/google.golang.org/api/transport/not_go19.go deleted file mode 100644 index 4489bc9b6e4..00000000000 --- a/vendor/google.golang.org/api/transport/not_go19.go +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2018 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. - -// +build !go1.9 - -package transport - -import ( - "golang.org/x/net/context" - "golang.org/x/oauth2/google" - "google.golang.org/api/internal" - "google.golang.org/api/option" -) - -// Creds constructs a google.DefaultCredentials from the information in the options, -// or obtains the default credentials in the same way as google.FindDefaultCredentials. -func Creds(ctx context.Context, opts ...option.ClientOption) (*google.DefaultCredentials, error) { - var ds internal.DialSettings - for _, opt := range opts { - opt.Apply(&ds) - } - return internal.Creds(ctx, &ds) -} diff --git a/vendor/google.golang.org/appengine/internal/socket/socket_service.pb.go b/vendor/google.golang.org/appengine/internal/socket/socket_service.pb.go deleted file mode 100644 index 60628ec9b9c..00000000000 --- a/vendor/google.golang.org/appengine/internal/socket/socket_service.pb.go +++ /dev/null @@ -1,1858 +0,0 @@ -// Code generated by protoc-gen-go. -// source: google.golang.org/appengine/internal/socket/socket_service.proto -// DO NOT EDIT! - -/* -Package socket is a generated protocol buffer package. - -It is generated from these files: - google.golang.org/appengine/internal/socket/socket_service.proto - -It has these top-level messages: - RemoteSocketServiceError - AddressPort - CreateSocketRequest - CreateSocketReply - BindRequest - BindReply - GetSocketNameRequest - GetSocketNameReply - GetPeerNameRequest - GetPeerNameReply - SocketOption - SetSocketOptionsRequest - SetSocketOptionsReply - GetSocketOptionsRequest - GetSocketOptionsReply - ConnectRequest - ConnectReply - ListenRequest - ListenReply - AcceptRequest - AcceptReply - ShutDownRequest - ShutDownReply - CloseRequest - CloseReply - SendRequest - SendReply - ReceiveRequest - ReceiveReply - PollEvent - PollRequest - PollReply - ResolveRequest - ResolveReply -*/ -package socket - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -type RemoteSocketServiceError_ErrorCode int32 - -const ( - RemoteSocketServiceError_SYSTEM_ERROR RemoteSocketServiceError_ErrorCode = 1 - RemoteSocketServiceError_GAI_ERROR RemoteSocketServiceError_ErrorCode = 2 - RemoteSocketServiceError_FAILURE RemoteSocketServiceError_ErrorCode = 4 - RemoteSocketServiceError_PERMISSION_DENIED RemoteSocketServiceError_ErrorCode = 5 - RemoteSocketServiceError_INVALID_REQUEST RemoteSocketServiceError_ErrorCode = 6 - RemoteSocketServiceError_SOCKET_CLOSED RemoteSocketServiceError_ErrorCode = 7 -) - -var RemoteSocketServiceError_ErrorCode_name = map[int32]string{ - 1: "SYSTEM_ERROR", - 2: "GAI_ERROR", - 4: "FAILURE", - 5: "PERMISSION_DENIED", - 6: "INVALID_REQUEST", - 7: "SOCKET_CLOSED", -} -var RemoteSocketServiceError_ErrorCode_value = map[string]int32{ - "SYSTEM_ERROR": 1, - "GAI_ERROR": 2, - "FAILURE": 4, - "PERMISSION_DENIED": 5, - "INVALID_REQUEST": 6, - "SOCKET_CLOSED": 7, -} - -func (x RemoteSocketServiceError_ErrorCode) Enum() *RemoteSocketServiceError_ErrorCode { - p := new(RemoteSocketServiceError_ErrorCode) - *p = x - return p -} -func (x RemoteSocketServiceError_ErrorCode) String() string { - return proto.EnumName(RemoteSocketServiceError_ErrorCode_name, int32(x)) -} -func (x *RemoteSocketServiceError_ErrorCode) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(RemoteSocketServiceError_ErrorCode_value, data, "RemoteSocketServiceError_ErrorCode") - if err != nil { - return err - } - *x = RemoteSocketServiceError_ErrorCode(value) - return nil -} - -type RemoteSocketServiceError_SystemError int32 - -const ( - RemoteSocketServiceError_SYS_SUCCESS RemoteSocketServiceError_SystemError = 0 - RemoteSocketServiceError_SYS_EPERM RemoteSocketServiceError_SystemError = 1 - RemoteSocketServiceError_SYS_ENOENT RemoteSocketServiceError_SystemError = 2 - RemoteSocketServiceError_SYS_ESRCH RemoteSocketServiceError_SystemError = 3 - RemoteSocketServiceError_SYS_EINTR RemoteSocketServiceError_SystemError = 4 - RemoteSocketServiceError_SYS_EIO RemoteSocketServiceError_SystemError = 5 - RemoteSocketServiceError_SYS_ENXIO RemoteSocketServiceError_SystemError = 6 - RemoteSocketServiceError_SYS_E2BIG RemoteSocketServiceError_SystemError = 7 - RemoteSocketServiceError_SYS_ENOEXEC RemoteSocketServiceError_SystemError = 8 - RemoteSocketServiceError_SYS_EBADF RemoteSocketServiceError_SystemError = 9 - RemoteSocketServiceError_SYS_ECHILD RemoteSocketServiceError_SystemError = 10 - RemoteSocketServiceError_SYS_EAGAIN RemoteSocketServiceError_SystemError = 11 - RemoteSocketServiceError_SYS_EWOULDBLOCK RemoteSocketServiceError_SystemError = 11 - RemoteSocketServiceError_SYS_ENOMEM RemoteSocketServiceError_SystemError = 12 - RemoteSocketServiceError_SYS_EACCES RemoteSocketServiceError_SystemError = 13 - RemoteSocketServiceError_SYS_EFAULT RemoteSocketServiceError_SystemError = 14 - RemoteSocketServiceError_SYS_ENOTBLK RemoteSocketServiceError_SystemError = 15 - RemoteSocketServiceError_SYS_EBUSY RemoteSocketServiceError_SystemError = 16 - RemoteSocketServiceError_SYS_EEXIST RemoteSocketServiceError_SystemError = 17 - RemoteSocketServiceError_SYS_EXDEV RemoteSocketServiceError_SystemError = 18 - RemoteSocketServiceError_SYS_ENODEV RemoteSocketServiceError_SystemError = 19 - RemoteSocketServiceError_SYS_ENOTDIR RemoteSocketServiceError_SystemError = 20 - RemoteSocketServiceError_SYS_EISDIR RemoteSocketServiceError_SystemError = 21 - RemoteSocketServiceError_SYS_EINVAL RemoteSocketServiceError_SystemError = 22 - RemoteSocketServiceError_SYS_ENFILE RemoteSocketServiceError_SystemError = 23 - RemoteSocketServiceError_SYS_EMFILE RemoteSocketServiceError_SystemError = 24 - RemoteSocketServiceError_SYS_ENOTTY RemoteSocketServiceError_SystemError = 25 - RemoteSocketServiceError_SYS_ETXTBSY RemoteSocketServiceError_SystemError = 26 - RemoteSocketServiceError_SYS_EFBIG RemoteSocketServiceError_SystemError = 27 - RemoteSocketServiceError_SYS_ENOSPC RemoteSocketServiceError_SystemError = 28 - RemoteSocketServiceError_SYS_ESPIPE RemoteSocketServiceError_SystemError = 29 - RemoteSocketServiceError_SYS_EROFS RemoteSocketServiceError_SystemError = 30 - RemoteSocketServiceError_SYS_EMLINK RemoteSocketServiceError_SystemError = 31 - RemoteSocketServiceError_SYS_EPIPE RemoteSocketServiceError_SystemError = 32 - RemoteSocketServiceError_SYS_EDOM RemoteSocketServiceError_SystemError = 33 - RemoteSocketServiceError_SYS_ERANGE RemoteSocketServiceError_SystemError = 34 - RemoteSocketServiceError_SYS_EDEADLK RemoteSocketServiceError_SystemError = 35 - RemoteSocketServiceError_SYS_EDEADLOCK RemoteSocketServiceError_SystemError = 35 - RemoteSocketServiceError_SYS_ENAMETOOLONG RemoteSocketServiceError_SystemError = 36 - RemoteSocketServiceError_SYS_ENOLCK RemoteSocketServiceError_SystemError = 37 - RemoteSocketServiceError_SYS_ENOSYS RemoteSocketServiceError_SystemError = 38 - RemoteSocketServiceError_SYS_ENOTEMPTY RemoteSocketServiceError_SystemError = 39 - RemoteSocketServiceError_SYS_ELOOP RemoteSocketServiceError_SystemError = 40 - RemoteSocketServiceError_SYS_ENOMSG RemoteSocketServiceError_SystemError = 42 - RemoteSocketServiceError_SYS_EIDRM RemoteSocketServiceError_SystemError = 43 - RemoteSocketServiceError_SYS_ECHRNG RemoteSocketServiceError_SystemError = 44 - RemoteSocketServiceError_SYS_EL2NSYNC RemoteSocketServiceError_SystemError = 45 - RemoteSocketServiceError_SYS_EL3HLT RemoteSocketServiceError_SystemError = 46 - RemoteSocketServiceError_SYS_EL3RST RemoteSocketServiceError_SystemError = 47 - RemoteSocketServiceError_SYS_ELNRNG RemoteSocketServiceError_SystemError = 48 - RemoteSocketServiceError_SYS_EUNATCH RemoteSocketServiceError_SystemError = 49 - RemoteSocketServiceError_SYS_ENOCSI RemoteSocketServiceError_SystemError = 50 - RemoteSocketServiceError_SYS_EL2HLT RemoteSocketServiceError_SystemError = 51 - RemoteSocketServiceError_SYS_EBADE RemoteSocketServiceError_SystemError = 52 - RemoteSocketServiceError_SYS_EBADR RemoteSocketServiceError_SystemError = 53 - RemoteSocketServiceError_SYS_EXFULL RemoteSocketServiceError_SystemError = 54 - RemoteSocketServiceError_SYS_ENOANO RemoteSocketServiceError_SystemError = 55 - RemoteSocketServiceError_SYS_EBADRQC RemoteSocketServiceError_SystemError = 56 - RemoteSocketServiceError_SYS_EBADSLT RemoteSocketServiceError_SystemError = 57 - RemoteSocketServiceError_SYS_EBFONT RemoteSocketServiceError_SystemError = 59 - RemoteSocketServiceError_SYS_ENOSTR RemoteSocketServiceError_SystemError = 60 - RemoteSocketServiceError_SYS_ENODATA RemoteSocketServiceError_SystemError = 61 - RemoteSocketServiceError_SYS_ETIME RemoteSocketServiceError_SystemError = 62 - RemoteSocketServiceError_SYS_ENOSR RemoteSocketServiceError_SystemError = 63 - RemoteSocketServiceError_SYS_ENONET RemoteSocketServiceError_SystemError = 64 - RemoteSocketServiceError_SYS_ENOPKG RemoteSocketServiceError_SystemError = 65 - RemoteSocketServiceError_SYS_EREMOTE RemoteSocketServiceError_SystemError = 66 - RemoteSocketServiceError_SYS_ENOLINK RemoteSocketServiceError_SystemError = 67 - RemoteSocketServiceError_SYS_EADV RemoteSocketServiceError_SystemError = 68 - RemoteSocketServiceError_SYS_ESRMNT RemoteSocketServiceError_SystemError = 69 - RemoteSocketServiceError_SYS_ECOMM RemoteSocketServiceError_SystemError = 70 - RemoteSocketServiceError_SYS_EPROTO RemoteSocketServiceError_SystemError = 71 - RemoteSocketServiceError_SYS_EMULTIHOP RemoteSocketServiceError_SystemError = 72 - RemoteSocketServiceError_SYS_EDOTDOT RemoteSocketServiceError_SystemError = 73 - RemoteSocketServiceError_SYS_EBADMSG RemoteSocketServiceError_SystemError = 74 - RemoteSocketServiceError_SYS_EOVERFLOW RemoteSocketServiceError_SystemError = 75 - RemoteSocketServiceError_SYS_ENOTUNIQ RemoteSocketServiceError_SystemError = 76 - RemoteSocketServiceError_SYS_EBADFD RemoteSocketServiceError_SystemError = 77 - RemoteSocketServiceError_SYS_EREMCHG RemoteSocketServiceError_SystemError = 78 - RemoteSocketServiceError_SYS_ELIBACC RemoteSocketServiceError_SystemError = 79 - RemoteSocketServiceError_SYS_ELIBBAD RemoteSocketServiceError_SystemError = 80 - RemoteSocketServiceError_SYS_ELIBSCN RemoteSocketServiceError_SystemError = 81 - RemoteSocketServiceError_SYS_ELIBMAX RemoteSocketServiceError_SystemError = 82 - RemoteSocketServiceError_SYS_ELIBEXEC RemoteSocketServiceError_SystemError = 83 - RemoteSocketServiceError_SYS_EILSEQ RemoteSocketServiceError_SystemError = 84 - RemoteSocketServiceError_SYS_ERESTART RemoteSocketServiceError_SystemError = 85 - RemoteSocketServiceError_SYS_ESTRPIPE RemoteSocketServiceError_SystemError = 86 - RemoteSocketServiceError_SYS_EUSERS RemoteSocketServiceError_SystemError = 87 - RemoteSocketServiceError_SYS_ENOTSOCK RemoteSocketServiceError_SystemError = 88 - RemoteSocketServiceError_SYS_EDESTADDRREQ RemoteSocketServiceError_SystemError = 89 - RemoteSocketServiceError_SYS_EMSGSIZE RemoteSocketServiceError_SystemError = 90 - RemoteSocketServiceError_SYS_EPROTOTYPE RemoteSocketServiceError_SystemError = 91 - RemoteSocketServiceError_SYS_ENOPROTOOPT RemoteSocketServiceError_SystemError = 92 - RemoteSocketServiceError_SYS_EPROTONOSUPPORT RemoteSocketServiceError_SystemError = 93 - RemoteSocketServiceError_SYS_ESOCKTNOSUPPORT RemoteSocketServiceError_SystemError = 94 - RemoteSocketServiceError_SYS_EOPNOTSUPP RemoteSocketServiceError_SystemError = 95 - RemoteSocketServiceError_SYS_ENOTSUP RemoteSocketServiceError_SystemError = 95 - RemoteSocketServiceError_SYS_EPFNOSUPPORT RemoteSocketServiceError_SystemError = 96 - RemoteSocketServiceError_SYS_EAFNOSUPPORT RemoteSocketServiceError_SystemError = 97 - RemoteSocketServiceError_SYS_EADDRINUSE RemoteSocketServiceError_SystemError = 98 - RemoteSocketServiceError_SYS_EADDRNOTAVAIL RemoteSocketServiceError_SystemError = 99 - RemoteSocketServiceError_SYS_ENETDOWN RemoteSocketServiceError_SystemError = 100 - RemoteSocketServiceError_SYS_ENETUNREACH RemoteSocketServiceError_SystemError = 101 - RemoteSocketServiceError_SYS_ENETRESET RemoteSocketServiceError_SystemError = 102 - RemoteSocketServiceError_SYS_ECONNABORTED RemoteSocketServiceError_SystemError = 103 - RemoteSocketServiceError_SYS_ECONNRESET RemoteSocketServiceError_SystemError = 104 - RemoteSocketServiceError_SYS_ENOBUFS RemoteSocketServiceError_SystemError = 105 - RemoteSocketServiceError_SYS_EISCONN RemoteSocketServiceError_SystemError = 106 - RemoteSocketServiceError_SYS_ENOTCONN RemoteSocketServiceError_SystemError = 107 - RemoteSocketServiceError_SYS_ESHUTDOWN RemoteSocketServiceError_SystemError = 108 - RemoteSocketServiceError_SYS_ETOOMANYREFS RemoteSocketServiceError_SystemError = 109 - RemoteSocketServiceError_SYS_ETIMEDOUT RemoteSocketServiceError_SystemError = 110 - RemoteSocketServiceError_SYS_ECONNREFUSED RemoteSocketServiceError_SystemError = 111 - RemoteSocketServiceError_SYS_EHOSTDOWN RemoteSocketServiceError_SystemError = 112 - RemoteSocketServiceError_SYS_EHOSTUNREACH RemoteSocketServiceError_SystemError = 113 - RemoteSocketServiceError_SYS_EALREADY RemoteSocketServiceError_SystemError = 114 - RemoteSocketServiceError_SYS_EINPROGRESS RemoteSocketServiceError_SystemError = 115 - RemoteSocketServiceError_SYS_ESTALE RemoteSocketServiceError_SystemError = 116 - RemoteSocketServiceError_SYS_EUCLEAN RemoteSocketServiceError_SystemError = 117 - RemoteSocketServiceError_SYS_ENOTNAM RemoteSocketServiceError_SystemError = 118 - RemoteSocketServiceError_SYS_ENAVAIL RemoteSocketServiceError_SystemError = 119 - RemoteSocketServiceError_SYS_EISNAM RemoteSocketServiceError_SystemError = 120 - RemoteSocketServiceError_SYS_EREMOTEIO RemoteSocketServiceError_SystemError = 121 - RemoteSocketServiceError_SYS_EDQUOT RemoteSocketServiceError_SystemError = 122 - RemoteSocketServiceError_SYS_ENOMEDIUM RemoteSocketServiceError_SystemError = 123 - RemoteSocketServiceError_SYS_EMEDIUMTYPE RemoteSocketServiceError_SystemError = 124 - RemoteSocketServiceError_SYS_ECANCELED RemoteSocketServiceError_SystemError = 125 - RemoteSocketServiceError_SYS_ENOKEY RemoteSocketServiceError_SystemError = 126 - RemoteSocketServiceError_SYS_EKEYEXPIRED RemoteSocketServiceError_SystemError = 127 - RemoteSocketServiceError_SYS_EKEYREVOKED RemoteSocketServiceError_SystemError = 128 - RemoteSocketServiceError_SYS_EKEYREJECTED RemoteSocketServiceError_SystemError = 129 - RemoteSocketServiceError_SYS_EOWNERDEAD RemoteSocketServiceError_SystemError = 130 - RemoteSocketServiceError_SYS_ENOTRECOVERABLE RemoteSocketServiceError_SystemError = 131 - RemoteSocketServiceError_SYS_ERFKILL RemoteSocketServiceError_SystemError = 132 -) - -var RemoteSocketServiceError_SystemError_name = map[int32]string{ - 0: "SYS_SUCCESS", - 1: "SYS_EPERM", - 2: "SYS_ENOENT", - 3: "SYS_ESRCH", - 4: "SYS_EINTR", - 5: "SYS_EIO", - 6: "SYS_ENXIO", - 7: "SYS_E2BIG", - 8: "SYS_ENOEXEC", - 9: "SYS_EBADF", - 10: "SYS_ECHILD", - 11: "SYS_EAGAIN", - // Duplicate value: 11: "SYS_EWOULDBLOCK", - 12: "SYS_ENOMEM", - 13: "SYS_EACCES", - 14: "SYS_EFAULT", - 15: "SYS_ENOTBLK", - 16: "SYS_EBUSY", - 17: "SYS_EEXIST", - 18: "SYS_EXDEV", - 19: "SYS_ENODEV", - 20: "SYS_ENOTDIR", - 21: "SYS_EISDIR", - 22: "SYS_EINVAL", - 23: "SYS_ENFILE", - 24: "SYS_EMFILE", - 25: "SYS_ENOTTY", - 26: "SYS_ETXTBSY", - 27: "SYS_EFBIG", - 28: "SYS_ENOSPC", - 29: "SYS_ESPIPE", - 30: "SYS_EROFS", - 31: "SYS_EMLINK", - 32: "SYS_EPIPE", - 33: "SYS_EDOM", - 34: "SYS_ERANGE", - 35: "SYS_EDEADLK", - // Duplicate value: 35: "SYS_EDEADLOCK", - 36: "SYS_ENAMETOOLONG", - 37: "SYS_ENOLCK", - 38: "SYS_ENOSYS", - 39: "SYS_ENOTEMPTY", - 40: "SYS_ELOOP", - 42: "SYS_ENOMSG", - 43: "SYS_EIDRM", - 44: "SYS_ECHRNG", - 45: "SYS_EL2NSYNC", - 46: "SYS_EL3HLT", - 47: "SYS_EL3RST", - 48: "SYS_ELNRNG", - 49: "SYS_EUNATCH", - 50: "SYS_ENOCSI", - 51: "SYS_EL2HLT", - 52: "SYS_EBADE", - 53: "SYS_EBADR", - 54: "SYS_EXFULL", - 55: "SYS_ENOANO", - 56: "SYS_EBADRQC", - 57: "SYS_EBADSLT", - 59: "SYS_EBFONT", - 60: "SYS_ENOSTR", - 61: "SYS_ENODATA", - 62: "SYS_ETIME", - 63: "SYS_ENOSR", - 64: "SYS_ENONET", - 65: "SYS_ENOPKG", - 66: "SYS_EREMOTE", - 67: "SYS_ENOLINK", - 68: "SYS_EADV", - 69: "SYS_ESRMNT", - 70: "SYS_ECOMM", - 71: "SYS_EPROTO", - 72: "SYS_EMULTIHOP", - 73: "SYS_EDOTDOT", - 74: "SYS_EBADMSG", - 75: "SYS_EOVERFLOW", - 76: "SYS_ENOTUNIQ", - 77: "SYS_EBADFD", - 78: "SYS_EREMCHG", - 79: "SYS_ELIBACC", - 80: "SYS_ELIBBAD", - 81: "SYS_ELIBSCN", - 82: "SYS_ELIBMAX", - 83: "SYS_ELIBEXEC", - 84: "SYS_EILSEQ", - 85: "SYS_ERESTART", - 86: "SYS_ESTRPIPE", - 87: "SYS_EUSERS", - 88: "SYS_ENOTSOCK", - 89: "SYS_EDESTADDRREQ", - 90: "SYS_EMSGSIZE", - 91: "SYS_EPROTOTYPE", - 92: "SYS_ENOPROTOOPT", - 93: "SYS_EPROTONOSUPPORT", - 94: "SYS_ESOCKTNOSUPPORT", - 95: "SYS_EOPNOTSUPP", - // Duplicate value: 95: "SYS_ENOTSUP", - 96: "SYS_EPFNOSUPPORT", - 97: "SYS_EAFNOSUPPORT", - 98: "SYS_EADDRINUSE", - 99: "SYS_EADDRNOTAVAIL", - 100: "SYS_ENETDOWN", - 101: "SYS_ENETUNREACH", - 102: "SYS_ENETRESET", - 103: "SYS_ECONNABORTED", - 104: "SYS_ECONNRESET", - 105: "SYS_ENOBUFS", - 106: "SYS_EISCONN", - 107: "SYS_ENOTCONN", - 108: "SYS_ESHUTDOWN", - 109: "SYS_ETOOMANYREFS", - 110: "SYS_ETIMEDOUT", - 111: "SYS_ECONNREFUSED", - 112: "SYS_EHOSTDOWN", - 113: "SYS_EHOSTUNREACH", - 114: "SYS_EALREADY", - 115: "SYS_EINPROGRESS", - 116: "SYS_ESTALE", - 117: "SYS_EUCLEAN", - 118: "SYS_ENOTNAM", - 119: "SYS_ENAVAIL", - 120: "SYS_EISNAM", - 121: "SYS_EREMOTEIO", - 122: "SYS_EDQUOT", - 123: "SYS_ENOMEDIUM", - 124: "SYS_EMEDIUMTYPE", - 125: "SYS_ECANCELED", - 126: "SYS_ENOKEY", - 127: "SYS_EKEYEXPIRED", - 128: "SYS_EKEYREVOKED", - 129: "SYS_EKEYREJECTED", - 130: "SYS_EOWNERDEAD", - 131: "SYS_ENOTRECOVERABLE", - 132: "SYS_ERFKILL", -} -var RemoteSocketServiceError_SystemError_value = map[string]int32{ - "SYS_SUCCESS": 0, - "SYS_EPERM": 1, - "SYS_ENOENT": 2, - "SYS_ESRCH": 3, - "SYS_EINTR": 4, - "SYS_EIO": 5, - "SYS_ENXIO": 6, - "SYS_E2BIG": 7, - "SYS_ENOEXEC": 8, - "SYS_EBADF": 9, - "SYS_ECHILD": 10, - "SYS_EAGAIN": 11, - "SYS_EWOULDBLOCK": 11, - "SYS_ENOMEM": 12, - "SYS_EACCES": 13, - "SYS_EFAULT": 14, - "SYS_ENOTBLK": 15, - "SYS_EBUSY": 16, - "SYS_EEXIST": 17, - "SYS_EXDEV": 18, - "SYS_ENODEV": 19, - "SYS_ENOTDIR": 20, - "SYS_EISDIR": 21, - "SYS_EINVAL": 22, - "SYS_ENFILE": 23, - "SYS_EMFILE": 24, - "SYS_ENOTTY": 25, - "SYS_ETXTBSY": 26, - "SYS_EFBIG": 27, - "SYS_ENOSPC": 28, - "SYS_ESPIPE": 29, - "SYS_EROFS": 30, - "SYS_EMLINK": 31, - "SYS_EPIPE": 32, - "SYS_EDOM": 33, - "SYS_ERANGE": 34, - "SYS_EDEADLK": 35, - "SYS_EDEADLOCK": 35, - "SYS_ENAMETOOLONG": 36, - "SYS_ENOLCK": 37, - "SYS_ENOSYS": 38, - "SYS_ENOTEMPTY": 39, - "SYS_ELOOP": 40, - "SYS_ENOMSG": 42, - "SYS_EIDRM": 43, - "SYS_ECHRNG": 44, - "SYS_EL2NSYNC": 45, - "SYS_EL3HLT": 46, - "SYS_EL3RST": 47, - "SYS_ELNRNG": 48, - "SYS_EUNATCH": 49, - "SYS_ENOCSI": 50, - "SYS_EL2HLT": 51, - "SYS_EBADE": 52, - "SYS_EBADR": 53, - "SYS_EXFULL": 54, - "SYS_ENOANO": 55, - "SYS_EBADRQC": 56, - "SYS_EBADSLT": 57, - "SYS_EBFONT": 59, - "SYS_ENOSTR": 60, - "SYS_ENODATA": 61, - "SYS_ETIME": 62, - "SYS_ENOSR": 63, - "SYS_ENONET": 64, - "SYS_ENOPKG": 65, - "SYS_EREMOTE": 66, - "SYS_ENOLINK": 67, - "SYS_EADV": 68, - "SYS_ESRMNT": 69, - "SYS_ECOMM": 70, - "SYS_EPROTO": 71, - "SYS_EMULTIHOP": 72, - "SYS_EDOTDOT": 73, - "SYS_EBADMSG": 74, - "SYS_EOVERFLOW": 75, - "SYS_ENOTUNIQ": 76, - "SYS_EBADFD": 77, - "SYS_EREMCHG": 78, - "SYS_ELIBACC": 79, - "SYS_ELIBBAD": 80, - "SYS_ELIBSCN": 81, - "SYS_ELIBMAX": 82, - "SYS_ELIBEXEC": 83, - "SYS_EILSEQ": 84, - "SYS_ERESTART": 85, - "SYS_ESTRPIPE": 86, - "SYS_EUSERS": 87, - "SYS_ENOTSOCK": 88, - "SYS_EDESTADDRREQ": 89, - "SYS_EMSGSIZE": 90, - "SYS_EPROTOTYPE": 91, - "SYS_ENOPROTOOPT": 92, - "SYS_EPROTONOSUPPORT": 93, - "SYS_ESOCKTNOSUPPORT": 94, - "SYS_EOPNOTSUPP": 95, - "SYS_ENOTSUP": 95, - "SYS_EPFNOSUPPORT": 96, - "SYS_EAFNOSUPPORT": 97, - "SYS_EADDRINUSE": 98, - "SYS_EADDRNOTAVAIL": 99, - "SYS_ENETDOWN": 100, - "SYS_ENETUNREACH": 101, - "SYS_ENETRESET": 102, - "SYS_ECONNABORTED": 103, - "SYS_ECONNRESET": 104, - "SYS_ENOBUFS": 105, - "SYS_EISCONN": 106, - "SYS_ENOTCONN": 107, - "SYS_ESHUTDOWN": 108, - "SYS_ETOOMANYREFS": 109, - "SYS_ETIMEDOUT": 110, - "SYS_ECONNREFUSED": 111, - "SYS_EHOSTDOWN": 112, - "SYS_EHOSTUNREACH": 113, - "SYS_EALREADY": 114, - "SYS_EINPROGRESS": 115, - "SYS_ESTALE": 116, - "SYS_EUCLEAN": 117, - "SYS_ENOTNAM": 118, - "SYS_ENAVAIL": 119, - "SYS_EISNAM": 120, - "SYS_EREMOTEIO": 121, - "SYS_EDQUOT": 122, - "SYS_ENOMEDIUM": 123, - "SYS_EMEDIUMTYPE": 124, - "SYS_ECANCELED": 125, - "SYS_ENOKEY": 126, - "SYS_EKEYEXPIRED": 127, - "SYS_EKEYREVOKED": 128, - "SYS_EKEYREJECTED": 129, - "SYS_EOWNERDEAD": 130, - "SYS_ENOTRECOVERABLE": 131, - "SYS_ERFKILL": 132, -} - -func (x RemoteSocketServiceError_SystemError) Enum() *RemoteSocketServiceError_SystemError { - p := new(RemoteSocketServiceError_SystemError) - *p = x - return p -} -func (x RemoteSocketServiceError_SystemError) String() string { - return proto.EnumName(RemoteSocketServiceError_SystemError_name, int32(x)) -} -func (x *RemoteSocketServiceError_SystemError) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(RemoteSocketServiceError_SystemError_value, data, "RemoteSocketServiceError_SystemError") - if err != nil { - return err - } - *x = RemoteSocketServiceError_SystemError(value) - return nil -} - -type CreateSocketRequest_SocketFamily int32 - -const ( - CreateSocketRequest_IPv4 CreateSocketRequest_SocketFamily = 1 - CreateSocketRequest_IPv6 CreateSocketRequest_SocketFamily = 2 -) - -var CreateSocketRequest_SocketFamily_name = map[int32]string{ - 1: "IPv4", - 2: "IPv6", -} -var CreateSocketRequest_SocketFamily_value = map[string]int32{ - "IPv4": 1, - "IPv6": 2, -} - -func (x CreateSocketRequest_SocketFamily) Enum() *CreateSocketRequest_SocketFamily { - p := new(CreateSocketRequest_SocketFamily) - *p = x - return p -} -func (x CreateSocketRequest_SocketFamily) String() string { - return proto.EnumName(CreateSocketRequest_SocketFamily_name, int32(x)) -} -func (x *CreateSocketRequest_SocketFamily) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(CreateSocketRequest_SocketFamily_value, data, "CreateSocketRequest_SocketFamily") - if err != nil { - return err - } - *x = CreateSocketRequest_SocketFamily(value) - return nil -} - -type CreateSocketRequest_SocketProtocol int32 - -const ( - CreateSocketRequest_TCP CreateSocketRequest_SocketProtocol = 1 - CreateSocketRequest_UDP CreateSocketRequest_SocketProtocol = 2 -) - -var CreateSocketRequest_SocketProtocol_name = map[int32]string{ - 1: "TCP", - 2: "UDP", -} -var CreateSocketRequest_SocketProtocol_value = map[string]int32{ - "TCP": 1, - "UDP": 2, -} - -func (x CreateSocketRequest_SocketProtocol) Enum() *CreateSocketRequest_SocketProtocol { - p := new(CreateSocketRequest_SocketProtocol) - *p = x - return p -} -func (x CreateSocketRequest_SocketProtocol) String() string { - return proto.EnumName(CreateSocketRequest_SocketProtocol_name, int32(x)) -} -func (x *CreateSocketRequest_SocketProtocol) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(CreateSocketRequest_SocketProtocol_value, data, "CreateSocketRequest_SocketProtocol") - if err != nil { - return err - } - *x = CreateSocketRequest_SocketProtocol(value) - return nil -} - -type SocketOption_SocketOptionLevel int32 - -const ( - SocketOption_SOCKET_SOL_IP SocketOption_SocketOptionLevel = 0 - SocketOption_SOCKET_SOL_SOCKET SocketOption_SocketOptionLevel = 1 - SocketOption_SOCKET_SOL_TCP SocketOption_SocketOptionLevel = 6 - SocketOption_SOCKET_SOL_UDP SocketOption_SocketOptionLevel = 17 -) - -var SocketOption_SocketOptionLevel_name = map[int32]string{ - 0: "SOCKET_SOL_IP", - 1: "SOCKET_SOL_SOCKET", - 6: "SOCKET_SOL_TCP", - 17: "SOCKET_SOL_UDP", -} -var SocketOption_SocketOptionLevel_value = map[string]int32{ - "SOCKET_SOL_IP": 0, - "SOCKET_SOL_SOCKET": 1, - "SOCKET_SOL_TCP": 6, - "SOCKET_SOL_UDP": 17, -} - -func (x SocketOption_SocketOptionLevel) Enum() *SocketOption_SocketOptionLevel { - p := new(SocketOption_SocketOptionLevel) - *p = x - return p -} -func (x SocketOption_SocketOptionLevel) String() string { - return proto.EnumName(SocketOption_SocketOptionLevel_name, int32(x)) -} -func (x *SocketOption_SocketOptionLevel) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(SocketOption_SocketOptionLevel_value, data, "SocketOption_SocketOptionLevel") - if err != nil { - return err - } - *x = SocketOption_SocketOptionLevel(value) - return nil -} - -type SocketOption_SocketOptionName int32 - -const ( - SocketOption_SOCKET_SO_DEBUG SocketOption_SocketOptionName = 1 - SocketOption_SOCKET_SO_REUSEADDR SocketOption_SocketOptionName = 2 - SocketOption_SOCKET_SO_TYPE SocketOption_SocketOptionName = 3 - SocketOption_SOCKET_SO_ERROR SocketOption_SocketOptionName = 4 - SocketOption_SOCKET_SO_DONTROUTE SocketOption_SocketOptionName = 5 - SocketOption_SOCKET_SO_BROADCAST SocketOption_SocketOptionName = 6 - SocketOption_SOCKET_SO_SNDBUF SocketOption_SocketOptionName = 7 - SocketOption_SOCKET_SO_RCVBUF SocketOption_SocketOptionName = 8 - SocketOption_SOCKET_SO_KEEPALIVE SocketOption_SocketOptionName = 9 - SocketOption_SOCKET_SO_OOBINLINE SocketOption_SocketOptionName = 10 - SocketOption_SOCKET_SO_LINGER SocketOption_SocketOptionName = 13 - SocketOption_SOCKET_SO_RCVTIMEO SocketOption_SocketOptionName = 20 - SocketOption_SOCKET_SO_SNDTIMEO SocketOption_SocketOptionName = 21 - SocketOption_SOCKET_IP_TOS SocketOption_SocketOptionName = 1 - SocketOption_SOCKET_IP_TTL SocketOption_SocketOptionName = 2 - SocketOption_SOCKET_IP_HDRINCL SocketOption_SocketOptionName = 3 - SocketOption_SOCKET_IP_OPTIONS SocketOption_SocketOptionName = 4 - SocketOption_SOCKET_TCP_NODELAY SocketOption_SocketOptionName = 1 - SocketOption_SOCKET_TCP_MAXSEG SocketOption_SocketOptionName = 2 - SocketOption_SOCKET_TCP_CORK SocketOption_SocketOptionName = 3 - SocketOption_SOCKET_TCP_KEEPIDLE SocketOption_SocketOptionName = 4 - SocketOption_SOCKET_TCP_KEEPINTVL SocketOption_SocketOptionName = 5 - SocketOption_SOCKET_TCP_KEEPCNT SocketOption_SocketOptionName = 6 - SocketOption_SOCKET_TCP_SYNCNT SocketOption_SocketOptionName = 7 - SocketOption_SOCKET_TCP_LINGER2 SocketOption_SocketOptionName = 8 - SocketOption_SOCKET_TCP_DEFER_ACCEPT SocketOption_SocketOptionName = 9 - SocketOption_SOCKET_TCP_WINDOW_CLAMP SocketOption_SocketOptionName = 10 - SocketOption_SOCKET_TCP_INFO SocketOption_SocketOptionName = 11 - SocketOption_SOCKET_TCP_QUICKACK SocketOption_SocketOptionName = 12 -) - -var SocketOption_SocketOptionName_name = map[int32]string{ - 1: "SOCKET_SO_DEBUG", - 2: "SOCKET_SO_REUSEADDR", - 3: "SOCKET_SO_TYPE", - 4: "SOCKET_SO_ERROR", - 5: "SOCKET_SO_DONTROUTE", - 6: "SOCKET_SO_BROADCAST", - 7: "SOCKET_SO_SNDBUF", - 8: "SOCKET_SO_RCVBUF", - 9: "SOCKET_SO_KEEPALIVE", - 10: "SOCKET_SO_OOBINLINE", - 13: "SOCKET_SO_LINGER", - 20: "SOCKET_SO_RCVTIMEO", - 21: "SOCKET_SO_SNDTIMEO", - // Duplicate value: 1: "SOCKET_IP_TOS", - // Duplicate value: 2: "SOCKET_IP_TTL", - // Duplicate value: 3: "SOCKET_IP_HDRINCL", - // Duplicate value: 4: "SOCKET_IP_OPTIONS", - // Duplicate value: 1: "SOCKET_TCP_NODELAY", - // Duplicate value: 2: "SOCKET_TCP_MAXSEG", - // Duplicate value: 3: "SOCKET_TCP_CORK", - // Duplicate value: 4: "SOCKET_TCP_KEEPIDLE", - // Duplicate value: 5: "SOCKET_TCP_KEEPINTVL", - // Duplicate value: 6: "SOCKET_TCP_KEEPCNT", - // Duplicate value: 7: "SOCKET_TCP_SYNCNT", - // Duplicate value: 8: "SOCKET_TCP_LINGER2", - // Duplicate value: 9: "SOCKET_TCP_DEFER_ACCEPT", - // Duplicate value: 10: "SOCKET_TCP_WINDOW_CLAMP", - 11: "SOCKET_TCP_INFO", - 12: "SOCKET_TCP_QUICKACK", -} -var SocketOption_SocketOptionName_value = map[string]int32{ - "SOCKET_SO_DEBUG": 1, - "SOCKET_SO_REUSEADDR": 2, - "SOCKET_SO_TYPE": 3, - "SOCKET_SO_ERROR": 4, - "SOCKET_SO_DONTROUTE": 5, - "SOCKET_SO_BROADCAST": 6, - "SOCKET_SO_SNDBUF": 7, - "SOCKET_SO_RCVBUF": 8, - "SOCKET_SO_KEEPALIVE": 9, - "SOCKET_SO_OOBINLINE": 10, - "SOCKET_SO_LINGER": 13, - "SOCKET_SO_RCVTIMEO": 20, - "SOCKET_SO_SNDTIMEO": 21, - "SOCKET_IP_TOS": 1, - "SOCKET_IP_TTL": 2, - "SOCKET_IP_HDRINCL": 3, - "SOCKET_IP_OPTIONS": 4, - "SOCKET_TCP_NODELAY": 1, - "SOCKET_TCP_MAXSEG": 2, - "SOCKET_TCP_CORK": 3, - "SOCKET_TCP_KEEPIDLE": 4, - "SOCKET_TCP_KEEPINTVL": 5, - "SOCKET_TCP_KEEPCNT": 6, - "SOCKET_TCP_SYNCNT": 7, - "SOCKET_TCP_LINGER2": 8, - "SOCKET_TCP_DEFER_ACCEPT": 9, - "SOCKET_TCP_WINDOW_CLAMP": 10, - "SOCKET_TCP_INFO": 11, - "SOCKET_TCP_QUICKACK": 12, -} - -func (x SocketOption_SocketOptionName) Enum() *SocketOption_SocketOptionName { - p := new(SocketOption_SocketOptionName) - *p = x - return p -} -func (x SocketOption_SocketOptionName) String() string { - return proto.EnumName(SocketOption_SocketOptionName_name, int32(x)) -} -func (x *SocketOption_SocketOptionName) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(SocketOption_SocketOptionName_value, data, "SocketOption_SocketOptionName") - if err != nil { - return err - } - *x = SocketOption_SocketOptionName(value) - return nil -} - -type ShutDownRequest_How int32 - -const ( - ShutDownRequest_SOCKET_SHUT_RD ShutDownRequest_How = 1 - ShutDownRequest_SOCKET_SHUT_WR ShutDownRequest_How = 2 - ShutDownRequest_SOCKET_SHUT_RDWR ShutDownRequest_How = 3 -) - -var ShutDownRequest_How_name = map[int32]string{ - 1: "SOCKET_SHUT_RD", - 2: "SOCKET_SHUT_WR", - 3: "SOCKET_SHUT_RDWR", -} -var ShutDownRequest_How_value = map[string]int32{ - "SOCKET_SHUT_RD": 1, - "SOCKET_SHUT_WR": 2, - "SOCKET_SHUT_RDWR": 3, -} - -func (x ShutDownRequest_How) Enum() *ShutDownRequest_How { - p := new(ShutDownRequest_How) - *p = x - return p -} -func (x ShutDownRequest_How) String() string { - return proto.EnumName(ShutDownRequest_How_name, int32(x)) -} -func (x *ShutDownRequest_How) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(ShutDownRequest_How_value, data, "ShutDownRequest_How") - if err != nil { - return err - } - *x = ShutDownRequest_How(value) - return nil -} - -type ReceiveRequest_Flags int32 - -const ( - ReceiveRequest_MSG_OOB ReceiveRequest_Flags = 1 - ReceiveRequest_MSG_PEEK ReceiveRequest_Flags = 2 -) - -var ReceiveRequest_Flags_name = map[int32]string{ - 1: "MSG_OOB", - 2: "MSG_PEEK", -} -var ReceiveRequest_Flags_value = map[string]int32{ - "MSG_OOB": 1, - "MSG_PEEK": 2, -} - -func (x ReceiveRequest_Flags) Enum() *ReceiveRequest_Flags { - p := new(ReceiveRequest_Flags) - *p = x - return p -} -func (x ReceiveRequest_Flags) String() string { - return proto.EnumName(ReceiveRequest_Flags_name, int32(x)) -} -func (x *ReceiveRequest_Flags) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(ReceiveRequest_Flags_value, data, "ReceiveRequest_Flags") - if err != nil { - return err - } - *x = ReceiveRequest_Flags(value) - return nil -} - -type PollEvent_PollEventFlag int32 - -const ( - PollEvent_SOCKET_POLLNONE PollEvent_PollEventFlag = 0 - PollEvent_SOCKET_POLLIN PollEvent_PollEventFlag = 1 - PollEvent_SOCKET_POLLPRI PollEvent_PollEventFlag = 2 - PollEvent_SOCKET_POLLOUT PollEvent_PollEventFlag = 4 - PollEvent_SOCKET_POLLERR PollEvent_PollEventFlag = 8 - PollEvent_SOCKET_POLLHUP PollEvent_PollEventFlag = 16 - PollEvent_SOCKET_POLLNVAL PollEvent_PollEventFlag = 32 - PollEvent_SOCKET_POLLRDNORM PollEvent_PollEventFlag = 64 - PollEvent_SOCKET_POLLRDBAND PollEvent_PollEventFlag = 128 - PollEvent_SOCKET_POLLWRNORM PollEvent_PollEventFlag = 256 - PollEvent_SOCKET_POLLWRBAND PollEvent_PollEventFlag = 512 - PollEvent_SOCKET_POLLMSG PollEvent_PollEventFlag = 1024 - PollEvent_SOCKET_POLLREMOVE PollEvent_PollEventFlag = 4096 - PollEvent_SOCKET_POLLRDHUP PollEvent_PollEventFlag = 8192 -) - -var PollEvent_PollEventFlag_name = map[int32]string{ - 0: "SOCKET_POLLNONE", - 1: "SOCKET_POLLIN", - 2: "SOCKET_POLLPRI", - 4: "SOCKET_POLLOUT", - 8: "SOCKET_POLLERR", - 16: "SOCKET_POLLHUP", - 32: "SOCKET_POLLNVAL", - 64: "SOCKET_POLLRDNORM", - 128: "SOCKET_POLLRDBAND", - 256: "SOCKET_POLLWRNORM", - 512: "SOCKET_POLLWRBAND", - 1024: "SOCKET_POLLMSG", - 4096: "SOCKET_POLLREMOVE", - 8192: "SOCKET_POLLRDHUP", -} -var PollEvent_PollEventFlag_value = map[string]int32{ - "SOCKET_POLLNONE": 0, - "SOCKET_POLLIN": 1, - "SOCKET_POLLPRI": 2, - "SOCKET_POLLOUT": 4, - "SOCKET_POLLERR": 8, - "SOCKET_POLLHUP": 16, - "SOCKET_POLLNVAL": 32, - "SOCKET_POLLRDNORM": 64, - "SOCKET_POLLRDBAND": 128, - "SOCKET_POLLWRNORM": 256, - "SOCKET_POLLWRBAND": 512, - "SOCKET_POLLMSG": 1024, - "SOCKET_POLLREMOVE": 4096, - "SOCKET_POLLRDHUP": 8192, -} - -func (x PollEvent_PollEventFlag) Enum() *PollEvent_PollEventFlag { - p := new(PollEvent_PollEventFlag) - *p = x - return p -} -func (x PollEvent_PollEventFlag) String() string { - return proto.EnumName(PollEvent_PollEventFlag_name, int32(x)) -} -func (x *PollEvent_PollEventFlag) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(PollEvent_PollEventFlag_value, data, "PollEvent_PollEventFlag") - if err != nil { - return err - } - *x = PollEvent_PollEventFlag(value) - return nil -} - -type ResolveReply_ErrorCode int32 - -const ( - ResolveReply_SOCKET_EAI_ADDRFAMILY ResolveReply_ErrorCode = 1 - ResolveReply_SOCKET_EAI_AGAIN ResolveReply_ErrorCode = 2 - ResolveReply_SOCKET_EAI_BADFLAGS ResolveReply_ErrorCode = 3 - ResolveReply_SOCKET_EAI_FAIL ResolveReply_ErrorCode = 4 - ResolveReply_SOCKET_EAI_FAMILY ResolveReply_ErrorCode = 5 - ResolveReply_SOCKET_EAI_MEMORY ResolveReply_ErrorCode = 6 - ResolveReply_SOCKET_EAI_NODATA ResolveReply_ErrorCode = 7 - ResolveReply_SOCKET_EAI_NONAME ResolveReply_ErrorCode = 8 - ResolveReply_SOCKET_EAI_SERVICE ResolveReply_ErrorCode = 9 - ResolveReply_SOCKET_EAI_SOCKTYPE ResolveReply_ErrorCode = 10 - ResolveReply_SOCKET_EAI_SYSTEM ResolveReply_ErrorCode = 11 - ResolveReply_SOCKET_EAI_BADHINTS ResolveReply_ErrorCode = 12 - ResolveReply_SOCKET_EAI_PROTOCOL ResolveReply_ErrorCode = 13 - ResolveReply_SOCKET_EAI_OVERFLOW ResolveReply_ErrorCode = 14 - ResolveReply_SOCKET_EAI_MAX ResolveReply_ErrorCode = 15 -) - -var ResolveReply_ErrorCode_name = map[int32]string{ - 1: "SOCKET_EAI_ADDRFAMILY", - 2: "SOCKET_EAI_AGAIN", - 3: "SOCKET_EAI_BADFLAGS", - 4: "SOCKET_EAI_FAIL", - 5: "SOCKET_EAI_FAMILY", - 6: "SOCKET_EAI_MEMORY", - 7: "SOCKET_EAI_NODATA", - 8: "SOCKET_EAI_NONAME", - 9: "SOCKET_EAI_SERVICE", - 10: "SOCKET_EAI_SOCKTYPE", - 11: "SOCKET_EAI_SYSTEM", - 12: "SOCKET_EAI_BADHINTS", - 13: "SOCKET_EAI_PROTOCOL", - 14: "SOCKET_EAI_OVERFLOW", - 15: "SOCKET_EAI_MAX", -} -var ResolveReply_ErrorCode_value = map[string]int32{ - "SOCKET_EAI_ADDRFAMILY": 1, - "SOCKET_EAI_AGAIN": 2, - "SOCKET_EAI_BADFLAGS": 3, - "SOCKET_EAI_FAIL": 4, - "SOCKET_EAI_FAMILY": 5, - "SOCKET_EAI_MEMORY": 6, - "SOCKET_EAI_NODATA": 7, - "SOCKET_EAI_NONAME": 8, - "SOCKET_EAI_SERVICE": 9, - "SOCKET_EAI_SOCKTYPE": 10, - "SOCKET_EAI_SYSTEM": 11, - "SOCKET_EAI_BADHINTS": 12, - "SOCKET_EAI_PROTOCOL": 13, - "SOCKET_EAI_OVERFLOW": 14, - "SOCKET_EAI_MAX": 15, -} - -func (x ResolveReply_ErrorCode) Enum() *ResolveReply_ErrorCode { - p := new(ResolveReply_ErrorCode) - *p = x - return p -} -func (x ResolveReply_ErrorCode) String() string { - return proto.EnumName(ResolveReply_ErrorCode_name, int32(x)) -} -func (x *ResolveReply_ErrorCode) UnmarshalJSON(data []byte) error { - value, err := proto.UnmarshalJSONEnum(ResolveReply_ErrorCode_value, data, "ResolveReply_ErrorCode") - if err != nil { - return err - } - *x = ResolveReply_ErrorCode(value) - return nil -} - -type RemoteSocketServiceError struct { - SystemError *int32 `protobuf:"varint,1,opt,name=system_error,def=0" json:"system_error,omitempty"` - ErrorDetail *string `protobuf:"bytes,2,opt,name=error_detail" json:"error_detail,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *RemoteSocketServiceError) Reset() { *m = RemoteSocketServiceError{} } -func (m *RemoteSocketServiceError) String() string { return proto.CompactTextString(m) } -func (*RemoteSocketServiceError) ProtoMessage() {} - -const Default_RemoteSocketServiceError_SystemError int32 = 0 - -func (m *RemoteSocketServiceError) GetSystemError() int32 { - if m != nil && m.SystemError != nil { - return *m.SystemError - } - return Default_RemoteSocketServiceError_SystemError -} - -func (m *RemoteSocketServiceError) GetErrorDetail() string { - if m != nil && m.ErrorDetail != nil { - return *m.ErrorDetail - } - return "" -} - -type AddressPort struct { - Port *int32 `protobuf:"varint,1,req,name=port" json:"port,omitempty"` - PackedAddress []byte `protobuf:"bytes,2,opt,name=packed_address" json:"packed_address,omitempty"` - HostnameHint *string `protobuf:"bytes,3,opt,name=hostname_hint" json:"hostname_hint,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *AddressPort) Reset() { *m = AddressPort{} } -func (m *AddressPort) String() string { return proto.CompactTextString(m) } -func (*AddressPort) ProtoMessage() {} - -func (m *AddressPort) GetPort() int32 { - if m != nil && m.Port != nil { - return *m.Port - } - return 0 -} - -func (m *AddressPort) GetPackedAddress() []byte { - if m != nil { - return m.PackedAddress - } - return nil -} - -func (m *AddressPort) GetHostnameHint() string { - if m != nil && m.HostnameHint != nil { - return *m.HostnameHint - } - return "" -} - -type CreateSocketRequest struct { - Family *CreateSocketRequest_SocketFamily `protobuf:"varint,1,req,name=family,enum=appengine.CreateSocketRequest_SocketFamily" json:"family,omitempty"` - Protocol *CreateSocketRequest_SocketProtocol `protobuf:"varint,2,req,name=protocol,enum=appengine.CreateSocketRequest_SocketProtocol" json:"protocol,omitempty"` - SocketOptions []*SocketOption `protobuf:"bytes,3,rep,name=socket_options" json:"socket_options,omitempty"` - ProxyExternalIp *AddressPort `protobuf:"bytes,4,opt,name=proxy_external_ip" json:"proxy_external_ip,omitempty"` - ListenBacklog *int32 `protobuf:"varint,5,opt,name=listen_backlog,def=0" json:"listen_backlog,omitempty"` - RemoteIp *AddressPort `protobuf:"bytes,6,opt,name=remote_ip" json:"remote_ip,omitempty"` - AppId *string `protobuf:"bytes,9,opt,name=app_id" json:"app_id,omitempty"` - ProjectId *int64 `protobuf:"varint,10,opt,name=project_id" json:"project_id,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CreateSocketRequest) Reset() { *m = CreateSocketRequest{} } -func (m *CreateSocketRequest) String() string { return proto.CompactTextString(m) } -func (*CreateSocketRequest) ProtoMessage() {} - -const Default_CreateSocketRequest_ListenBacklog int32 = 0 - -func (m *CreateSocketRequest) GetFamily() CreateSocketRequest_SocketFamily { - if m != nil && m.Family != nil { - return *m.Family - } - return CreateSocketRequest_IPv4 -} - -func (m *CreateSocketRequest) GetProtocol() CreateSocketRequest_SocketProtocol { - if m != nil && m.Protocol != nil { - return *m.Protocol - } - return CreateSocketRequest_TCP -} - -func (m *CreateSocketRequest) GetSocketOptions() []*SocketOption { - if m != nil { - return m.SocketOptions - } - return nil -} - -func (m *CreateSocketRequest) GetProxyExternalIp() *AddressPort { - if m != nil { - return m.ProxyExternalIp - } - return nil -} - -func (m *CreateSocketRequest) GetListenBacklog() int32 { - if m != nil && m.ListenBacklog != nil { - return *m.ListenBacklog - } - return Default_CreateSocketRequest_ListenBacklog -} - -func (m *CreateSocketRequest) GetRemoteIp() *AddressPort { - if m != nil { - return m.RemoteIp - } - return nil -} - -func (m *CreateSocketRequest) GetAppId() string { - if m != nil && m.AppId != nil { - return *m.AppId - } - return "" -} - -func (m *CreateSocketRequest) GetProjectId() int64 { - if m != nil && m.ProjectId != nil { - return *m.ProjectId - } - return 0 -} - -type CreateSocketReply struct { - SocketDescriptor *string `protobuf:"bytes,1,opt,name=socket_descriptor" json:"socket_descriptor,omitempty"` - ServerAddress *AddressPort `protobuf:"bytes,3,opt,name=server_address" json:"server_address,omitempty"` - ProxyExternalIp *AddressPort `protobuf:"bytes,4,opt,name=proxy_external_ip" json:"proxy_external_ip,omitempty"` - XXX_extensions map[int32]proto.Extension `json:"-"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CreateSocketReply) Reset() { *m = CreateSocketReply{} } -func (m *CreateSocketReply) String() string { return proto.CompactTextString(m) } -func (*CreateSocketReply) ProtoMessage() {} - -var extRange_CreateSocketReply = []proto.ExtensionRange{ - {1000, 536870911}, -} - -func (*CreateSocketReply) ExtensionRangeArray() []proto.ExtensionRange { - return extRange_CreateSocketReply -} -func (m *CreateSocketReply) ExtensionMap() map[int32]proto.Extension { - if m.XXX_extensions == nil { - m.XXX_extensions = make(map[int32]proto.Extension) - } - return m.XXX_extensions -} - -func (m *CreateSocketReply) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -func (m *CreateSocketReply) GetServerAddress() *AddressPort { - if m != nil { - return m.ServerAddress - } - return nil -} - -func (m *CreateSocketReply) GetProxyExternalIp() *AddressPort { - if m != nil { - return m.ProxyExternalIp - } - return nil -} - -type BindRequest struct { - SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"` - ProxyExternalIp *AddressPort `protobuf:"bytes,2,req,name=proxy_external_ip" json:"proxy_external_ip,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *BindRequest) Reset() { *m = BindRequest{} } -func (m *BindRequest) String() string { return proto.CompactTextString(m) } -func (*BindRequest) ProtoMessage() {} - -func (m *BindRequest) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -func (m *BindRequest) GetProxyExternalIp() *AddressPort { - if m != nil { - return m.ProxyExternalIp - } - return nil -} - -type BindReply struct { - ProxyExternalIp *AddressPort `protobuf:"bytes,1,opt,name=proxy_external_ip" json:"proxy_external_ip,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *BindReply) Reset() { *m = BindReply{} } -func (m *BindReply) String() string { return proto.CompactTextString(m) } -func (*BindReply) ProtoMessage() {} - -func (m *BindReply) GetProxyExternalIp() *AddressPort { - if m != nil { - return m.ProxyExternalIp - } - return nil -} - -type GetSocketNameRequest struct { - SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GetSocketNameRequest) Reset() { *m = GetSocketNameRequest{} } -func (m *GetSocketNameRequest) String() string { return proto.CompactTextString(m) } -func (*GetSocketNameRequest) ProtoMessage() {} - -func (m *GetSocketNameRequest) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -type GetSocketNameReply struct { - ProxyExternalIp *AddressPort `protobuf:"bytes,2,opt,name=proxy_external_ip" json:"proxy_external_ip,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GetSocketNameReply) Reset() { *m = GetSocketNameReply{} } -func (m *GetSocketNameReply) String() string { return proto.CompactTextString(m) } -func (*GetSocketNameReply) ProtoMessage() {} - -func (m *GetSocketNameReply) GetProxyExternalIp() *AddressPort { - if m != nil { - return m.ProxyExternalIp - } - return nil -} - -type GetPeerNameRequest struct { - SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GetPeerNameRequest) Reset() { *m = GetPeerNameRequest{} } -func (m *GetPeerNameRequest) String() string { return proto.CompactTextString(m) } -func (*GetPeerNameRequest) ProtoMessage() {} - -func (m *GetPeerNameRequest) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -type GetPeerNameReply struct { - PeerIp *AddressPort `protobuf:"bytes,2,opt,name=peer_ip" json:"peer_ip,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GetPeerNameReply) Reset() { *m = GetPeerNameReply{} } -func (m *GetPeerNameReply) String() string { return proto.CompactTextString(m) } -func (*GetPeerNameReply) ProtoMessage() {} - -func (m *GetPeerNameReply) GetPeerIp() *AddressPort { - if m != nil { - return m.PeerIp - } - return nil -} - -type SocketOption struct { - Level *SocketOption_SocketOptionLevel `protobuf:"varint,1,req,name=level,enum=appengine.SocketOption_SocketOptionLevel" json:"level,omitempty"` - Option *SocketOption_SocketOptionName `protobuf:"varint,2,req,name=option,enum=appengine.SocketOption_SocketOptionName" json:"option,omitempty"` - Value []byte `protobuf:"bytes,3,req,name=value" json:"value,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *SocketOption) Reset() { *m = SocketOption{} } -func (m *SocketOption) String() string { return proto.CompactTextString(m) } -func (*SocketOption) ProtoMessage() {} - -func (m *SocketOption) GetLevel() SocketOption_SocketOptionLevel { - if m != nil && m.Level != nil { - return *m.Level - } - return SocketOption_SOCKET_SOL_IP -} - -func (m *SocketOption) GetOption() SocketOption_SocketOptionName { - if m != nil && m.Option != nil { - return *m.Option - } - return SocketOption_SOCKET_SO_DEBUG -} - -func (m *SocketOption) GetValue() []byte { - if m != nil { - return m.Value - } - return nil -} - -type SetSocketOptionsRequest struct { - SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"` - Options []*SocketOption `protobuf:"bytes,2,rep,name=options" json:"options,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *SetSocketOptionsRequest) Reset() { *m = SetSocketOptionsRequest{} } -func (m *SetSocketOptionsRequest) String() string { return proto.CompactTextString(m) } -func (*SetSocketOptionsRequest) ProtoMessage() {} - -func (m *SetSocketOptionsRequest) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -func (m *SetSocketOptionsRequest) GetOptions() []*SocketOption { - if m != nil { - return m.Options - } - return nil -} - -type SetSocketOptionsReply struct { - XXX_unrecognized []byte `json:"-"` -} - -func (m *SetSocketOptionsReply) Reset() { *m = SetSocketOptionsReply{} } -func (m *SetSocketOptionsReply) String() string { return proto.CompactTextString(m) } -func (*SetSocketOptionsReply) ProtoMessage() {} - -type GetSocketOptionsRequest struct { - SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"` - Options []*SocketOption `protobuf:"bytes,2,rep,name=options" json:"options,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GetSocketOptionsRequest) Reset() { *m = GetSocketOptionsRequest{} } -func (m *GetSocketOptionsRequest) String() string { return proto.CompactTextString(m) } -func (*GetSocketOptionsRequest) ProtoMessage() {} - -func (m *GetSocketOptionsRequest) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -func (m *GetSocketOptionsRequest) GetOptions() []*SocketOption { - if m != nil { - return m.Options - } - return nil -} - -type GetSocketOptionsReply struct { - Options []*SocketOption `protobuf:"bytes,2,rep,name=options" json:"options,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *GetSocketOptionsReply) Reset() { *m = GetSocketOptionsReply{} } -func (m *GetSocketOptionsReply) String() string { return proto.CompactTextString(m) } -func (*GetSocketOptionsReply) ProtoMessage() {} - -func (m *GetSocketOptionsReply) GetOptions() []*SocketOption { - if m != nil { - return m.Options - } - return nil -} - -type ConnectRequest struct { - SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"` - RemoteIp *AddressPort `protobuf:"bytes,2,req,name=remote_ip" json:"remote_ip,omitempty"` - TimeoutSeconds *float64 `protobuf:"fixed64,3,opt,name=timeout_seconds,def=-1" json:"timeout_seconds,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *ConnectRequest) Reset() { *m = ConnectRequest{} } -func (m *ConnectRequest) String() string { return proto.CompactTextString(m) } -func (*ConnectRequest) ProtoMessage() {} - -const Default_ConnectRequest_TimeoutSeconds float64 = -1 - -func (m *ConnectRequest) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -func (m *ConnectRequest) GetRemoteIp() *AddressPort { - if m != nil { - return m.RemoteIp - } - return nil -} - -func (m *ConnectRequest) GetTimeoutSeconds() float64 { - if m != nil && m.TimeoutSeconds != nil { - return *m.TimeoutSeconds - } - return Default_ConnectRequest_TimeoutSeconds -} - -type ConnectReply struct { - ProxyExternalIp *AddressPort `protobuf:"bytes,1,opt,name=proxy_external_ip" json:"proxy_external_ip,omitempty"` - XXX_extensions map[int32]proto.Extension `json:"-"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *ConnectReply) Reset() { *m = ConnectReply{} } -func (m *ConnectReply) String() string { return proto.CompactTextString(m) } -func (*ConnectReply) ProtoMessage() {} - -var extRange_ConnectReply = []proto.ExtensionRange{ - {1000, 536870911}, -} - -func (*ConnectReply) ExtensionRangeArray() []proto.ExtensionRange { - return extRange_ConnectReply -} -func (m *ConnectReply) ExtensionMap() map[int32]proto.Extension { - if m.XXX_extensions == nil { - m.XXX_extensions = make(map[int32]proto.Extension) - } - return m.XXX_extensions -} - -func (m *ConnectReply) GetProxyExternalIp() *AddressPort { - if m != nil { - return m.ProxyExternalIp - } - return nil -} - -type ListenRequest struct { - SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"` - Backlog *int32 `protobuf:"varint,2,req,name=backlog" json:"backlog,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *ListenRequest) Reset() { *m = ListenRequest{} } -func (m *ListenRequest) String() string { return proto.CompactTextString(m) } -func (*ListenRequest) ProtoMessage() {} - -func (m *ListenRequest) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -func (m *ListenRequest) GetBacklog() int32 { - if m != nil && m.Backlog != nil { - return *m.Backlog - } - return 0 -} - -type ListenReply struct { - XXX_unrecognized []byte `json:"-"` -} - -func (m *ListenReply) Reset() { *m = ListenReply{} } -func (m *ListenReply) String() string { return proto.CompactTextString(m) } -func (*ListenReply) ProtoMessage() {} - -type AcceptRequest struct { - SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"` - TimeoutSeconds *float64 `protobuf:"fixed64,2,opt,name=timeout_seconds,def=-1" json:"timeout_seconds,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *AcceptRequest) Reset() { *m = AcceptRequest{} } -func (m *AcceptRequest) String() string { return proto.CompactTextString(m) } -func (*AcceptRequest) ProtoMessage() {} - -const Default_AcceptRequest_TimeoutSeconds float64 = -1 - -func (m *AcceptRequest) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -func (m *AcceptRequest) GetTimeoutSeconds() float64 { - if m != nil && m.TimeoutSeconds != nil { - return *m.TimeoutSeconds - } - return Default_AcceptRequest_TimeoutSeconds -} - -type AcceptReply struct { - NewSocketDescriptor []byte `protobuf:"bytes,2,opt,name=new_socket_descriptor" json:"new_socket_descriptor,omitempty"` - RemoteAddress *AddressPort `protobuf:"bytes,3,opt,name=remote_address" json:"remote_address,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *AcceptReply) Reset() { *m = AcceptReply{} } -func (m *AcceptReply) String() string { return proto.CompactTextString(m) } -func (*AcceptReply) ProtoMessage() {} - -func (m *AcceptReply) GetNewSocketDescriptor() []byte { - if m != nil { - return m.NewSocketDescriptor - } - return nil -} - -func (m *AcceptReply) GetRemoteAddress() *AddressPort { - if m != nil { - return m.RemoteAddress - } - return nil -} - -type ShutDownRequest struct { - SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"` - How *ShutDownRequest_How `protobuf:"varint,2,req,name=how,enum=appengine.ShutDownRequest_How" json:"how,omitempty"` - SendOffset *int64 `protobuf:"varint,3,req,name=send_offset" json:"send_offset,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *ShutDownRequest) Reset() { *m = ShutDownRequest{} } -func (m *ShutDownRequest) String() string { return proto.CompactTextString(m) } -func (*ShutDownRequest) ProtoMessage() {} - -func (m *ShutDownRequest) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -func (m *ShutDownRequest) GetHow() ShutDownRequest_How { - if m != nil && m.How != nil { - return *m.How - } - return ShutDownRequest_SOCKET_SHUT_RD -} - -func (m *ShutDownRequest) GetSendOffset() int64 { - if m != nil && m.SendOffset != nil { - return *m.SendOffset - } - return 0 -} - -type ShutDownReply struct { - XXX_unrecognized []byte `json:"-"` -} - -func (m *ShutDownReply) Reset() { *m = ShutDownReply{} } -func (m *ShutDownReply) String() string { return proto.CompactTextString(m) } -func (*ShutDownReply) ProtoMessage() {} - -type CloseRequest struct { - SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"` - SendOffset *int64 `protobuf:"varint,2,opt,name=send_offset,def=-1" json:"send_offset,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *CloseRequest) Reset() { *m = CloseRequest{} } -func (m *CloseRequest) String() string { return proto.CompactTextString(m) } -func (*CloseRequest) ProtoMessage() {} - -const Default_CloseRequest_SendOffset int64 = -1 - -func (m *CloseRequest) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -func (m *CloseRequest) GetSendOffset() int64 { - if m != nil && m.SendOffset != nil { - return *m.SendOffset - } - return Default_CloseRequest_SendOffset -} - -type CloseReply struct { - XXX_unrecognized []byte `json:"-"` -} - -func (m *CloseReply) Reset() { *m = CloseReply{} } -func (m *CloseReply) String() string { return proto.CompactTextString(m) } -func (*CloseReply) ProtoMessage() {} - -type SendRequest struct { - SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"` - Data []byte `protobuf:"bytes,2,req,name=data" json:"data,omitempty"` - StreamOffset *int64 `protobuf:"varint,3,req,name=stream_offset" json:"stream_offset,omitempty"` - Flags *int32 `protobuf:"varint,4,opt,name=flags,def=0" json:"flags,omitempty"` - SendTo *AddressPort `protobuf:"bytes,5,opt,name=send_to" json:"send_to,omitempty"` - TimeoutSeconds *float64 `protobuf:"fixed64,6,opt,name=timeout_seconds,def=-1" json:"timeout_seconds,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *SendRequest) Reset() { *m = SendRequest{} } -func (m *SendRequest) String() string { return proto.CompactTextString(m) } -func (*SendRequest) ProtoMessage() {} - -const Default_SendRequest_Flags int32 = 0 -const Default_SendRequest_TimeoutSeconds float64 = -1 - -func (m *SendRequest) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -func (m *SendRequest) GetData() []byte { - if m != nil { - return m.Data - } - return nil -} - -func (m *SendRequest) GetStreamOffset() int64 { - if m != nil && m.StreamOffset != nil { - return *m.StreamOffset - } - return 0 -} - -func (m *SendRequest) GetFlags() int32 { - if m != nil && m.Flags != nil { - return *m.Flags - } - return Default_SendRequest_Flags -} - -func (m *SendRequest) GetSendTo() *AddressPort { - if m != nil { - return m.SendTo - } - return nil -} - -func (m *SendRequest) GetTimeoutSeconds() float64 { - if m != nil && m.TimeoutSeconds != nil { - return *m.TimeoutSeconds - } - return Default_SendRequest_TimeoutSeconds -} - -type SendReply struct { - DataSent *int32 `protobuf:"varint,1,opt,name=data_sent" json:"data_sent,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *SendReply) Reset() { *m = SendReply{} } -func (m *SendReply) String() string { return proto.CompactTextString(m) } -func (*SendReply) ProtoMessage() {} - -func (m *SendReply) GetDataSent() int32 { - if m != nil && m.DataSent != nil { - return *m.DataSent - } - return 0 -} - -type ReceiveRequest struct { - SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"` - DataSize *int32 `protobuf:"varint,2,req,name=data_size" json:"data_size,omitempty"` - Flags *int32 `protobuf:"varint,3,opt,name=flags,def=0" json:"flags,omitempty"` - TimeoutSeconds *float64 `protobuf:"fixed64,5,opt,name=timeout_seconds,def=-1" json:"timeout_seconds,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *ReceiveRequest) Reset() { *m = ReceiveRequest{} } -func (m *ReceiveRequest) String() string { return proto.CompactTextString(m) } -func (*ReceiveRequest) ProtoMessage() {} - -const Default_ReceiveRequest_Flags int32 = 0 -const Default_ReceiveRequest_TimeoutSeconds float64 = -1 - -func (m *ReceiveRequest) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -func (m *ReceiveRequest) GetDataSize() int32 { - if m != nil && m.DataSize != nil { - return *m.DataSize - } - return 0 -} - -func (m *ReceiveRequest) GetFlags() int32 { - if m != nil && m.Flags != nil { - return *m.Flags - } - return Default_ReceiveRequest_Flags -} - -func (m *ReceiveRequest) GetTimeoutSeconds() float64 { - if m != nil && m.TimeoutSeconds != nil { - return *m.TimeoutSeconds - } - return Default_ReceiveRequest_TimeoutSeconds -} - -type ReceiveReply struct { - StreamOffset *int64 `protobuf:"varint,2,opt,name=stream_offset" json:"stream_offset,omitempty"` - Data []byte `protobuf:"bytes,3,opt,name=data" json:"data,omitempty"` - ReceivedFrom *AddressPort `protobuf:"bytes,4,opt,name=received_from" json:"received_from,omitempty"` - BufferSize *int32 `protobuf:"varint,5,opt,name=buffer_size" json:"buffer_size,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *ReceiveReply) Reset() { *m = ReceiveReply{} } -func (m *ReceiveReply) String() string { return proto.CompactTextString(m) } -func (*ReceiveReply) ProtoMessage() {} - -func (m *ReceiveReply) GetStreamOffset() int64 { - if m != nil && m.StreamOffset != nil { - return *m.StreamOffset - } - return 0 -} - -func (m *ReceiveReply) GetData() []byte { - if m != nil { - return m.Data - } - return nil -} - -func (m *ReceiveReply) GetReceivedFrom() *AddressPort { - if m != nil { - return m.ReceivedFrom - } - return nil -} - -func (m *ReceiveReply) GetBufferSize() int32 { - if m != nil && m.BufferSize != nil { - return *m.BufferSize - } - return 0 -} - -type PollEvent struct { - SocketDescriptor *string `protobuf:"bytes,1,req,name=socket_descriptor" json:"socket_descriptor,omitempty"` - RequestedEvents *int32 `protobuf:"varint,2,req,name=requested_events" json:"requested_events,omitempty"` - ObservedEvents *int32 `protobuf:"varint,3,req,name=observed_events" json:"observed_events,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *PollEvent) Reset() { *m = PollEvent{} } -func (m *PollEvent) String() string { return proto.CompactTextString(m) } -func (*PollEvent) ProtoMessage() {} - -func (m *PollEvent) GetSocketDescriptor() string { - if m != nil && m.SocketDescriptor != nil { - return *m.SocketDescriptor - } - return "" -} - -func (m *PollEvent) GetRequestedEvents() int32 { - if m != nil && m.RequestedEvents != nil { - return *m.RequestedEvents - } - return 0 -} - -func (m *PollEvent) GetObservedEvents() int32 { - if m != nil && m.ObservedEvents != nil { - return *m.ObservedEvents - } - return 0 -} - -type PollRequest struct { - Events []*PollEvent `protobuf:"bytes,1,rep,name=events" json:"events,omitempty"` - TimeoutSeconds *float64 `protobuf:"fixed64,2,opt,name=timeout_seconds,def=-1" json:"timeout_seconds,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *PollRequest) Reset() { *m = PollRequest{} } -func (m *PollRequest) String() string { return proto.CompactTextString(m) } -func (*PollRequest) ProtoMessage() {} - -const Default_PollRequest_TimeoutSeconds float64 = -1 - -func (m *PollRequest) GetEvents() []*PollEvent { - if m != nil { - return m.Events - } - return nil -} - -func (m *PollRequest) GetTimeoutSeconds() float64 { - if m != nil && m.TimeoutSeconds != nil { - return *m.TimeoutSeconds - } - return Default_PollRequest_TimeoutSeconds -} - -type PollReply struct { - Events []*PollEvent `protobuf:"bytes,2,rep,name=events" json:"events,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *PollReply) Reset() { *m = PollReply{} } -func (m *PollReply) String() string { return proto.CompactTextString(m) } -func (*PollReply) ProtoMessage() {} - -func (m *PollReply) GetEvents() []*PollEvent { - if m != nil { - return m.Events - } - return nil -} - -type ResolveRequest struct { - Name *string `protobuf:"bytes,1,req,name=name" json:"name,omitempty"` - AddressFamilies []CreateSocketRequest_SocketFamily `protobuf:"varint,2,rep,name=address_families,enum=appengine.CreateSocketRequest_SocketFamily" json:"address_families,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *ResolveRequest) Reset() { *m = ResolveRequest{} } -func (m *ResolveRequest) String() string { return proto.CompactTextString(m) } -func (*ResolveRequest) ProtoMessage() {} - -func (m *ResolveRequest) GetName() string { - if m != nil && m.Name != nil { - return *m.Name - } - return "" -} - -func (m *ResolveRequest) GetAddressFamilies() []CreateSocketRequest_SocketFamily { - if m != nil { - return m.AddressFamilies - } - return nil -} - -type ResolveReply struct { - PackedAddress [][]byte `protobuf:"bytes,2,rep,name=packed_address" json:"packed_address,omitempty"` - CanonicalName *string `protobuf:"bytes,3,opt,name=canonical_name" json:"canonical_name,omitempty"` - Aliases []string `protobuf:"bytes,4,rep,name=aliases" json:"aliases,omitempty"` - XXX_unrecognized []byte `json:"-"` -} - -func (m *ResolveReply) Reset() { *m = ResolveReply{} } -func (m *ResolveReply) String() string { return proto.CompactTextString(m) } -func (*ResolveReply) ProtoMessage() {} - -func (m *ResolveReply) GetPackedAddress() [][]byte { - if m != nil { - return m.PackedAddress - } - return nil -} - -func (m *ResolveReply) GetCanonicalName() string { - if m != nil && m.CanonicalName != nil { - return *m.CanonicalName - } - return "" -} - -func (m *ResolveReply) GetAliases() []string { - if m != nil { - return m.Aliases - } - return nil -} - -func init() { -} diff --git a/vendor/google.golang.org/appengine/socket/doc.go b/vendor/google.golang.org/appengine/socket/doc.go deleted file mode 100644 index 3de46df826b..00000000000 --- a/vendor/google.golang.org/appengine/socket/doc.go +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2012 Google Inc. All rights reserved. -// Use of this source code is governed by the Apache 2.0 -// license that can be found in the LICENSE file. - -// Package socket provides outbound network sockets. -// -// This package is only required in the classic App Engine environment. -// Applications running only in App Engine "flexible environment" should -// use the standard library's net package. -package socket diff --git a/vendor/google.golang.org/appengine/socket/socket_classic.go b/vendor/google.golang.org/appengine/socket/socket_classic.go deleted file mode 100644 index 0ad50e2d36d..00000000000 --- a/vendor/google.golang.org/appengine/socket/socket_classic.go +++ /dev/null @@ -1,290 +0,0 @@ -// Copyright 2012 Google Inc. All rights reserved. -// Use of this source code is governed by the Apache 2.0 -// license that can be found in the LICENSE file. - -// +build appengine - -package socket - -import ( - "fmt" - "io" - "net" - "strconv" - "time" - - "github.com/golang/protobuf/proto" - "golang.org/x/net/context" - "google.golang.org/appengine/internal" - - pb "google.golang.org/appengine/internal/socket" -) - -// Dial connects to the address addr on the network protocol. -// The address format is host:port, where host may be a hostname or an IP address. -// Known protocols are "tcp" and "udp". -// The returned connection satisfies net.Conn, and is valid while ctx is valid; -// if the connection is to be used after ctx becomes invalid, invoke SetContext -// with the new context. -func Dial(ctx context.Context, protocol, addr string) (*Conn, error) { - return DialTimeout(ctx, protocol, addr, 0) -} - -var ipFamilies = []pb.CreateSocketRequest_SocketFamily{ - pb.CreateSocketRequest_IPv4, - pb.CreateSocketRequest_IPv6, -} - -// DialTimeout is like Dial but takes a timeout. -// The timeout includes name resolution, if required. -func DialTimeout(ctx context.Context, protocol, addr string, timeout time.Duration) (*Conn, error) { - dialCtx := ctx // Used for dialing and name resolution, but not stored in the *Conn. - if timeout > 0 { - var cancel context.CancelFunc - dialCtx, cancel = context.WithTimeout(ctx, timeout) - defer cancel() - } - - host, portStr, err := net.SplitHostPort(addr) - if err != nil { - return nil, err - } - port, err := strconv.Atoi(portStr) - if err != nil { - return nil, fmt.Errorf("socket: bad port %q: %v", portStr, err) - } - - var prot pb.CreateSocketRequest_SocketProtocol - switch protocol { - case "tcp": - prot = pb.CreateSocketRequest_TCP - case "udp": - prot = pb.CreateSocketRequest_UDP - default: - return nil, fmt.Errorf("socket: unknown protocol %q", protocol) - } - - packedAddrs, resolved, err := resolve(dialCtx, ipFamilies, host) - if err != nil { - return nil, fmt.Errorf("socket: failed resolving %q: %v", host, err) - } - if len(packedAddrs) == 0 { - return nil, fmt.Errorf("no addresses for %q", host) - } - - packedAddr := packedAddrs[0] // use first address - fam := pb.CreateSocketRequest_IPv4 - if len(packedAddr) == net.IPv6len { - fam = pb.CreateSocketRequest_IPv6 - } - - req := &pb.CreateSocketRequest{ - Family: fam.Enum(), - Protocol: prot.Enum(), - RemoteIp: &pb.AddressPort{ - Port: proto.Int32(int32(port)), - PackedAddress: packedAddr, - }, - } - if resolved { - req.RemoteIp.HostnameHint = &host - } - res := &pb.CreateSocketReply{} - if err := internal.Call(dialCtx, "remote_socket", "CreateSocket", req, res); err != nil { - return nil, err - } - - return &Conn{ - ctx: ctx, - desc: res.GetSocketDescriptor(), - prot: prot, - local: res.ProxyExternalIp, - remote: req.RemoteIp, - }, nil -} - -// LookupIP returns the given host's IP addresses. -func LookupIP(ctx context.Context, host string) (addrs []net.IP, err error) { - packedAddrs, _, err := resolve(ctx, ipFamilies, host) - if err != nil { - return nil, fmt.Errorf("socket: failed resolving %q: %v", host, err) - } - addrs = make([]net.IP, len(packedAddrs)) - for i, pa := range packedAddrs { - addrs[i] = net.IP(pa) - } - return addrs, nil -} - -func resolve(ctx context.Context, fams []pb.CreateSocketRequest_SocketFamily, host string) ([][]byte, bool, error) { - // Check if it's an IP address. - if ip := net.ParseIP(host); ip != nil { - if ip := ip.To4(); ip != nil { - return [][]byte{ip}, false, nil - } - return [][]byte{ip}, false, nil - } - - req := &pb.ResolveRequest{ - Name: &host, - AddressFamilies: fams, - } - res := &pb.ResolveReply{} - if err := internal.Call(ctx, "remote_socket", "Resolve", req, res); err != nil { - // XXX: need to map to pb.ResolveReply_ErrorCode? - return nil, false, err - } - return res.PackedAddress, true, nil -} - -// withDeadline is like context.WithDeadline, except it ignores the zero deadline. -func withDeadline(parent context.Context, deadline time.Time) (context.Context, context.CancelFunc) { - if deadline.IsZero() { - return parent, func() {} - } - return context.WithDeadline(parent, deadline) -} - -// Conn represents a socket connection. -// It implements net.Conn. -type Conn struct { - ctx context.Context - desc string - offset int64 - - prot pb.CreateSocketRequest_SocketProtocol - local, remote *pb.AddressPort - - readDeadline, writeDeadline time.Time // optional -} - -// SetContext sets the context that is used by this Conn. -// It is usually used only when using a Conn that was created in a different context, -// such as when a connection is created during a warmup request but used while -// servicing a user request. -func (cn *Conn) SetContext(ctx context.Context) { - cn.ctx = ctx -} - -func (cn *Conn) Read(b []byte) (n int, err error) { - const maxRead = 1 << 20 - if len(b) > maxRead { - b = b[:maxRead] - } - - req := &pb.ReceiveRequest{ - SocketDescriptor: &cn.desc, - DataSize: proto.Int32(int32(len(b))), - } - res := &pb.ReceiveReply{} - if !cn.readDeadline.IsZero() { - req.TimeoutSeconds = proto.Float64(cn.readDeadline.Sub(time.Now()).Seconds()) - } - ctx, cancel := withDeadline(cn.ctx, cn.readDeadline) - defer cancel() - if err := internal.Call(ctx, "remote_socket", "Receive", req, res); err != nil { - return 0, err - } - if len(res.Data) == 0 { - return 0, io.EOF - } - if len(res.Data) > len(b) { - return 0, fmt.Errorf("socket: internal error: read too much data: %d > %d", len(res.Data), len(b)) - } - return copy(b, res.Data), nil -} - -func (cn *Conn) Write(b []byte) (n int, err error) { - const lim = 1 << 20 // max per chunk - - for n < len(b) { - chunk := b[n:] - if len(chunk) > lim { - chunk = chunk[:lim] - } - - req := &pb.SendRequest{ - SocketDescriptor: &cn.desc, - Data: chunk, - StreamOffset: &cn.offset, - } - res := &pb.SendReply{} - if !cn.writeDeadline.IsZero() { - req.TimeoutSeconds = proto.Float64(cn.writeDeadline.Sub(time.Now()).Seconds()) - } - ctx, cancel := withDeadline(cn.ctx, cn.writeDeadline) - defer cancel() - if err = internal.Call(ctx, "remote_socket", "Send", req, res); err != nil { - // assume zero bytes were sent in this RPC - break - } - n += int(res.GetDataSent()) - cn.offset += int64(res.GetDataSent()) - } - - return -} - -func (cn *Conn) Close() error { - req := &pb.CloseRequest{ - SocketDescriptor: &cn.desc, - } - res := &pb.CloseReply{} - if err := internal.Call(cn.ctx, "remote_socket", "Close", req, res); err != nil { - return err - } - cn.desc = "CLOSED" - return nil -} - -func addr(prot pb.CreateSocketRequest_SocketProtocol, ap *pb.AddressPort) net.Addr { - if ap == nil { - return nil - } - switch prot { - case pb.CreateSocketRequest_TCP: - return &net.TCPAddr{ - IP: net.IP(ap.PackedAddress), - Port: int(*ap.Port), - } - case pb.CreateSocketRequest_UDP: - return &net.UDPAddr{ - IP: net.IP(ap.PackedAddress), - Port: int(*ap.Port), - } - } - panic("unknown protocol " + prot.String()) -} - -func (cn *Conn) LocalAddr() net.Addr { return addr(cn.prot, cn.local) } -func (cn *Conn) RemoteAddr() net.Addr { return addr(cn.prot, cn.remote) } - -func (cn *Conn) SetDeadline(t time.Time) error { - cn.readDeadline = t - cn.writeDeadline = t - return nil -} - -func (cn *Conn) SetReadDeadline(t time.Time) error { - cn.readDeadline = t - return nil -} - -func (cn *Conn) SetWriteDeadline(t time.Time) error { - cn.writeDeadline = t - return nil -} - -// KeepAlive signals that the connection is still in use. -// It may be called to prevent the socket being closed due to inactivity. -func (cn *Conn) KeepAlive() error { - req := &pb.GetSocketNameRequest{ - SocketDescriptor: &cn.desc, - } - res := &pb.GetSocketNameReply{} - return internal.Call(cn.ctx, "remote_socket", "GetSocketName", req, res) -} - -func init() { - internal.RegisterErrorCodeMap("remote_socket", pb.RemoteSocketServiceError_ErrorCode_name) -} diff --git a/vendor/google.golang.org/appengine/socket/socket_vm.go b/vendor/google.golang.org/appengine/socket/socket_vm.go deleted file mode 100644 index c804169a1c0..00000000000 --- a/vendor/google.golang.org/appengine/socket/socket_vm.go +++ /dev/null @@ -1,64 +0,0 @@ -// Copyright 2015 Google Inc. All rights reserved. -// Use of this source code is governed by the Apache 2.0 -// license that can be found in the LICENSE file. - -// +build !appengine - -package socket - -import ( - "net" - "time" - - "golang.org/x/net/context" -) - -// Dial connects to the address addr on the network protocol. -// The address format is host:port, where host may be a hostname or an IP address. -// Known protocols are "tcp" and "udp". -// The returned connection satisfies net.Conn, and is valid while ctx is valid; -// if the connection is to be used after ctx becomes invalid, invoke SetContext -// with the new context. -func Dial(ctx context.Context, protocol, addr string) (*Conn, error) { - conn, err := net.Dial(protocol, addr) - if err != nil { - return nil, err - } - return &Conn{conn}, nil -} - -// DialTimeout is like Dial but takes a timeout. -// The timeout includes name resolution, if required. -func DialTimeout(ctx context.Context, protocol, addr string, timeout time.Duration) (*Conn, error) { - conn, err := net.DialTimeout(protocol, addr, timeout) - if err != nil { - return nil, err - } - return &Conn{conn}, nil -} - -// LookupIP returns the given host's IP addresses. -func LookupIP(ctx context.Context, host string) (addrs []net.IP, err error) { - return net.LookupIP(host) -} - -// Conn represents a socket connection. -// It implements net.Conn. -type Conn struct { - net.Conn -} - -// SetContext sets the context that is used by this Conn. -// It is usually used only when using a Conn that was created in a different context, -// such as when a connection is created during a warmup request but used while -// servicing a user request. -func (cn *Conn) SetContext(ctx context.Context) { - // This function is not required in App Engine "flexible environment". -} - -// KeepAlive signals that the connection is still in use. -// It may be called to prevent the socket being closed due to inactivity. -func (cn *Conn) KeepAlive() error { - // This function is not required in App Engine "flexible environment". - return nil -} diff --git a/vendor/google.golang.org/genproto/LICENSE b/vendor/google.golang.org/genproto/LICENSE deleted file mode 100644 index d6456956733..00000000000 --- a/vendor/google.golang.org/genproto/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/vendor/google.golang.org/genproto/googleapis/api/annotations/annotations.pb.go b/vendor/google.golang.org/genproto/googleapis/api/annotations/annotations.pb.go deleted file mode 100644 index c5a54b3d7ad..00000000000 --- a/vendor/google.golang.org/genproto/googleapis/api/annotations/annotations.pb.go +++ /dev/null @@ -1,54 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: google/api/annotations.proto - -package annotations // import "google.golang.org/genproto/googleapis/api/annotations" - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" -import descriptor "github.com/golang/protobuf/protoc-gen-go/descriptor" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -var E_Http = &proto.ExtensionDesc{ - ExtendedType: (*descriptor.MethodOptions)(nil), - ExtensionType: (*HttpRule)(nil), - Field: 72295728, - Name: "google.api.http", - Tag: "bytes,72295728,opt,name=http", - Filename: "google/api/annotations.proto", -} - -func init() { - proto.RegisterExtension(E_Http) -} - -func init() { - proto.RegisterFile("google/api/annotations.proto", fileDescriptor_annotations_7782c41cc734273a) -} - -var fileDescriptor_annotations_7782c41cc734273a = []byte{ - // 208 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x49, 0xcf, 0xcf, 0x4f, - 0xcf, 0x49, 0xd5, 0x4f, 0x2c, 0xc8, 0xd4, 0x4f, 0xcc, 0xcb, 0xcb, 0x2f, 0x49, 0x2c, 0xc9, 0xcc, - 0xcf, 0x2b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x82, 0xc8, 0xea, 0x25, 0x16, 0x64, - 0x4a, 0x89, 0x22, 0xa9, 0xcc, 0x28, 0x29, 0x29, 0x80, 0x28, 0x91, 0x52, 0x80, 0x0a, 0x83, 0x79, - 0x49, 0xa5, 0x69, 0xfa, 0x29, 0xa9, 0xc5, 0xc9, 0x45, 0x99, 0x05, 0x25, 0xf9, 0x45, 0x10, 0x15, - 0x56, 0xde, 0x5c, 0x2c, 0x20, 0xf5, 0x42, 0x72, 0x7a, 0x50, 0xd3, 0x60, 0x4a, 0xf5, 0x7c, 0x53, - 0x4b, 0x32, 0xf2, 0x53, 0xfc, 0x0b, 0xc0, 0x56, 0x4a, 0x6c, 0x38, 0xb5, 0x47, 0x49, 0x81, 0x51, - 0x83, 0xdb, 0x48, 0x44, 0x0f, 0x61, 0xad, 0x9e, 0x47, 0x49, 0x49, 0x41, 0x50, 0x69, 0x4e, 0x6a, - 0x10, 0xd8, 0x10, 0xa7, 0x3c, 0x2e, 0xbe, 0xe4, 0xfc, 0x5c, 0x24, 0x05, 0x4e, 0x02, 0x8e, 0x08, - 0x67, 0x07, 0x80, 0x4c, 0x0e, 0x60, 0x8c, 0x72, 0x84, 0xca, 0xa7, 0xe7, 0xe7, 0x24, 0xe6, 0xa5, - 0xeb, 0xe5, 0x17, 0xa5, 0xeb, 0xa7, 0xa7, 0xe6, 0x81, 0xed, 0xd5, 0x87, 0x48, 0x25, 0x16, 0x64, - 0x16, 0xa3, 0x7b, 0xda, 0x1a, 0x89, 0xbd, 0x88, 0x89, 0xc5, 0xdd, 0x31, 0xc0, 0x33, 0x89, 0x0d, - 0xac, 0xc9, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x29, 0x19, 0x62, 0x28, 0x01, 0x00, 0x00, -} diff --git a/vendor/google.golang.org/genproto/googleapis/api/annotations/http.pb.go b/vendor/google.golang.org/genproto/googleapis/api/annotations/http.pb.go deleted file mode 100644 index e32d3b4f30e..00000000000 --- a/vendor/google.golang.org/genproto/googleapis/api/annotations/http.pb.go +++ /dev/null @@ -1,666 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: google/api/http.proto - -package annotations // import "google.golang.org/genproto/googleapis/api/annotations" - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -// Defines the HTTP configuration for an API service. It contains a list of -// [HttpRule][google.api.HttpRule], each specifying the mapping of an RPC method -// to one or more HTTP REST API methods. -type Http struct { - // A list of HTTP configuration rules that apply to individual API methods. - // - // **NOTE:** All service configuration rules follow "last one wins" order. - Rules []*HttpRule `protobuf:"bytes,1,rep,name=rules" json:"rules,omitempty"` - // When set to true, URL path parmeters will be fully URI-decoded except in - // cases of single segment matches in reserved expansion, where "%2F" will be - // left encoded. - // - // The default behavior is to not decode RFC 6570 reserved characters in multi - // segment matches. - FullyDecodeReservedExpansion bool `protobuf:"varint,2,opt,name=fully_decode_reserved_expansion,json=fullyDecodeReservedExpansion" json:"fully_decode_reserved_expansion,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Http) Reset() { *m = Http{} } -func (m *Http) String() string { return proto.CompactTextString(m) } -func (*Http) ProtoMessage() {} -func (*Http) Descriptor() ([]byte, []int) { - return fileDescriptor_http_9c97bbd8b94894d4, []int{0} -} -func (m *Http) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Http.Unmarshal(m, b) -} -func (m *Http) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Http.Marshal(b, m, deterministic) -} -func (dst *Http) XXX_Merge(src proto.Message) { - xxx_messageInfo_Http.Merge(dst, src) -} -func (m *Http) XXX_Size() int { - return xxx_messageInfo_Http.Size(m) -} -func (m *Http) XXX_DiscardUnknown() { - xxx_messageInfo_Http.DiscardUnknown(m) -} - -var xxx_messageInfo_Http proto.InternalMessageInfo - -func (m *Http) GetRules() []*HttpRule { - if m != nil { - return m.Rules - } - return nil -} - -func (m *Http) GetFullyDecodeReservedExpansion() bool { - if m != nil { - return m.FullyDecodeReservedExpansion - } - return false -} - -// `HttpRule` defines the mapping of an RPC method to one or more HTTP -// REST API methods. The mapping specifies how different portions of the RPC -// request message are mapped to URL path, URL query parameters, and -// HTTP request body. The mapping is typically specified as an -// `google.api.http` annotation on the RPC method, -// see "google/api/annotations.proto" for details. -// -// The mapping consists of a field specifying the path template and -// method kind. The path template can refer to fields in the request -// message, as in the example below which describes a REST GET -// operation on a resource collection of messages: -// -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http).get = "/v1/messages/{message_id}/{sub.subfield}"; -// } -// } -// message GetMessageRequest { -// message SubMessage { -// string subfield = 1; -// } -// string message_id = 1; // mapped to the URL -// SubMessage sub = 2; // `sub.subfield` is url-mapped -// } -// message Message { -// string text = 1; // content of the resource -// } -// -// The same http annotation can alternatively be expressed inside the -// `GRPC API Configuration` YAML file. -// -// http: -// rules: -// - selector: .Messaging.GetMessage -// get: /v1/messages/{message_id}/{sub.subfield} -// -// This definition enables an automatic, bidrectional mapping of HTTP -// JSON to RPC. Example: -// -// HTTP | RPC -// -----|----- -// `GET /v1/messages/123456/foo` | `GetMessage(message_id: "123456" sub: SubMessage(subfield: "foo"))` -// -// In general, not only fields but also field paths can be referenced -// from a path pattern. Fields mapped to the path pattern cannot be -// repeated and must have a primitive (non-message) type. -// -// Any fields in the request message which are not bound by the path -// pattern automatically become (optional) HTTP query -// parameters. Assume the following definition of the request message: -// -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http).get = "/v1/messages/{message_id}"; -// } -// } -// message GetMessageRequest { -// message SubMessage { -// string subfield = 1; -// } -// string message_id = 1; // mapped to the URL -// int64 revision = 2; // becomes a parameter -// SubMessage sub = 3; // `sub.subfield` becomes a parameter -// } -// -// -// This enables a HTTP JSON to RPC mapping as below: -// -// HTTP | RPC -// -----|----- -// `GET /v1/messages/123456?revision=2&sub.subfield=foo` | `GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))` -// -// Note that fields which are mapped to HTTP parameters must have a -// primitive type or a repeated primitive type. Message types are not -// allowed. In the case of a repeated type, the parameter can be -// repeated in the URL, as in `...?param=A¶m=B`. -// -// For HTTP method kinds which allow a request body, the `body` field -// specifies the mapping. Consider a REST update method on the -// message resource collection: -// -// -// service Messaging { -// rpc UpdateMessage(UpdateMessageRequest) returns (Message) { -// option (google.api.http) = { -// put: "/v1/messages/{message_id}" -// body: "message" -// }; -// } -// } -// message UpdateMessageRequest { -// string message_id = 1; // mapped to the URL -// Message message = 2; // mapped to the body -// } -// -// -// The following HTTP JSON to RPC mapping is enabled, where the -// representation of the JSON in the request body is determined by -// protos JSON encoding: -// -// HTTP | RPC -// -----|----- -// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" message { text: "Hi!" })` -// -// The special name `*` can be used in the body mapping to define that -// every field not bound by the path template should be mapped to the -// request body. This enables the following alternative definition of -// the update method: -// -// service Messaging { -// rpc UpdateMessage(Message) returns (Message) { -// option (google.api.http) = { -// put: "/v1/messages/{message_id}" -// body: "*" -// }; -// } -// } -// message Message { -// string message_id = 1; -// string text = 2; -// } -// -// -// The following HTTP JSON to RPC mapping is enabled: -// -// HTTP | RPC -// -----|----- -// `PUT /v1/messages/123456 { "text": "Hi!" }` | `UpdateMessage(message_id: "123456" text: "Hi!")` -// -// Note that when using `*` in the body mapping, it is not possible to -// have HTTP parameters, as all fields not bound by the path end in -// the body. This makes this option more rarely used in practice of -// defining REST APIs. The common usage of `*` is in custom methods -// which don't use the URL at all for transferring data. -// -// It is possible to define multiple HTTP methods for one RPC by using -// the `additional_bindings` option. Example: -// -// service Messaging { -// rpc GetMessage(GetMessageRequest) returns (Message) { -// option (google.api.http) = { -// get: "/v1/messages/{message_id}" -// additional_bindings { -// get: "/v1/users/{user_id}/messages/{message_id}" -// } -// }; -// } -// } -// message GetMessageRequest { -// string message_id = 1; -// string user_id = 2; -// } -// -// -// This enables the following two alternative HTTP JSON to RPC -// mappings: -// -// HTTP | RPC -// -----|----- -// `GET /v1/messages/123456` | `GetMessage(message_id: "123456")` -// `GET /v1/users/me/messages/123456` | `GetMessage(user_id: "me" message_id: "123456")` -// -// # Rules for HTTP mapping -// -// The rules for mapping HTTP path, query parameters, and body fields -// to the request message are as follows: -// -// 1. The `body` field specifies either `*` or a field path, or is -// omitted. If omitted, it indicates there is no HTTP request body. -// 2. Leaf fields (recursive expansion of nested messages in the -// request) can be classified into three types: -// (a) Matched in the URL template. -// (b) Covered by body (if body is `*`, everything except (a) fields; -// else everything under the body field) -// (c) All other fields. -// 3. URL query parameters found in the HTTP request are mapped to (c) fields. -// 4. Any body sent with an HTTP request can contain only (b) fields. -// -// The syntax of the path template is as follows: -// -// Template = "/" Segments [ Verb ] ; -// Segments = Segment { "/" Segment } ; -// Segment = "*" | "**" | LITERAL | Variable ; -// Variable = "{" FieldPath [ "=" Segments ] "}" ; -// FieldPath = IDENT { "." IDENT } ; -// Verb = ":" LITERAL ; -// -// The syntax `*` matches a single path segment. The syntax `**` matches zero -// or more path segments, which must be the last part of the path except the -// `Verb`. The syntax `LITERAL` matches literal text in the path. -// -// The syntax `Variable` matches part of the URL path as specified by its -// template. A variable template must not contain other variables. If a variable -// matches a single path segment, its template may be omitted, e.g. `{var}` -// is equivalent to `{var=*}`. -// -// If a variable contains exactly one path segment, such as `"{var}"` or -// `"{var=*}"`, when such a variable is expanded into a URL path, all characters -// except `[-_.~0-9a-zA-Z]` are percent-encoded. Such variables show up in the -// Discovery Document as `{var}`. -// -// If a variable contains one or more path segments, such as `"{var=foo/*}"` -// or `"{var=**}"`, when such a variable is expanded into a URL path, all -// characters except `[-_.~/0-9a-zA-Z]` are percent-encoded. Such variables -// show up in the Discovery Document as `{+var}`. -// -// NOTE: While the single segment variable matches the semantics of -// [RFC 6570](https://tools.ietf.org/html/rfc6570) Section 3.2.2 -// Simple String Expansion, the multi segment variable **does not** match -// RFC 6570 Reserved Expansion. The reason is that the Reserved Expansion -// does not expand special characters like `?` and `#`, which would lead -// to invalid URLs. -// -// NOTE: the field paths in variables and in the `body` must not refer to -// repeated fields or map fields. -type HttpRule struct { - // Selects methods to which this rule applies. - // - // Refer to [selector][google.api.DocumentationRule.selector] for syntax details. - Selector string `protobuf:"bytes,1,opt,name=selector" json:"selector,omitempty"` - // Determines the URL pattern is matched by this rules. This pattern can be - // used with any of the {get|put|post|delete|patch} methods. A custom method - // can be defined using the 'custom' field. - // - // Types that are valid to be assigned to Pattern: - // *HttpRule_Get - // *HttpRule_Put - // *HttpRule_Post - // *HttpRule_Delete - // *HttpRule_Patch - // *HttpRule_Custom - Pattern isHttpRule_Pattern `protobuf_oneof:"pattern"` - // The name of the request field whose value is mapped to the HTTP body, or - // `*` for mapping all fields not captured by the path pattern to the HTTP - // body. NOTE: the referred field must not be a repeated field and must be - // present at the top-level of request message type. - Body string `protobuf:"bytes,7,opt,name=body" json:"body,omitempty"` - // Additional HTTP bindings for the selector. Nested bindings must - // not contain an `additional_bindings` field themselves (that is, - // the nesting may only be one level deep). - AdditionalBindings []*HttpRule `protobuf:"bytes,11,rep,name=additional_bindings,json=additionalBindings" json:"additional_bindings,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *HttpRule) Reset() { *m = HttpRule{} } -func (m *HttpRule) String() string { return proto.CompactTextString(m) } -func (*HttpRule) ProtoMessage() {} -func (*HttpRule) Descriptor() ([]byte, []int) { - return fileDescriptor_http_9c97bbd8b94894d4, []int{1} -} -func (m *HttpRule) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_HttpRule.Unmarshal(m, b) -} -func (m *HttpRule) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_HttpRule.Marshal(b, m, deterministic) -} -func (dst *HttpRule) XXX_Merge(src proto.Message) { - xxx_messageInfo_HttpRule.Merge(dst, src) -} -func (m *HttpRule) XXX_Size() int { - return xxx_messageInfo_HttpRule.Size(m) -} -func (m *HttpRule) XXX_DiscardUnknown() { - xxx_messageInfo_HttpRule.DiscardUnknown(m) -} - -var xxx_messageInfo_HttpRule proto.InternalMessageInfo - -type isHttpRule_Pattern interface { - isHttpRule_Pattern() -} - -type HttpRule_Get struct { - Get string `protobuf:"bytes,2,opt,name=get,oneof"` -} -type HttpRule_Put struct { - Put string `protobuf:"bytes,3,opt,name=put,oneof"` -} -type HttpRule_Post struct { - Post string `protobuf:"bytes,4,opt,name=post,oneof"` -} -type HttpRule_Delete struct { - Delete string `protobuf:"bytes,5,opt,name=delete,oneof"` -} -type HttpRule_Patch struct { - Patch string `protobuf:"bytes,6,opt,name=patch,oneof"` -} -type HttpRule_Custom struct { - Custom *CustomHttpPattern `protobuf:"bytes,8,opt,name=custom,oneof"` -} - -func (*HttpRule_Get) isHttpRule_Pattern() {} -func (*HttpRule_Put) isHttpRule_Pattern() {} -func (*HttpRule_Post) isHttpRule_Pattern() {} -func (*HttpRule_Delete) isHttpRule_Pattern() {} -func (*HttpRule_Patch) isHttpRule_Pattern() {} -func (*HttpRule_Custom) isHttpRule_Pattern() {} - -func (m *HttpRule) GetPattern() isHttpRule_Pattern { - if m != nil { - return m.Pattern - } - return nil -} - -func (m *HttpRule) GetSelector() string { - if m != nil { - return m.Selector - } - return "" -} - -func (m *HttpRule) GetGet() string { - if x, ok := m.GetPattern().(*HttpRule_Get); ok { - return x.Get - } - return "" -} - -func (m *HttpRule) GetPut() string { - if x, ok := m.GetPattern().(*HttpRule_Put); ok { - return x.Put - } - return "" -} - -func (m *HttpRule) GetPost() string { - if x, ok := m.GetPattern().(*HttpRule_Post); ok { - return x.Post - } - return "" -} - -func (m *HttpRule) GetDelete() string { - if x, ok := m.GetPattern().(*HttpRule_Delete); ok { - return x.Delete - } - return "" -} - -func (m *HttpRule) GetPatch() string { - if x, ok := m.GetPattern().(*HttpRule_Patch); ok { - return x.Patch - } - return "" -} - -func (m *HttpRule) GetCustom() *CustomHttpPattern { - if x, ok := m.GetPattern().(*HttpRule_Custom); ok { - return x.Custom - } - return nil -} - -func (m *HttpRule) GetBody() string { - if m != nil { - return m.Body - } - return "" -} - -func (m *HttpRule) GetAdditionalBindings() []*HttpRule { - if m != nil { - return m.AdditionalBindings - } - return nil -} - -// XXX_OneofFuncs is for the internal use of the proto package. -func (*HttpRule) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _HttpRule_OneofMarshaler, _HttpRule_OneofUnmarshaler, _HttpRule_OneofSizer, []interface{}{ - (*HttpRule_Get)(nil), - (*HttpRule_Put)(nil), - (*HttpRule_Post)(nil), - (*HttpRule_Delete)(nil), - (*HttpRule_Patch)(nil), - (*HttpRule_Custom)(nil), - } -} - -func _HttpRule_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*HttpRule) - // pattern - switch x := m.Pattern.(type) { - case *HttpRule_Get: - b.EncodeVarint(2<<3 | proto.WireBytes) - b.EncodeStringBytes(x.Get) - case *HttpRule_Put: - b.EncodeVarint(3<<3 | proto.WireBytes) - b.EncodeStringBytes(x.Put) - case *HttpRule_Post: - b.EncodeVarint(4<<3 | proto.WireBytes) - b.EncodeStringBytes(x.Post) - case *HttpRule_Delete: - b.EncodeVarint(5<<3 | proto.WireBytes) - b.EncodeStringBytes(x.Delete) - case *HttpRule_Patch: - b.EncodeVarint(6<<3 | proto.WireBytes) - b.EncodeStringBytes(x.Patch) - case *HttpRule_Custom: - b.EncodeVarint(8<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Custom); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("HttpRule.Pattern has unexpected type %T", x) - } - return nil -} - -func _HttpRule_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*HttpRule) - switch tag { - case 2: // pattern.get - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.Pattern = &HttpRule_Get{x} - return true, err - case 3: // pattern.put - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.Pattern = &HttpRule_Put{x} - return true, err - case 4: // pattern.post - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.Pattern = &HttpRule_Post{x} - return true, err - case 5: // pattern.delete - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.Pattern = &HttpRule_Delete{x} - return true, err - case 6: // pattern.patch - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.Pattern = &HttpRule_Patch{x} - return true, err - case 8: // pattern.custom - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(CustomHttpPattern) - err := b.DecodeMessage(msg) - m.Pattern = &HttpRule_Custom{msg} - return true, err - default: - return false, nil - } -} - -func _HttpRule_OneofSizer(msg proto.Message) (n int) { - m := msg.(*HttpRule) - // pattern - switch x := m.Pattern.(type) { - case *HttpRule_Get: - n += 1 // tag and wire - n += proto.SizeVarint(uint64(len(x.Get))) - n += len(x.Get) - case *HttpRule_Put: - n += 1 // tag and wire - n += proto.SizeVarint(uint64(len(x.Put))) - n += len(x.Put) - case *HttpRule_Post: - n += 1 // tag and wire - n += proto.SizeVarint(uint64(len(x.Post))) - n += len(x.Post) - case *HttpRule_Delete: - n += 1 // tag and wire - n += proto.SizeVarint(uint64(len(x.Delete))) - n += len(x.Delete) - case *HttpRule_Patch: - n += 1 // tag and wire - n += proto.SizeVarint(uint64(len(x.Patch))) - n += len(x.Patch) - case *HttpRule_Custom: - s := proto.Size(x.Custom) - n += 1 // tag and wire - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - -// A custom pattern is used for defining custom HTTP verb. -type CustomHttpPattern struct { - // The name of this custom HTTP verb. - Kind string `protobuf:"bytes,1,opt,name=kind" json:"kind,omitempty"` - // The path matched by this custom verb. - Path string `protobuf:"bytes,2,opt,name=path" json:"path,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *CustomHttpPattern) Reset() { *m = CustomHttpPattern{} } -func (m *CustomHttpPattern) String() string { return proto.CompactTextString(m) } -func (*CustomHttpPattern) ProtoMessage() {} -func (*CustomHttpPattern) Descriptor() ([]byte, []int) { - return fileDescriptor_http_9c97bbd8b94894d4, []int{2} -} -func (m *CustomHttpPattern) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_CustomHttpPattern.Unmarshal(m, b) -} -func (m *CustomHttpPattern) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_CustomHttpPattern.Marshal(b, m, deterministic) -} -func (dst *CustomHttpPattern) XXX_Merge(src proto.Message) { - xxx_messageInfo_CustomHttpPattern.Merge(dst, src) -} -func (m *CustomHttpPattern) XXX_Size() int { - return xxx_messageInfo_CustomHttpPattern.Size(m) -} -func (m *CustomHttpPattern) XXX_DiscardUnknown() { - xxx_messageInfo_CustomHttpPattern.DiscardUnknown(m) -} - -var xxx_messageInfo_CustomHttpPattern proto.InternalMessageInfo - -func (m *CustomHttpPattern) GetKind() string { - if m != nil { - return m.Kind - } - return "" -} - -func (m *CustomHttpPattern) GetPath() string { - if m != nil { - return m.Path - } - return "" -} - -func init() { - proto.RegisterType((*Http)(nil), "google.api.Http") - proto.RegisterType((*HttpRule)(nil), "google.api.HttpRule") - proto.RegisterType((*CustomHttpPattern)(nil), "google.api.CustomHttpPattern") -} - -func init() { proto.RegisterFile("google/api/http.proto", fileDescriptor_http_9c97bbd8b94894d4) } - -var fileDescriptor_http_9c97bbd8b94894d4 = []byte{ - // 401 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x92, 0x41, 0xab, 0x13, 0x31, - 0x10, 0xc7, 0xdd, 0x76, 0xdb, 0xd7, 0x4e, 0x41, 0x30, 0x3e, 0x25, 0x88, 0x62, 0xe9, 0xa9, 0x78, - 0xd8, 0xc2, 0xf3, 0xe0, 0xe1, 0x9d, 0x5e, 0xb5, 0xf8, 0xbc, 0x95, 0x3d, 0x7a, 0x29, 0xe9, 0x66, - 0x4c, 0xa3, 0x79, 0x49, 0xd8, 0xcc, 0x8a, 0xfd, 0x3a, 0x7e, 0x07, 0xbf, 0x9b, 0x47, 0x49, 0x36, - 0xb5, 0x05, 0xc1, 0xdb, 0xfc, 0xff, 0xf3, 0xcb, 0xcc, 0x64, 0x18, 0x78, 0xa6, 0x9c, 0x53, 0x06, - 0x57, 0xc2, 0xeb, 0xd5, 0x81, 0xc8, 0x57, 0xbe, 0x75, 0xe4, 0x18, 0xf4, 0x76, 0x25, 0xbc, 0x5e, - 0x1c, 0xa1, 0xbc, 0x27, 0xf2, 0xec, 0x0d, 0x8c, 0xda, 0xce, 0x60, 0xe0, 0xc5, 0x7c, 0xb8, 0x9c, - 0xdd, 0x5c, 0x57, 0x67, 0xa6, 0x8a, 0x40, 0xdd, 0x19, 0xac, 0x7b, 0x84, 0x6d, 0xe0, 0xf5, 0x97, - 0xce, 0x98, 0xe3, 0x4e, 0x62, 0xe3, 0x24, 0xee, 0x5a, 0x0c, 0xd8, 0x7e, 0x47, 0xb9, 0xc3, 0x1f, - 0x5e, 0xd8, 0xa0, 0x9d, 0xe5, 0x83, 0x79, 0xb1, 0x9c, 0xd4, 0x2f, 0x13, 0xf6, 0x21, 0x51, 0x75, - 0x86, 0x36, 0x27, 0x66, 0xf1, 0x6b, 0x00, 0x93, 0x53, 0x69, 0xf6, 0x02, 0x26, 0x01, 0x0d, 0x36, - 0xe4, 0x5a, 0x5e, 0xcc, 0x8b, 0xe5, 0xb4, 0xfe, 0xab, 0x19, 0x83, 0xa1, 0x42, 0x4a, 0x35, 0xa7, - 0xf7, 0x8f, 0xea, 0x28, 0xa2, 0xe7, 0x3b, 0xe2, 0xc3, 0x93, 0xe7, 0x3b, 0x62, 0xd7, 0x50, 0x7a, - 0x17, 0x88, 0x97, 0xd9, 0x4c, 0x8a, 0x71, 0x18, 0x4b, 0x34, 0x48, 0xc8, 0x47, 0xd9, 0xcf, 0x9a, - 0x3d, 0x87, 0x91, 0x17, 0xd4, 0x1c, 0xf8, 0x38, 0x27, 0x7a, 0xc9, 0xde, 0xc1, 0xb8, 0xe9, 0x02, - 0xb9, 0x07, 0x3e, 0x99, 0x17, 0xcb, 0xd9, 0xcd, 0xab, 0xcb, 0x65, 0xbc, 0x4f, 0x99, 0x38, 0xf7, - 0x56, 0x10, 0x61, 0x6b, 0x63, 0xc1, 0x1e, 0x67, 0x0c, 0xca, 0xbd, 0x93, 0x47, 0x7e, 0x95, 0x3e, - 0x90, 0x62, 0xb6, 0x81, 0xa7, 0x42, 0x4a, 0x4d, 0xda, 0x59, 0x61, 0x76, 0x7b, 0x6d, 0xa5, 0xb6, - 0x2a, 0xf0, 0xd9, 0x7f, 0xd6, 0xcc, 0xce, 0x0f, 0xd6, 0x99, 0x5f, 0x4f, 0xe1, 0xca, 0xf7, 0xfd, - 0x16, 0xb7, 0xf0, 0xe4, 0x9f, 0x21, 0x62, 0xeb, 0x6f, 0xda, 0xca, 0xbc, 0xbb, 0x14, 0x47, 0xcf, - 0x0b, 0x3a, 0xf4, 0x8b, 0xab, 0x53, 0xbc, 0xfe, 0x0a, 0x8f, 0x1b, 0xf7, 0x70, 0xd1, 0x76, 0x3d, - 0x4d, 0x65, 0xe2, 0x61, 0x6c, 0x8b, 0xcf, 0x77, 0x39, 0xa1, 0x9c, 0x11, 0x56, 0x55, 0xae, 0x55, - 0x2b, 0x85, 0x36, 0x9d, 0xcd, 0xaa, 0x4f, 0x09, 0xaf, 0x43, 0x3a, 0x28, 0x61, 0xad, 0x23, 0x11, - 0xc7, 0x0c, 0xb7, 0x17, 0xf1, 0xef, 0xa2, 0xf8, 0x39, 0x28, 0x3f, 0xde, 0x6d, 0x3f, 0xed, 0xc7, - 0xe9, 0xdd, 0xdb, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x73, 0x2c, 0xed, 0xfb, 0x87, 0x02, 0x00, - 0x00, -} diff --git a/vendor/google.golang.org/genproto/googleapis/iam/v1/iam_policy.pb.go b/vendor/google.golang.org/genproto/googleapis/iam/v1/iam_policy.pb.go deleted file mode 100644 index 0450c83f77f..00000000000 --- a/vendor/google.golang.org/genproto/googleapis/iam/v1/iam_policy.pb.go +++ /dev/null @@ -1,412 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: google/iam/v1/iam_policy.proto - -package iam // import "google.golang.org/genproto/googleapis/iam/v1" - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "google.golang.org/genproto/googleapis/api/annotations" - -import ( - context "golang.org/x/net/context" - grpc "google.golang.org/grpc" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -// Request message for `SetIamPolicy` method. -type SetIamPolicyRequest struct { - // REQUIRED: The resource for which the policy is being specified. - // `resource` is usually specified as a path. For example, a Project - // resource is specified as `projects/{project}`. - Resource string `protobuf:"bytes,1,opt,name=resource" json:"resource,omitempty"` - // REQUIRED: The complete policy to be applied to the `resource`. The size of - // the policy is limited to a few 10s of KB. An empty policy is a - // valid policy but certain Cloud Platform services (such as Projects) - // might reject them. - Policy *Policy `protobuf:"bytes,2,opt,name=policy" json:"policy,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *SetIamPolicyRequest) Reset() { *m = SetIamPolicyRequest{} } -func (m *SetIamPolicyRequest) String() string { return proto.CompactTextString(m) } -func (*SetIamPolicyRequest) ProtoMessage() {} -func (*SetIamPolicyRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_iam_policy_511a6b0802f7199d, []int{0} -} -func (m *SetIamPolicyRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SetIamPolicyRequest.Unmarshal(m, b) -} -func (m *SetIamPolicyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SetIamPolicyRequest.Marshal(b, m, deterministic) -} -func (dst *SetIamPolicyRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_SetIamPolicyRequest.Merge(dst, src) -} -func (m *SetIamPolicyRequest) XXX_Size() int { - return xxx_messageInfo_SetIamPolicyRequest.Size(m) -} -func (m *SetIamPolicyRequest) XXX_DiscardUnknown() { - xxx_messageInfo_SetIamPolicyRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_SetIamPolicyRequest proto.InternalMessageInfo - -func (m *SetIamPolicyRequest) GetResource() string { - if m != nil { - return m.Resource - } - return "" -} - -func (m *SetIamPolicyRequest) GetPolicy() *Policy { - if m != nil { - return m.Policy - } - return nil -} - -// Request message for `GetIamPolicy` method. -type GetIamPolicyRequest struct { - // REQUIRED: The resource for which the policy is being requested. - // `resource` is usually specified as a path. For example, a Project - // resource is specified as `projects/{project}`. - Resource string `protobuf:"bytes,1,opt,name=resource" json:"resource,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetIamPolicyRequest) Reset() { *m = GetIamPolicyRequest{} } -func (m *GetIamPolicyRequest) String() string { return proto.CompactTextString(m) } -func (*GetIamPolicyRequest) ProtoMessage() {} -func (*GetIamPolicyRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_iam_policy_511a6b0802f7199d, []int{1} -} -func (m *GetIamPolicyRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetIamPolicyRequest.Unmarshal(m, b) -} -func (m *GetIamPolicyRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetIamPolicyRequest.Marshal(b, m, deterministic) -} -func (dst *GetIamPolicyRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetIamPolicyRequest.Merge(dst, src) -} -func (m *GetIamPolicyRequest) XXX_Size() int { - return xxx_messageInfo_GetIamPolicyRequest.Size(m) -} -func (m *GetIamPolicyRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetIamPolicyRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetIamPolicyRequest proto.InternalMessageInfo - -func (m *GetIamPolicyRequest) GetResource() string { - if m != nil { - return m.Resource - } - return "" -} - -// Request message for `TestIamPermissions` method. -type TestIamPermissionsRequest struct { - // REQUIRED: The resource for which the policy detail is being requested. - // `resource` is usually specified as a path. For example, a Project - // resource is specified as `projects/{project}`. - Resource string `protobuf:"bytes,1,opt,name=resource" json:"resource,omitempty"` - // The set of permissions to check for the `resource`. Permissions with - // wildcards (such as '*' or 'storage.*') are not allowed. For more - // information see - // [IAM Overview](https://cloud.google.com/iam/docs/overview#permissions). - Permissions []string `protobuf:"bytes,2,rep,name=permissions" json:"permissions,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *TestIamPermissionsRequest) Reset() { *m = TestIamPermissionsRequest{} } -func (m *TestIamPermissionsRequest) String() string { return proto.CompactTextString(m) } -func (*TestIamPermissionsRequest) ProtoMessage() {} -func (*TestIamPermissionsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_iam_policy_511a6b0802f7199d, []int{2} -} -func (m *TestIamPermissionsRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TestIamPermissionsRequest.Unmarshal(m, b) -} -func (m *TestIamPermissionsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TestIamPermissionsRequest.Marshal(b, m, deterministic) -} -func (dst *TestIamPermissionsRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_TestIamPermissionsRequest.Merge(dst, src) -} -func (m *TestIamPermissionsRequest) XXX_Size() int { - return xxx_messageInfo_TestIamPermissionsRequest.Size(m) -} -func (m *TestIamPermissionsRequest) XXX_DiscardUnknown() { - xxx_messageInfo_TestIamPermissionsRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_TestIamPermissionsRequest proto.InternalMessageInfo - -func (m *TestIamPermissionsRequest) GetResource() string { - if m != nil { - return m.Resource - } - return "" -} - -func (m *TestIamPermissionsRequest) GetPermissions() []string { - if m != nil { - return m.Permissions - } - return nil -} - -// Response message for `TestIamPermissions` method. -type TestIamPermissionsResponse struct { - // A subset of `TestPermissionsRequest.permissions` that the caller is - // allowed. - Permissions []string `protobuf:"bytes,1,rep,name=permissions" json:"permissions,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *TestIamPermissionsResponse) Reset() { *m = TestIamPermissionsResponse{} } -func (m *TestIamPermissionsResponse) String() string { return proto.CompactTextString(m) } -func (*TestIamPermissionsResponse) ProtoMessage() {} -func (*TestIamPermissionsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_iam_policy_511a6b0802f7199d, []int{3} -} -func (m *TestIamPermissionsResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_TestIamPermissionsResponse.Unmarshal(m, b) -} -func (m *TestIamPermissionsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_TestIamPermissionsResponse.Marshal(b, m, deterministic) -} -func (dst *TestIamPermissionsResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_TestIamPermissionsResponse.Merge(dst, src) -} -func (m *TestIamPermissionsResponse) XXX_Size() int { - return xxx_messageInfo_TestIamPermissionsResponse.Size(m) -} -func (m *TestIamPermissionsResponse) XXX_DiscardUnknown() { - xxx_messageInfo_TestIamPermissionsResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_TestIamPermissionsResponse proto.InternalMessageInfo - -func (m *TestIamPermissionsResponse) GetPermissions() []string { - if m != nil { - return m.Permissions - } - return nil -} - -func init() { - proto.RegisterType((*SetIamPolicyRequest)(nil), "google.iam.v1.SetIamPolicyRequest") - proto.RegisterType((*GetIamPolicyRequest)(nil), "google.iam.v1.GetIamPolicyRequest") - proto.RegisterType((*TestIamPermissionsRequest)(nil), "google.iam.v1.TestIamPermissionsRequest") - proto.RegisterType((*TestIamPermissionsResponse)(nil), "google.iam.v1.TestIamPermissionsResponse") -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// IAMPolicyClient is the client API for IAMPolicy service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type IAMPolicyClient interface { - // Sets the access control policy on the specified resource. Replaces any - // existing policy. - SetIamPolicy(ctx context.Context, in *SetIamPolicyRequest, opts ...grpc.CallOption) (*Policy, error) - // Gets the access control policy for a resource. - // Returns an empty policy if the resource exists and does not have a policy - // set. - GetIamPolicy(ctx context.Context, in *GetIamPolicyRequest, opts ...grpc.CallOption) (*Policy, error) - // Returns permissions that a caller has on the specified resource. - // If the resource does not exist, this will return an empty set of - // permissions, not a NOT_FOUND error. - TestIamPermissions(ctx context.Context, in *TestIamPermissionsRequest, opts ...grpc.CallOption) (*TestIamPermissionsResponse, error) -} - -type iAMPolicyClient struct { - cc *grpc.ClientConn -} - -func NewIAMPolicyClient(cc *grpc.ClientConn) IAMPolicyClient { - return &iAMPolicyClient{cc} -} - -func (c *iAMPolicyClient) SetIamPolicy(ctx context.Context, in *SetIamPolicyRequest, opts ...grpc.CallOption) (*Policy, error) { - out := new(Policy) - err := c.cc.Invoke(ctx, "/google.iam.v1.IAMPolicy/SetIamPolicy", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *iAMPolicyClient) GetIamPolicy(ctx context.Context, in *GetIamPolicyRequest, opts ...grpc.CallOption) (*Policy, error) { - out := new(Policy) - err := c.cc.Invoke(ctx, "/google.iam.v1.IAMPolicy/GetIamPolicy", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *iAMPolicyClient) TestIamPermissions(ctx context.Context, in *TestIamPermissionsRequest, opts ...grpc.CallOption) (*TestIamPermissionsResponse, error) { - out := new(TestIamPermissionsResponse) - err := c.cc.Invoke(ctx, "/google.iam.v1.IAMPolicy/TestIamPermissions", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for IAMPolicy service - -type IAMPolicyServer interface { - // Sets the access control policy on the specified resource. Replaces any - // existing policy. - SetIamPolicy(context.Context, *SetIamPolicyRequest) (*Policy, error) - // Gets the access control policy for a resource. - // Returns an empty policy if the resource exists and does not have a policy - // set. - GetIamPolicy(context.Context, *GetIamPolicyRequest) (*Policy, error) - // Returns permissions that a caller has on the specified resource. - // If the resource does not exist, this will return an empty set of - // permissions, not a NOT_FOUND error. - TestIamPermissions(context.Context, *TestIamPermissionsRequest) (*TestIamPermissionsResponse, error) -} - -func RegisterIAMPolicyServer(s *grpc.Server, srv IAMPolicyServer) { - s.RegisterService(&_IAMPolicy_serviceDesc, srv) -} - -func _IAMPolicy_SetIamPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SetIamPolicyRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(IAMPolicyServer).SetIamPolicy(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.iam.v1.IAMPolicy/SetIamPolicy", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(IAMPolicyServer).SetIamPolicy(ctx, req.(*SetIamPolicyRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _IAMPolicy_GetIamPolicy_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetIamPolicyRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(IAMPolicyServer).GetIamPolicy(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.iam.v1.IAMPolicy/GetIamPolicy", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(IAMPolicyServer).GetIamPolicy(ctx, req.(*GetIamPolicyRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _IAMPolicy_TestIamPermissions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(TestIamPermissionsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(IAMPolicyServer).TestIamPermissions(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.iam.v1.IAMPolicy/TestIamPermissions", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(IAMPolicyServer).TestIamPermissions(ctx, req.(*TestIamPermissionsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _IAMPolicy_serviceDesc = grpc.ServiceDesc{ - ServiceName: "google.iam.v1.IAMPolicy", - HandlerType: (*IAMPolicyServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "SetIamPolicy", - Handler: _IAMPolicy_SetIamPolicy_Handler, - }, - { - MethodName: "GetIamPolicy", - Handler: _IAMPolicy_GetIamPolicy_Handler, - }, - { - MethodName: "TestIamPermissions", - Handler: _IAMPolicy_TestIamPermissions_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "google/iam/v1/iam_policy.proto", -} - -func init() { - proto.RegisterFile("google/iam/v1/iam_policy.proto", fileDescriptor_iam_policy_511a6b0802f7199d) -} - -var fileDescriptor_iam_policy_511a6b0802f7199d = []byte{ - // 411 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4b, 0xcf, 0xcf, 0x4f, - 0xcf, 0x49, 0xd5, 0xcf, 0x4c, 0xcc, 0xd5, 0x2f, 0x33, 0x04, 0x51, 0xf1, 0x05, 0xf9, 0x39, 0x99, - 0xc9, 0x95, 0x7a, 0x05, 0x45, 0xf9, 0x25, 0xf9, 0x42, 0xbc, 0x10, 0x79, 0xbd, 0xcc, 0xc4, 0x5c, - 0xbd, 0x32, 0x43, 0x29, 0x19, 0xa8, 0xf2, 0xc4, 0x82, 0x4c, 0xfd, 0xc4, 0xbc, 0xbc, 0xfc, 0x92, - 0xc4, 0x92, 0xcc, 0xfc, 0xbc, 0x62, 0x88, 0x62, 0x29, 0x29, 0x54, 0xc3, 0x90, 0x0d, 0x52, 0x4a, - 0xe0, 0x12, 0x0e, 0x4e, 0x2d, 0xf1, 0x4c, 0xcc, 0x0d, 0x00, 0x8b, 0x06, 0xa5, 0x16, 0x96, 0xa6, - 0x16, 0x97, 0x08, 0x49, 0x71, 0x71, 0x14, 0xa5, 0x16, 0xe7, 0x97, 0x16, 0x25, 0xa7, 0x4a, 0x30, - 0x2a, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xf9, 0x42, 0xba, 0x5c, 0x6c, 0x10, 0x23, 0x24, 0x98, 0x14, - 0x18, 0x35, 0xb8, 0x8d, 0x44, 0xf5, 0x50, 0x1c, 0xa3, 0x07, 0x35, 0x09, 0xaa, 0x48, 0xc9, 0x90, - 0x4b, 0xd8, 0x9d, 0x34, 0x1b, 0x94, 0x22, 0xb9, 0x24, 0x43, 0x52, 0x8b, 0xc1, 0x7a, 0x52, 0x8b, - 0x72, 0x33, 0x8b, 0x8b, 0x41, 0x9e, 0x21, 0xc6, 0x69, 0x0a, 0x5c, 0xdc, 0x05, 0x08, 0x1d, 0x12, - 0x4c, 0x0a, 0xcc, 0x1a, 0x9c, 0x41, 0xc8, 0x42, 0x4a, 0x76, 0x5c, 0x52, 0xd8, 0x8c, 0x2e, 0x2e, - 0xc8, 0xcf, 0x2b, 0xc6, 0xd0, 0xcf, 0x88, 0xa1, 0xdf, 0x68, 0x0a, 0x33, 0x17, 0xa7, 0xa7, 0xa3, - 0x2f, 0xc4, 0x2f, 0x42, 0x25, 0x5c, 0x3c, 0xc8, 0xa1, 0x27, 0xa4, 0x84, 0x16, 0x14, 0x58, 0x82, - 0x56, 0x0a, 0x7b, 0x70, 0x29, 0x69, 0x36, 0x5d, 0x7e, 0x32, 0x99, 0x49, 0x59, 0x49, 0x0e, 0x14, - 0x45, 0xd5, 0x30, 0x1f, 0xd9, 0x6a, 0x69, 0xd5, 0x5a, 0x15, 0x23, 0x99, 0x62, 0xc5, 0xa8, 0x05, - 0xb2, 0xd5, 0x1d, 0x9f, 0xad, 0xee, 0x54, 0xb1, 0x35, 0x1d, 0xcd, 0xd6, 0x59, 0x8c, 0x5c, 0x42, - 0x98, 0x41, 0x27, 0xa4, 0x81, 0x66, 0x30, 0xce, 0x88, 0x93, 0xd2, 0x24, 0x42, 0x25, 0x24, 0x1e, - 0x94, 0xf4, 0xc1, 0xce, 0xd2, 0x54, 0x52, 0xc1, 0x74, 0x56, 0x09, 0x86, 0x2e, 0x2b, 0x46, 0x2d, - 0xa7, 0x36, 0x46, 0x2e, 0xc1, 0xe4, 0xfc, 0x5c, 0x54, 0x1b, 0x9c, 0xf8, 0xe0, 0x1e, 0x08, 0x00, - 0x25, 0xf6, 0x00, 0xc6, 0x28, 0x03, 0xa8, 0x82, 0xf4, 0xfc, 0x9c, 0xc4, 0xbc, 0x74, 0xbd, 0xfc, - 0xa2, 0x74, 0xfd, 0xf4, 0xd4, 0x3c, 0x70, 0x56, 0xd0, 0x87, 0x48, 0x25, 0x16, 0x64, 0x16, 0x43, - 0x73, 0x8a, 0x75, 0x66, 0x62, 0xee, 0x0f, 0x46, 0xc6, 0x55, 0x4c, 0xc2, 0xee, 0x10, 0x5d, 0xce, - 0x39, 0xf9, 0xa5, 0x29, 0x7a, 0x9e, 0x89, 0xb9, 0x7a, 0x61, 0x86, 0xa7, 0x60, 0xa2, 0x31, 0x60, - 0xd1, 0x18, 0xcf, 0xc4, 0xdc, 0x98, 0x30, 0xc3, 0x24, 0x36, 0xb0, 0x59, 0xc6, 0x80, 0x00, 0x00, - 0x00, 0xff, 0xff, 0xea, 0x62, 0x8f, 0x22, 0xc1, 0x03, 0x00, 0x00, -} diff --git a/vendor/google.golang.org/genproto/googleapis/iam/v1/policy.pb.go b/vendor/google.golang.org/genproto/googleapis/iam/v1/policy.pb.go deleted file mode 100644 index dd132dfc83a..00000000000 --- a/vendor/google.golang.org/genproto/googleapis/iam/v1/policy.pb.go +++ /dev/null @@ -1,366 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: google/iam/v1/policy.proto - -package iam // import "google.golang.org/genproto/googleapis/iam/v1" - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "google.golang.org/genproto/googleapis/api/annotations" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -// The type of action performed on a Binding in a policy. -type BindingDelta_Action int32 - -const ( - // Unspecified. - BindingDelta_ACTION_UNSPECIFIED BindingDelta_Action = 0 - // Addition of a Binding. - BindingDelta_ADD BindingDelta_Action = 1 - // Removal of a Binding. - BindingDelta_REMOVE BindingDelta_Action = 2 -) - -var BindingDelta_Action_name = map[int32]string{ - 0: "ACTION_UNSPECIFIED", - 1: "ADD", - 2: "REMOVE", -} -var BindingDelta_Action_value = map[string]int32{ - "ACTION_UNSPECIFIED": 0, - "ADD": 1, - "REMOVE": 2, -} - -func (x BindingDelta_Action) String() string { - return proto.EnumName(BindingDelta_Action_name, int32(x)) -} -func (BindingDelta_Action) EnumDescriptor() ([]byte, []int) { - return fileDescriptor_policy_d61b385c3386eaea, []int{3, 0} -} - -// Defines an Identity and Access Management (IAM) policy. It is used to -// specify access control policies for Cloud Platform resources. -// -// -// A `Policy` consists of a list of `bindings`. A `Binding` binds a list of -// `members` to a `role`, where the members can be user accounts, Google groups, -// Google domains, and service accounts. A `role` is a named list of permissions -// defined by IAM. -// -// **Example** -// -// { -// "bindings": [ -// { -// "role": "roles/owner", -// "members": [ -// "user:mike@example.com", -// "group:admins@example.com", -// "domain:google.com", -// "serviceAccount:my-other-app@appspot.gserviceaccount.com", -// ] -// }, -// { -// "role": "roles/viewer", -// "members": ["user:sean@example.com"] -// } -// ] -// } -// -// For a description of IAM and its features, see the -// [IAM developer's guide](https://cloud.google.com/iam). -type Policy struct { - // Version of the `Policy`. The default version is 0. - Version int32 `protobuf:"varint,1,opt,name=version" json:"version,omitempty"` - // Associates a list of `members` to a `role`. - // Multiple `bindings` must not be specified for the same `role`. - // `bindings` with no members will result in an error. - Bindings []*Binding `protobuf:"bytes,4,rep,name=bindings" json:"bindings,omitempty"` - // `etag` is used for optimistic concurrency control as a way to help - // prevent simultaneous updates of a policy from overwriting each other. - // It is strongly suggested that systems make use of the `etag` in the - // read-modify-write cycle to perform policy updates in order to avoid race - // conditions: An `etag` is returned in the response to `getIamPolicy`, and - // systems are expected to put that etag in the request to `setIamPolicy` to - // ensure that their change will be applied to the same version of the policy. - // - // If no `etag` is provided in the call to `setIamPolicy`, then the existing - // policy is overwritten blindly. - Etag []byte `protobuf:"bytes,3,opt,name=etag,proto3" json:"etag,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Policy) Reset() { *m = Policy{} } -func (m *Policy) String() string { return proto.CompactTextString(m) } -func (*Policy) ProtoMessage() {} -func (*Policy) Descriptor() ([]byte, []int) { - return fileDescriptor_policy_d61b385c3386eaea, []int{0} -} -func (m *Policy) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Policy.Unmarshal(m, b) -} -func (m *Policy) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Policy.Marshal(b, m, deterministic) -} -func (dst *Policy) XXX_Merge(src proto.Message) { - xxx_messageInfo_Policy.Merge(dst, src) -} -func (m *Policy) XXX_Size() int { - return xxx_messageInfo_Policy.Size(m) -} -func (m *Policy) XXX_DiscardUnknown() { - xxx_messageInfo_Policy.DiscardUnknown(m) -} - -var xxx_messageInfo_Policy proto.InternalMessageInfo - -func (m *Policy) GetVersion() int32 { - if m != nil { - return m.Version - } - return 0 -} - -func (m *Policy) GetBindings() []*Binding { - if m != nil { - return m.Bindings - } - return nil -} - -func (m *Policy) GetEtag() []byte { - if m != nil { - return m.Etag - } - return nil -} - -// Associates `members` with a `role`. -type Binding struct { - // Role that is assigned to `members`. - // For example, `roles/viewer`, `roles/editor`, or `roles/owner`. - // Required - Role string `protobuf:"bytes,1,opt,name=role" json:"role,omitempty"` - // Specifies the identities requesting access for a Cloud Platform resource. - // `members` can have the following values: - // - // * `allUsers`: A special identifier that represents anyone who is - // on the internet; with or without a Google account. - // - // * `allAuthenticatedUsers`: A special identifier that represents anyone - // who is authenticated with a Google account or a service account. - // - // * `user:{emailid}`: An email address that represents a specific Google - // account. For example, `alice@gmail.com` or `joe@example.com`. - // - // - // * `serviceAccount:{emailid}`: An email address that represents a service - // account. For example, `my-other-app@appspot.gserviceaccount.com`. - // - // * `group:{emailid}`: An email address that represents a Google group. - // For example, `admins@example.com`. - // - // * `domain:{domain}`: A Google Apps domain name that represents all the - // users of that domain. For example, `google.com` or `example.com`. - // - // - Members []string `protobuf:"bytes,2,rep,name=members" json:"members,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Binding) Reset() { *m = Binding{} } -func (m *Binding) String() string { return proto.CompactTextString(m) } -func (*Binding) ProtoMessage() {} -func (*Binding) Descriptor() ([]byte, []int) { - return fileDescriptor_policy_d61b385c3386eaea, []int{1} -} -func (m *Binding) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Binding.Unmarshal(m, b) -} -func (m *Binding) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Binding.Marshal(b, m, deterministic) -} -func (dst *Binding) XXX_Merge(src proto.Message) { - xxx_messageInfo_Binding.Merge(dst, src) -} -func (m *Binding) XXX_Size() int { - return xxx_messageInfo_Binding.Size(m) -} -func (m *Binding) XXX_DiscardUnknown() { - xxx_messageInfo_Binding.DiscardUnknown(m) -} - -var xxx_messageInfo_Binding proto.InternalMessageInfo - -func (m *Binding) GetRole() string { - if m != nil { - return m.Role - } - return "" -} - -func (m *Binding) GetMembers() []string { - if m != nil { - return m.Members - } - return nil -} - -// The difference delta between two policies. -type PolicyDelta struct { - // The delta for Bindings between two policies. - BindingDeltas []*BindingDelta `protobuf:"bytes,1,rep,name=binding_deltas,json=bindingDeltas" json:"binding_deltas,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PolicyDelta) Reset() { *m = PolicyDelta{} } -func (m *PolicyDelta) String() string { return proto.CompactTextString(m) } -func (*PolicyDelta) ProtoMessage() {} -func (*PolicyDelta) Descriptor() ([]byte, []int) { - return fileDescriptor_policy_d61b385c3386eaea, []int{2} -} -func (m *PolicyDelta) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PolicyDelta.Unmarshal(m, b) -} -func (m *PolicyDelta) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PolicyDelta.Marshal(b, m, deterministic) -} -func (dst *PolicyDelta) XXX_Merge(src proto.Message) { - xxx_messageInfo_PolicyDelta.Merge(dst, src) -} -func (m *PolicyDelta) XXX_Size() int { - return xxx_messageInfo_PolicyDelta.Size(m) -} -func (m *PolicyDelta) XXX_DiscardUnknown() { - xxx_messageInfo_PolicyDelta.DiscardUnknown(m) -} - -var xxx_messageInfo_PolicyDelta proto.InternalMessageInfo - -func (m *PolicyDelta) GetBindingDeltas() []*BindingDelta { - if m != nil { - return m.BindingDeltas - } - return nil -} - -// One delta entry for Binding. Each individual change (only one member in each -// entry) to a binding will be a separate entry. -type BindingDelta struct { - // The action that was performed on a Binding. - // Required - Action BindingDelta_Action `protobuf:"varint,1,opt,name=action,enum=google.iam.v1.BindingDelta_Action" json:"action,omitempty"` - // Role that is assigned to `members`. - // For example, `roles/viewer`, `roles/editor`, or `roles/owner`. - // Required - Role string `protobuf:"bytes,2,opt,name=role" json:"role,omitempty"` - // A single identity requesting access for a Cloud Platform resource. - // Follows the same format of Binding.members. - // Required - Member string `protobuf:"bytes,3,opt,name=member" json:"member,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *BindingDelta) Reset() { *m = BindingDelta{} } -func (m *BindingDelta) String() string { return proto.CompactTextString(m) } -func (*BindingDelta) ProtoMessage() {} -func (*BindingDelta) Descriptor() ([]byte, []int) { - return fileDescriptor_policy_d61b385c3386eaea, []int{3} -} -func (m *BindingDelta) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_BindingDelta.Unmarshal(m, b) -} -func (m *BindingDelta) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_BindingDelta.Marshal(b, m, deterministic) -} -func (dst *BindingDelta) XXX_Merge(src proto.Message) { - xxx_messageInfo_BindingDelta.Merge(dst, src) -} -func (m *BindingDelta) XXX_Size() int { - return xxx_messageInfo_BindingDelta.Size(m) -} -func (m *BindingDelta) XXX_DiscardUnknown() { - xxx_messageInfo_BindingDelta.DiscardUnknown(m) -} - -var xxx_messageInfo_BindingDelta proto.InternalMessageInfo - -func (m *BindingDelta) GetAction() BindingDelta_Action { - if m != nil { - return m.Action - } - return BindingDelta_ACTION_UNSPECIFIED -} - -func (m *BindingDelta) GetRole() string { - if m != nil { - return m.Role - } - return "" -} - -func (m *BindingDelta) GetMember() string { - if m != nil { - return m.Member - } - return "" -} - -func init() { - proto.RegisterType((*Policy)(nil), "google.iam.v1.Policy") - proto.RegisterType((*Binding)(nil), "google.iam.v1.Binding") - proto.RegisterType((*PolicyDelta)(nil), "google.iam.v1.PolicyDelta") - proto.RegisterType((*BindingDelta)(nil), "google.iam.v1.BindingDelta") - proto.RegisterEnum("google.iam.v1.BindingDelta_Action", BindingDelta_Action_name, BindingDelta_Action_value) -} - -func init() { proto.RegisterFile("google/iam/v1/policy.proto", fileDescriptor_policy_d61b385c3386eaea) } - -var fileDescriptor_policy_d61b385c3386eaea = []byte{ - // 403 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x7c, 0x52, 0x4d, 0xab, 0x13, 0x31, - 0x14, 0x35, 0xed, 0x73, 0x6a, 0xef, 0xfb, 0xa0, 0x46, 0x28, 0xc3, 0xd3, 0x45, 0x99, 0x55, 0x57, - 0x19, 0x5b, 0x11, 0x41, 0x57, 0xfd, 0x18, 0x65, 0x16, 0xbe, 0x37, 0x46, 0xed, 0x42, 0x0a, 0x8f, - 0x4c, 0x1b, 0x42, 0x64, 0x92, 0x0c, 0x33, 0x63, 0xc1, 0xb5, 0xff, 0x46, 0xf0, 0x8f, 0xf8, 0x8b, - 0x5c, 0xca, 0x24, 0x99, 0x47, 0x0b, 0xe2, 0x2e, 0xe7, 0x9e, 0x73, 0x72, 0xcf, 0xcd, 0x0d, 0x5c, - 0x0b, 0x63, 0x44, 0xc1, 0x63, 0xc9, 0x54, 0x7c, 0x98, 0xc5, 0xa5, 0x29, 0xe4, 0xee, 0x3b, 0x29, - 0x2b, 0xd3, 0x18, 0x7c, 0xe9, 0x38, 0x22, 0x99, 0x22, 0x87, 0xd9, 0xf5, 0x33, 0x2f, 0x65, 0xa5, - 0x8c, 0x99, 0xd6, 0xa6, 0x61, 0x8d, 0x34, 0xba, 0x76, 0xe2, 0xe8, 0x2b, 0x04, 0x99, 0x35, 0xe3, - 0x10, 0x06, 0x07, 0x5e, 0xd5, 0xd2, 0xe8, 0x10, 0x4d, 0xd0, 0xf4, 0x21, 0xed, 0x20, 0x9e, 0xc3, - 0xa3, 0x5c, 0xea, 0xbd, 0xd4, 0xa2, 0x0e, 0xcf, 0x26, 0xfd, 0xe9, 0xf9, 0x7c, 0x4c, 0x4e, 0x7a, - 0x90, 0xa5, 0xa3, 0xe9, 0xbd, 0x0e, 0x63, 0x38, 0xe3, 0x0d, 0x13, 0x61, 0x7f, 0x82, 0xa6, 0x17, - 0xd4, 0x9e, 0xa3, 0x57, 0x30, 0xf0, 0xc2, 0x96, 0xae, 0x4c, 0xc1, 0x6d, 0xa7, 0x21, 0xb5, 0xe7, - 0x36, 0x80, 0xe2, 0x2a, 0xe7, 0x55, 0x1d, 0xf6, 0x26, 0xfd, 0xe9, 0x90, 0x76, 0x30, 0xfa, 0x00, - 0xe7, 0x2e, 0xe4, 0x9a, 0x17, 0x0d, 0xc3, 0x4b, 0xb8, 0xf2, 0x7d, 0xee, 0xf6, 0x6d, 0xa1, 0x0e, - 0x91, 0x4d, 0xf5, 0xf4, 0xdf, 0xa9, 0xac, 0x89, 0x5e, 0xe6, 0x47, 0xa8, 0x8e, 0x7e, 0x21, 0xb8, - 0x38, 0xe6, 0xf1, 0x6b, 0x08, 0xd8, 0xae, 0xe9, 0xa6, 0xbf, 0x9a, 0x47, 0xff, 0xb9, 0x8c, 0x2c, - 0xac, 0x92, 0x7a, 0xc7, 0xfd, 0x34, 0xbd, 0xa3, 0x69, 0xc6, 0x10, 0xb8, 0xf8, 0xf6, 0x09, 0x86, - 0xd4, 0xa3, 0xe8, 0x25, 0x04, 0xce, 0x8d, 0xc7, 0x80, 0x17, 0xab, 0x4f, 0xe9, 0xed, 0xcd, 0xdd, - 0xe7, 0x9b, 0x8f, 0x59, 0xb2, 0x4a, 0xdf, 0xa6, 0xc9, 0x7a, 0xf4, 0x00, 0x0f, 0xa0, 0xbf, 0x58, - 0xaf, 0x47, 0x08, 0x03, 0x04, 0x34, 0x79, 0x7f, 0xbb, 0x49, 0x46, 0xbd, 0xe5, 0x0f, 0x04, 0x8f, - 0x77, 0x46, 0x9d, 0x86, 0x5a, 0xfa, 0x67, 0xc9, 0xda, 0x55, 0x66, 0xe8, 0xcb, 0x73, 0xcf, 0x0a, - 0x53, 0x30, 0x2d, 0x88, 0xa9, 0x44, 0x2c, 0xb8, 0xb6, 0x8b, 0x8e, 0x1d, 0xc5, 0x4a, 0x59, 0xfb, - 0x4f, 0xf3, 0x46, 0x32, 0xf5, 0x07, 0xa1, 0x9f, 0xbd, 0x27, 0xef, 0x9c, 0x6b, 0x55, 0x98, 0x6f, - 0x7b, 0x92, 0x32, 0x45, 0x36, 0xb3, 0xdf, 0x5d, 0x75, 0x6b, 0xab, 0xdb, 0x94, 0xa9, 0xed, 0x66, - 0x96, 0x07, 0xf6, 0xae, 0x17, 0x7f, 0x03, 0x00, 0x00, 0xff, 0xff, 0xfc, 0x18, 0xca, 0xaa, 0x7f, - 0x02, 0x00, 0x00, -} diff --git a/vendor/google.golang.org/genproto/googleapis/pubsub/v1/pubsub.pb.go b/vendor/google.golang.org/genproto/googleapis/pubsub/v1/pubsub.pb.go deleted file mode 100644 index 96690c7f417..00000000000 --- a/vendor/google.golang.org/genproto/googleapis/pubsub/v1/pubsub.pb.go +++ /dev/null @@ -1,3239 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: google/pubsub/v1/pubsub.proto - -package pubsub // import "google.golang.org/genproto/googleapis/pubsub/v1" - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" -import duration "github.com/golang/protobuf/ptypes/duration" -import empty "github.com/golang/protobuf/ptypes/empty" -import timestamp "github.com/golang/protobuf/ptypes/timestamp" -import _ "google.golang.org/genproto/googleapis/api/annotations" -import field_mask "google.golang.org/genproto/protobuf/field_mask" - -import ( - context "golang.org/x/net/context" - grpc "google.golang.org/grpc" -) - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -// A topic resource. -type Topic struct { - // The name of the topic. It must have the format - // `"projects/{project}/topics/{topic}"`. `{topic}` must start with a letter, - // and contain only letters (`[A-Za-z]`), numbers (`[0-9]`), dashes (`-`), - // underscores (`_`), periods (`.`), tildes (`~`), plus (`+`) or percent - // signs (`%`). It must be between 3 and 255 characters in length, and it - // must not start with `"goog"`. - Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - // User labels. - Labels map[string]string `protobuf:"bytes,2,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Topic) Reset() { *m = Topic{} } -func (m *Topic) String() string { return proto.CompactTextString(m) } -func (*Topic) ProtoMessage() {} -func (*Topic) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{0} -} -func (m *Topic) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Topic.Unmarshal(m, b) -} -func (m *Topic) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Topic.Marshal(b, m, deterministic) -} -func (dst *Topic) XXX_Merge(src proto.Message) { - xxx_messageInfo_Topic.Merge(dst, src) -} -func (m *Topic) XXX_Size() int { - return xxx_messageInfo_Topic.Size(m) -} -func (m *Topic) XXX_DiscardUnknown() { - xxx_messageInfo_Topic.DiscardUnknown(m) -} - -var xxx_messageInfo_Topic proto.InternalMessageInfo - -func (m *Topic) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *Topic) GetLabels() map[string]string { - if m != nil { - return m.Labels - } - return nil -} - -// A message data and its attributes. The message payload must not be empty; -// it must contain either a non-empty data field, or at least one attribute. -type PubsubMessage struct { - // The message payload. - Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` - // Optional attributes for this message. - Attributes map[string]string `protobuf:"bytes,2,rep,name=attributes" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - // ID of this message, assigned by the server when the message is published. - // Guaranteed to be unique within the topic. This value may be read by a - // subscriber that receives a `PubsubMessage` via a `Pull` call or a push - // delivery. It must not be populated by the publisher in a `Publish` call. - MessageId string `protobuf:"bytes,3,opt,name=message_id,json=messageId" json:"message_id,omitempty"` - // The time at which the message was published, populated by the server when - // it receives the `Publish` call. It must not be populated by the - // publisher in a `Publish` call. - PublishTime *timestamp.Timestamp `protobuf:"bytes,4,opt,name=publish_time,json=publishTime" json:"publish_time,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PubsubMessage) Reset() { *m = PubsubMessage{} } -func (m *PubsubMessage) String() string { return proto.CompactTextString(m) } -func (*PubsubMessage) ProtoMessage() {} -func (*PubsubMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{1} -} -func (m *PubsubMessage) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PubsubMessage.Unmarshal(m, b) -} -func (m *PubsubMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PubsubMessage.Marshal(b, m, deterministic) -} -func (dst *PubsubMessage) XXX_Merge(src proto.Message) { - xxx_messageInfo_PubsubMessage.Merge(dst, src) -} -func (m *PubsubMessage) XXX_Size() int { - return xxx_messageInfo_PubsubMessage.Size(m) -} -func (m *PubsubMessage) XXX_DiscardUnknown() { - xxx_messageInfo_PubsubMessage.DiscardUnknown(m) -} - -var xxx_messageInfo_PubsubMessage proto.InternalMessageInfo - -func (m *PubsubMessage) GetData() []byte { - if m != nil { - return m.Data - } - return nil -} - -func (m *PubsubMessage) GetAttributes() map[string]string { - if m != nil { - return m.Attributes - } - return nil -} - -func (m *PubsubMessage) GetMessageId() string { - if m != nil { - return m.MessageId - } - return "" -} - -func (m *PubsubMessage) GetPublishTime() *timestamp.Timestamp { - if m != nil { - return m.PublishTime - } - return nil -} - -// Request for the GetTopic method. -type GetTopicRequest struct { - // The name of the topic to get. - // Format is `projects/{project}/topics/{topic}`. - Topic string `protobuf:"bytes,1,opt,name=topic" json:"topic,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetTopicRequest) Reset() { *m = GetTopicRequest{} } -func (m *GetTopicRequest) String() string { return proto.CompactTextString(m) } -func (*GetTopicRequest) ProtoMessage() {} -func (*GetTopicRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{2} -} -func (m *GetTopicRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetTopicRequest.Unmarshal(m, b) -} -func (m *GetTopicRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetTopicRequest.Marshal(b, m, deterministic) -} -func (dst *GetTopicRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetTopicRequest.Merge(dst, src) -} -func (m *GetTopicRequest) XXX_Size() int { - return xxx_messageInfo_GetTopicRequest.Size(m) -} -func (m *GetTopicRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetTopicRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetTopicRequest proto.InternalMessageInfo - -func (m *GetTopicRequest) GetTopic() string { - if m != nil { - return m.Topic - } - return "" -} - -// Request for the UpdateTopic method. -type UpdateTopicRequest struct { - // The topic to update. - Topic *Topic `protobuf:"bytes,1,opt,name=topic" json:"topic,omitempty"` - // Indicates which fields in the provided topic to update. - // Must be specified and non-empty. - UpdateMask *field_mask.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask" json:"update_mask,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdateTopicRequest) Reset() { *m = UpdateTopicRequest{} } -func (m *UpdateTopicRequest) String() string { return proto.CompactTextString(m) } -func (*UpdateTopicRequest) ProtoMessage() {} -func (*UpdateTopicRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{3} -} -func (m *UpdateTopicRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpdateTopicRequest.Unmarshal(m, b) -} -func (m *UpdateTopicRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpdateTopicRequest.Marshal(b, m, deterministic) -} -func (dst *UpdateTopicRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdateTopicRequest.Merge(dst, src) -} -func (m *UpdateTopicRequest) XXX_Size() int { - return xxx_messageInfo_UpdateTopicRequest.Size(m) -} -func (m *UpdateTopicRequest) XXX_DiscardUnknown() { - xxx_messageInfo_UpdateTopicRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdateTopicRequest proto.InternalMessageInfo - -func (m *UpdateTopicRequest) GetTopic() *Topic { - if m != nil { - return m.Topic - } - return nil -} - -func (m *UpdateTopicRequest) GetUpdateMask() *field_mask.FieldMask { - if m != nil { - return m.UpdateMask - } - return nil -} - -// Request for the Publish method. -type PublishRequest struct { - // The messages in the request will be published on this topic. - // Format is `projects/{project}/topics/{topic}`. - Topic string `protobuf:"bytes,1,opt,name=topic" json:"topic,omitempty"` - // The messages to publish. - Messages []*PubsubMessage `protobuf:"bytes,2,rep,name=messages" json:"messages,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PublishRequest) Reset() { *m = PublishRequest{} } -func (m *PublishRequest) String() string { return proto.CompactTextString(m) } -func (*PublishRequest) ProtoMessage() {} -func (*PublishRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{4} -} -func (m *PublishRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PublishRequest.Unmarshal(m, b) -} -func (m *PublishRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PublishRequest.Marshal(b, m, deterministic) -} -func (dst *PublishRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_PublishRequest.Merge(dst, src) -} -func (m *PublishRequest) XXX_Size() int { - return xxx_messageInfo_PublishRequest.Size(m) -} -func (m *PublishRequest) XXX_DiscardUnknown() { - xxx_messageInfo_PublishRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_PublishRequest proto.InternalMessageInfo - -func (m *PublishRequest) GetTopic() string { - if m != nil { - return m.Topic - } - return "" -} - -func (m *PublishRequest) GetMessages() []*PubsubMessage { - if m != nil { - return m.Messages - } - return nil -} - -// Response for the `Publish` method. -type PublishResponse struct { - // The server-assigned ID of each published message, in the same order as - // the messages in the request. IDs are guaranteed to be unique within - // the topic. - MessageIds []string `protobuf:"bytes,1,rep,name=message_ids,json=messageIds" json:"message_ids,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PublishResponse) Reset() { *m = PublishResponse{} } -func (m *PublishResponse) String() string { return proto.CompactTextString(m) } -func (*PublishResponse) ProtoMessage() {} -func (*PublishResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{5} -} -func (m *PublishResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PublishResponse.Unmarshal(m, b) -} -func (m *PublishResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PublishResponse.Marshal(b, m, deterministic) -} -func (dst *PublishResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_PublishResponse.Merge(dst, src) -} -func (m *PublishResponse) XXX_Size() int { - return xxx_messageInfo_PublishResponse.Size(m) -} -func (m *PublishResponse) XXX_DiscardUnknown() { - xxx_messageInfo_PublishResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_PublishResponse proto.InternalMessageInfo - -func (m *PublishResponse) GetMessageIds() []string { - if m != nil { - return m.MessageIds - } - return nil -} - -// Request for the `ListTopics` method. -type ListTopicsRequest struct { - // The name of the cloud project that topics belong to. - // Format is `projects/{project}`. - Project string `protobuf:"bytes,1,opt,name=project" json:"project,omitempty"` - // Maximum number of topics to return. - PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize" json:"page_size,omitempty"` - // The value returned by the last `ListTopicsResponse`; indicates that this is - // a continuation of a prior `ListTopics` call, and that the system should - // return the next page of data. - PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken" json:"page_token,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ListTopicsRequest) Reset() { *m = ListTopicsRequest{} } -func (m *ListTopicsRequest) String() string { return proto.CompactTextString(m) } -func (*ListTopicsRequest) ProtoMessage() {} -func (*ListTopicsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{6} -} -func (m *ListTopicsRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ListTopicsRequest.Unmarshal(m, b) -} -func (m *ListTopicsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ListTopicsRequest.Marshal(b, m, deterministic) -} -func (dst *ListTopicsRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListTopicsRequest.Merge(dst, src) -} -func (m *ListTopicsRequest) XXX_Size() int { - return xxx_messageInfo_ListTopicsRequest.Size(m) -} -func (m *ListTopicsRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ListTopicsRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_ListTopicsRequest proto.InternalMessageInfo - -func (m *ListTopicsRequest) GetProject() string { - if m != nil { - return m.Project - } - return "" -} - -func (m *ListTopicsRequest) GetPageSize() int32 { - if m != nil { - return m.PageSize - } - return 0 -} - -func (m *ListTopicsRequest) GetPageToken() string { - if m != nil { - return m.PageToken - } - return "" -} - -// Response for the `ListTopics` method. -type ListTopicsResponse struct { - // The resulting topics. - Topics []*Topic `protobuf:"bytes,1,rep,name=topics" json:"topics,omitempty"` - // If not empty, indicates that there may be more topics that match the - // request; this value should be passed in a new `ListTopicsRequest`. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken" json:"next_page_token,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ListTopicsResponse) Reset() { *m = ListTopicsResponse{} } -func (m *ListTopicsResponse) String() string { return proto.CompactTextString(m) } -func (*ListTopicsResponse) ProtoMessage() {} -func (*ListTopicsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{7} -} -func (m *ListTopicsResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ListTopicsResponse.Unmarshal(m, b) -} -func (m *ListTopicsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ListTopicsResponse.Marshal(b, m, deterministic) -} -func (dst *ListTopicsResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListTopicsResponse.Merge(dst, src) -} -func (m *ListTopicsResponse) XXX_Size() int { - return xxx_messageInfo_ListTopicsResponse.Size(m) -} -func (m *ListTopicsResponse) XXX_DiscardUnknown() { - xxx_messageInfo_ListTopicsResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_ListTopicsResponse proto.InternalMessageInfo - -func (m *ListTopicsResponse) GetTopics() []*Topic { - if m != nil { - return m.Topics - } - return nil -} - -func (m *ListTopicsResponse) GetNextPageToken() string { - if m != nil { - return m.NextPageToken - } - return "" -} - -// Request for the `ListTopicSubscriptions` method. -type ListTopicSubscriptionsRequest struct { - // The name of the topic that subscriptions are attached to. - // Format is `projects/{project}/topics/{topic}`. - Topic string `protobuf:"bytes,1,opt,name=topic" json:"topic,omitempty"` - // Maximum number of subscription names to return. - PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize" json:"page_size,omitempty"` - // The value returned by the last `ListTopicSubscriptionsResponse`; indicates - // that this is a continuation of a prior `ListTopicSubscriptions` call, and - // that the system should return the next page of data. - PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken" json:"page_token,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ListTopicSubscriptionsRequest) Reset() { *m = ListTopicSubscriptionsRequest{} } -func (m *ListTopicSubscriptionsRequest) String() string { return proto.CompactTextString(m) } -func (*ListTopicSubscriptionsRequest) ProtoMessage() {} -func (*ListTopicSubscriptionsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{8} -} -func (m *ListTopicSubscriptionsRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ListTopicSubscriptionsRequest.Unmarshal(m, b) -} -func (m *ListTopicSubscriptionsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ListTopicSubscriptionsRequest.Marshal(b, m, deterministic) -} -func (dst *ListTopicSubscriptionsRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListTopicSubscriptionsRequest.Merge(dst, src) -} -func (m *ListTopicSubscriptionsRequest) XXX_Size() int { - return xxx_messageInfo_ListTopicSubscriptionsRequest.Size(m) -} -func (m *ListTopicSubscriptionsRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ListTopicSubscriptionsRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_ListTopicSubscriptionsRequest proto.InternalMessageInfo - -func (m *ListTopicSubscriptionsRequest) GetTopic() string { - if m != nil { - return m.Topic - } - return "" -} - -func (m *ListTopicSubscriptionsRequest) GetPageSize() int32 { - if m != nil { - return m.PageSize - } - return 0 -} - -func (m *ListTopicSubscriptionsRequest) GetPageToken() string { - if m != nil { - return m.PageToken - } - return "" -} - -// Response for the `ListTopicSubscriptions` method. -type ListTopicSubscriptionsResponse struct { - // The names of the subscriptions that match the request. - Subscriptions []string `protobuf:"bytes,1,rep,name=subscriptions" json:"subscriptions,omitempty"` - // If not empty, indicates that there may be more subscriptions that match - // the request; this value should be passed in a new - // `ListTopicSubscriptionsRequest` to get more subscriptions. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken" json:"next_page_token,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ListTopicSubscriptionsResponse) Reset() { *m = ListTopicSubscriptionsResponse{} } -func (m *ListTopicSubscriptionsResponse) String() string { return proto.CompactTextString(m) } -func (*ListTopicSubscriptionsResponse) ProtoMessage() {} -func (*ListTopicSubscriptionsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{9} -} -func (m *ListTopicSubscriptionsResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ListTopicSubscriptionsResponse.Unmarshal(m, b) -} -func (m *ListTopicSubscriptionsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ListTopicSubscriptionsResponse.Marshal(b, m, deterministic) -} -func (dst *ListTopicSubscriptionsResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListTopicSubscriptionsResponse.Merge(dst, src) -} -func (m *ListTopicSubscriptionsResponse) XXX_Size() int { - return xxx_messageInfo_ListTopicSubscriptionsResponse.Size(m) -} -func (m *ListTopicSubscriptionsResponse) XXX_DiscardUnknown() { - xxx_messageInfo_ListTopicSubscriptionsResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_ListTopicSubscriptionsResponse proto.InternalMessageInfo - -func (m *ListTopicSubscriptionsResponse) GetSubscriptions() []string { - if m != nil { - return m.Subscriptions - } - return nil -} - -func (m *ListTopicSubscriptionsResponse) GetNextPageToken() string { - if m != nil { - return m.NextPageToken - } - return "" -} - -// Request for the `DeleteTopic` method. -type DeleteTopicRequest struct { - // Name of the topic to delete. - // Format is `projects/{project}/topics/{topic}`. - Topic string `protobuf:"bytes,1,opt,name=topic" json:"topic,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DeleteTopicRequest) Reset() { *m = DeleteTopicRequest{} } -func (m *DeleteTopicRequest) String() string { return proto.CompactTextString(m) } -func (*DeleteTopicRequest) ProtoMessage() {} -func (*DeleteTopicRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{10} -} -func (m *DeleteTopicRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DeleteTopicRequest.Unmarshal(m, b) -} -func (m *DeleteTopicRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DeleteTopicRequest.Marshal(b, m, deterministic) -} -func (dst *DeleteTopicRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_DeleteTopicRequest.Merge(dst, src) -} -func (m *DeleteTopicRequest) XXX_Size() int { - return xxx_messageInfo_DeleteTopicRequest.Size(m) -} -func (m *DeleteTopicRequest) XXX_DiscardUnknown() { - xxx_messageInfo_DeleteTopicRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_DeleteTopicRequest proto.InternalMessageInfo - -func (m *DeleteTopicRequest) GetTopic() string { - if m != nil { - return m.Topic - } - return "" -} - -// A subscription resource. -type Subscription struct { - // The name of the subscription. It must have the format - // `"projects/{project}/subscriptions/{subscription}"`. `{subscription}` must - // start with a letter, and contain only letters (`[A-Za-z]`), numbers - // (`[0-9]`), dashes (`-`), underscores (`_`), periods (`.`), tildes (`~`), - // plus (`+`) or percent signs (`%`). It must be between 3 and 255 characters - // in length, and it must not start with `"goog"`. - Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - // The name of the topic from which this subscription is receiving messages. - // Format is `projects/{project}/topics/{topic}`. - // The value of this field will be `_deleted-topic_` if the topic has been - // deleted. - Topic string `protobuf:"bytes,2,opt,name=topic" json:"topic,omitempty"` - // If push delivery is used with this subscription, this field is - // used to configure it. An empty `pushConfig` signifies that the subscriber - // will pull and ack messages using API methods. - PushConfig *PushConfig `protobuf:"bytes,4,opt,name=push_config,json=pushConfig" json:"push_config,omitempty"` - // This value is the maximum time after a subscriber receives a message - // before the subscriber should acknowledge the message. After message - // delivery but before the ack deadline expires and before the message is - // acknowledged, it is an outstanding message and will not be delivered - // again during that time (on a best-effort basis). - // - // For pull subscriptions, this value is used as the initial value for the ack - // deadline. To override this value for a given message, call - // `ModifyAckDeadline` with the corresponding `ack_id` if using - // pull. - // The minimum custom deadline you can specify is 10 seconds. - // The maximum custom deadline you can specify is 600 seconds (10 minutes). - // If this parameter is 0, a default value of 10 seconds is used. - // - // For push delivery, this value is also used to set the request timeout for - // the call to the push endpoint. - // - // If the subscriber never acknowledges the message, the Pub/Sub - // system will eventually redeliver the message. - AckDeadlineSeconds int32 `protobuf:"varint,5,opt,name=ack_deadline_seconds,json=ackDeadlineSeconds" json:"ack_deadline_seconds,omitempty"` - // Indicates whether to retain acknowledged messages. If true, then - // messages are not expunged from the subscription's backlog, even if they are - // acknowledged, until they fall out of the `message_retention_duration` - // window. - RetainAckedMessages bool `protobuf:"varint,7,opt,name=retain_acked_messages,json=retainAckedMessages" json:"retain_acked_messages,omitempty"` - // How long to retain unacknowledged messages in the subscription's backlog, - // from the moment a message is published. - // If `retain_acked_messages` is true, then this also configures the retention - // of acknowledged messages, and thus configures how far back in time a `Seek` - // can be done. Defaults to 7 days. Cannot be more than 7 days or less than 10 - // minutes. - MessageRetentionDuration *duration.Duration `protobuf:"bytes,8,opt,name=message_retention_duration,json=messageRetentionDuration" json:"message_retention_duration,omitempty"` - // User labels. - Labels map[string]string `protobuf:"bytes,9,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Subscription) Reset() { *m = Subscription{} } -func (m *Subscription) String() string { return proto.CompactTextString(m) } -func (*Subscription) ProtoMessage() {} -func (*Subscription) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{11} -} -func (m *Subscription) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Subscription.Unmarshal(m, b) -} -func (m *Subscription) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Subscription.Marshal(b, m, deterministic) -} -func (dst *Subscription) XXX_Merge(src proto.Message) { - xxx_messageInfo_Subscription.Merge(dst, src) -} -func (m *Subscription) XXX_Size() int { - return xxx_messageInfo_Subscription.Size(m) -} -func (m *Subscription) XXX_DiscardUnknown() { - xxx_messageInfo_Subscription.DiscardUnknown(m) -} - -var xxx_messageInfo_Subscription proto.InternalMessageInfo - -func (m *Subscription) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *Subscription) GetTopic() string { - if m != nil { - return m.Topic - } - return "" -} - -func (m *Subscription) GetPushConfig() *PushConfig { - if m != nil { - return m.PushConfig - } - return nil -} - -func (m *Subscription) GetAckDeadlineSeconds() int32 { - if m != nil { - return m.AckDeadlineSeconds - } - return 0 -} - -func (m *Subscription) GetRetainAckedMessages() bool { - if m != nil { - return m.RetainAckedMessages - } - return false -} - -func (m *Subscription) GetMessageRetentionDuration() *duration.Duration { - if m != nil { - return m.MessageRetentionDuration - } - return nil -} - -func (m *Subscription) GetLabels() map[string]string { - if m != nil { - return m.Labels - } - return nil -} - -// Configuration for a push delivery endpoint. -type PushConfig struct { - // A URL locating the endpoint to which messages should be pushed. - // For example, a Webhook endpoint might use "https://example.com/push". - PushEndpoint string `protobuf:"bytes,1,opt,name=push_endpoint,json=pushEndpoint" json:"push_endpoint,omitempty"` - // Endpoint configuration attributes. - // - // Every endpoint has a set of API supported attributes that can be used to - // control different aspects of the message delivery. - // - // The currently supported attribute is `x-goog-version`, which you can - // use to change the format of the pushed message. This attribute - // indicates the version of the data expected by the endpoint. This - // controls the shape of the pushed message (i.e., its fields and metadata). - // The endpoint version is based on the version of the Pub/Sub API. - // - // If not present during the `CreateSubscription` call, it will default to - // the version of the API used to make such call. If not present during a - // `ModifyPushConfig` call, its value will not be changed. `GetSubscription` - // calls will always return a valid version, even if the subscription was - // created without this attribute. - // - // The possible values for this attribute are: - // - // * `v1beta1`: uses the push format defined in the v1beta1 Pub/Sub API. - // * `v1` or `v1beta2`: uses the push format defined in the v1 Pub/Sub API. - Attributes map[string]string `protobuf:"bytes,2,rep,name=attributes" json:"attributes,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PushConfig) Reset() { *m = PushConfig{} } -func (m *PushConfig) String() string { return proto.CompactTextString(m) } -func (*PushConfig) ProtoMessage() {} -func (*PushConfig) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{12} -} -func (m *PushConfig) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PushConfig.Unmarshal(m, b) -} -func (m *PushConfig) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PushConfig.Marshal(b, m, deterministic) -} -func (dst *PushConfig) XXX_Merge(src proto.Message) { - xxx_messageInfo_PushConfig.Merge(dst, src) -} -func (m *PushConfig) XXX_Size() int { - return xxx_messageInfo_PushConfig.Size(m) -} -func (m *PushConfig) XXX_DiscardUnknown() { - xxx_messageInfo_PushConfig.DiscardUnknown(m) -} - -var xxx_messageInfo_PushConfig proto.InternalMessageInfo - -func (m *PushConfig) GetPushEndpoint() string { - if m != nil { - return m.PushEndpoint - } - return "" -} - -func (m *PushConfig) GetAttributes() map[string]string { - if m != nil { - return m.Attributes - } - return nil -} - -// A message and its corresponding acknowledgment ID. -type ReceivedMessage struct { - // This ID can be used to acknowledge the received message. - AckId string `protobuf:"bytes,1,opt,name=ack_id,json=ackId" json:"ack_id,omitempty"` - // The message. - Message *PubsubMessage `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ReceivedMessage) Reset() { *m = ReceivedMessage{} } -func (m *ReceivedMessage) String() string { return proto.CompactTextString(m) } -func (*ReceivedMessage) ProtoMessage() {} -func (*ReceivedMessage) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{13} -} -func (m *ReceivedMessage) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ReceivedMessage.Unmarshal(m, b) -} -func (m *ReceivedMessage) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ReceivedMessage.Marshal(b, m, deterministic) -} -func (dst *ReceivedMessage) XXX_Merge(src proto.Message) { - xxx_messageInfo_ReceivedMessage.Merge(dst, src) -} -func (m *ReceivedMessage) XXX_Size() int { - return xxx_messageInfo_ReceivedMessage.Size(m) -} -func (m *ReceivedMessage) XXX_DiscardUnknown() { - xxx_messageInfo_ReceivedMessage.DiscardUnknown(m) -} - -var xxx_messageInfo_ReceivedMessage proto.InternalMessageInfo - -func (m *ReceivedMessage) GetAckId() string { - if m != nil { - return m.AckId - } - return "" -} - -func (m *ReceivedMessage) GetMessage() *PubsubMessage { - if m != nil { - return m.Message - } - return nil -} - -// Request for the GetSubscription method. -type GetSubscriptionRequest struct { - // The name of the subscription to get. - // Format is `projects/{project}/subscriptions/{sub}`. - Subscription string `protobuf:"bytes,1,opt,name=subscription" json:"subscription,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *GetSubscriptionRequest) Reset() { *m = GetSubscriptionRequest{} } -func (m *GetSubscriptionRequest) String() string { return proto.CompactTextString(m) } -func (*GetSubscriptionRequest) ProtoMessage() {} -func (*GetSubscriptionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{14} -} -func (m *GetSubscriptionRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_GetSubscriptionRequest.Unmarshal(m, b) -} -func (m *GetSubscriptionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_GetSubscriptionRequest.Marshal(b, m, deterministic) -} -func (dst *GetSubscriptionRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_GetSubscriptionRequest.Merge(dst, src) -} -func (m *GetSubscriptionRequest) XXX_Size() int { - return xxx_messageInfo_GetSubscriptionRequest.Size(m) -} -func (m *GetSubscriptionRequest) XXX_DiscardUnknown() { - xxx_messageInfo_GetSubscriptionRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_GetSubscriptionRequest proto.InternalMessageInfo - -func (m *GetSubscriptionRequest) GetSubscription() string { - if m != nil { - return m.Subscription - } - return "" -} - -// Request for the UpdateSubscription method. -type UpdateSubscriptionRequest struct { - // The updated subscription object. - Subscription *Subscription `protobuf:"bytes,1,opt,name=subscription" json:"subscription,omitempty"` - // Indicates which fields in the provided subscription to update. - // Must be specified and non-empty. - UpdateMask *field_mask.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask" json:"update_mask,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdateSubscriptionRequest) Reset() { *m = UpdateSubscriptionRequest{} } -func (m *UpdateSubscriptionRequest) String() string { return proto.CompactTextString(m) } -func (*UpdateSubscriptionRequest) ProtoMessage() {} -func (*UpdateSubscriptionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{15} -} -func (m *UpdateSubscriptionRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpdateSubscriptionRequest.Unmarshal(m, b) -} -func (m *UpdateSubscriptionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpdateSubscriptionRequest.Marshal(b, m, deterministic) -} -func (dst *UpdateSubscriptionRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdateSubscriptionRequest.Merge(dst, src) -} -func (m *UpdateSubscriptionRequest) XXX_Size() int { - return xxx_messageInfo_UpdateSubscriptionRequest.Size(m) -} -func (m *UpdateSubscriptionRequest) XXX_DiscardUnknown() { - xxx_messageInfo_UpdateSubscriptionRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdateSubscriptionRequest proto.InternalMessageInfo - -func (m *UpdateSubscriptionRequest) GetSubscription() *Subscription { - if m != nil { - return m.Subscription - } - return nil -} - -func (m *UpdateSubscriptionRequest) GetUpdateMask() *field_mask.FieldMask { - if m != nil { - return m.UpdateMask - } - return nil -} - -// Request for the `ListSubscriptions` method. -type ListSubscriptionsRequest struct { - // The name of the cloud project that subscriptions belong to. - // Format is `projects/{project}`. - Project string `protobuf:"bytes,1,opt,name=project" json:"project,omitempty"` - // Maximum number of subscriptions to return. - PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize" json:"page_size,omitempty"` - // The value returned by the last `ListSubscriptionsResponse`; indicates that - // this is a continuation of a prior `ListSubscriptions` call, and that the - // system should return the next page of data. - PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken" json:"page_token,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ListSubscriptionsRequest) Reset() { *m = ListSubscriptionsRequest{} } -func (m *ListSubscriptionsRequest) String() string { return proto.CompactTextString(m) } -func (*ListSubscriptionsRequest) ProtoMessage() {} -func (*ListSubscriptionsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{16} -} -func (m *ListSubscriptionsRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ListSubscriptionsRequest.Unmarshal(m, b) -} -func (m *ListSubscriptionsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ListSubscriptionsRequest.Marshal(b, m, deterministic) -} -func (dst *ListSubscriptionsRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListSubscriptionsRequest.Merge(dst, src) -} -func (m *ListSubscriptionsRequest) XXX_Size() int { - return xxx_messageInfo_ListSubscriptionsRequest.Size(m) -} -func (m *ListSubscriptionsRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ListSubscriptionsRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_ListSubscriptionsRequest proto.InternalMessageInfo - -func (m *ListSubscriptionsRequest) GetProject() string { - if m != nil { - return m.Project - } - return "" -} - -func (m *ListSubscriptionsRequest) GetPageSize() int32 { - if m != nil { - return m.PageSize - } - return 0 -} - -func (m *ListSubscriptionsRequest) GetPageToken() string { - if m != nil { - return m.PageToken - } - return "" -} - -// Response for the `ListSubscriptions` method. -type ListSubscriptionsResponse struct { - // The subscriptions that match the request. - Subscriptions []*Subscription `protobuf:"bytes,1,rep,name=subscriptions" json:"subscriptions,omitempty"` - // If not empty, indicates that there may be more subscriptions that match - // the request; this value should be passed in a new - // `ListSubscriptionsRequest` to get more subscriptions. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken" json:"next_page_token,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ListSubscriptionsResponse) Reset() { *m = ListSubscriptionsResponse{} } -func (m *ListSubscriptionsResponse) String() string { return proto.CompactTextString(m) } -func (*ListSubscriptionsResponse) ProtoMessage() {} -func (*ListSubscriptionsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{17} -} -func (m *ListSubscriptionsResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ListSubscriptionsResponse.Unmarshal(m, b) -} -func (m *ListSubscriptionsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ListSubscriptionsResponse.Marshal(b, m, deterministic) -} -func (dst *ListSubscriptionsResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListSubscriptionsResponse.Merge(dst, src) -} -func (m *ListSubscriptionsResponse) XXX_Size() int { - return xxx_messageInfo_ListSubscriptionsResponse.Size(m) -} -func (m *ListSubscriptionsResponse) XXX_DiscardUnknown() { - xxx_messageInfo_ListSubscriptionsResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_ListSubscriptionsResponse proto.InternalMessageInfo - -func (m *ListSubscriptionsResponse) GetSubscriptions() []*Subscription { - if m != nil { - return m.Subscriptions - } - return nil -} - -func (m *ListSubscriptionsResponse) GetNextPageToken() string { - if m != nil { - return m.NextPageToken - } - return "" -} - -// Request for the DeleteSubscription method. -type DeleteSubscriptionRequest struct { - // The subscription to delete. - // Format is `projects/{project}/subscriptions/{sub}`. - Subscription string `protobuf:"bytes,1,opt,name=subscription" json:"subscription,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DeleteSubscriptionRequest) Reset() { *m = DeleteSubscriptionRequest{} } -func (m *DeleteSubscriptionRequest) String() string { return proto.CompactTextString(m) } -func (*DeleteSubscriptionRequest) ProtoMessage() {} -func (*DeleteSubscriptionRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{18} -} -func (m *DeleteSubscriptionRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DeleteSubscriptionRequest.Unmarshal(m, b) -} -func (m *DeleteSubscriptionRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DeleteSubscriptionRequest.Marshal(b, m, deterministic) -} -func (dst *DeleteSubscriptionRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_DeleteSubscriptionRequest.Merge(dst, src) -} -func (m *DeleteSubscriptionRequest) XXX_Size() int { - return xxx_messageInfo_DeleteSubscriptionRequest.Size(m) -} -func (m *DeleteSubscriptionRequest) XXX_DiscardUnknown() { - xxx_messageInfo_DeleteSubscriptionRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_DeleteSubscriptionRequest proto.InternalMessageInfo - -func (m *DeleteSubscriptionRequest) GetSubscription() string { - if m != nil { - return m.Subscription - } - return "" -} - -// Request for the ModifyPushConfig method. -type ModifyPushConfigRequest struct { - // The name of the subscription. - // Format is `projects/{project}/subscriptions/{sub}`. - Subscription string `protobuf:"bytes,1,opt,name=subscription" json:"subscription,omitempty"` - // The push configuration for future deliveries. - // - // An empty `pushConfig` indicates that the Pub/Sub system should - // stop pushing messages from the given subscription and allow - // messages to be pulled and acknowledged - effectively pausing - // the subscription if `Pull` is not called. - PushConfig *PushConfig `protobuf:"bytes,2,opt,name=push_config,json=pushConfig" json:"push_config,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ModifyPushConfigRequest) Reset() { *m = ModifyPushConfigRequest{} } -func (m *ModifyPushConfigRequest) String() string { return proto.CompactTextString(m) } -func (*ModifyPushConfigRequest) ProtoMessage() {} -func (*ModifyPushConfigRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{19} -} -func (m *ModifyPushConfigRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ModifyPushConfigRequest.Unmarshal(m, b) -} -func (m *ModifyPushConfigRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ModifyPushConfigRequest.Marshal(b, m, deterministic) -} -func (dst *ModifyPushConfigRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ModifyPushConfigRequest.Merge(dst, src) -} -func (m *ModifyPushConfigRequest) XXX_Size() int { - return xxx_messageInfo_ModifyPushConfigRequest.Size(m) -} -func (m *ModifyPushConfigRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ModifyPushConfigRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_ModifyPushConfigRequest proto.InternalMessageInfo - -func (m *ModifyPushConfigRequest) GetSubscription() string { - if m != nil { - return m.Subscription - } - return "" -} - -func (m *ModifyPushConfigRequest) GetPushConfig() *PushConfig { - if m != nil { - return m.PushConfig - } - return nil -} - -// Request for the `Pull` method. -type PullRequest struct { - // The subscription from which messages should be pulled. - // Format is `projects/{project}/subscriptions/{sub}`. - Subscription string `protobuf:"bytes,1,opt,name=subscription" json:"subscription,omitempty"` - // If this field set to true, the system will respond immediately even if - // it there are no messages available to return in the `Pull` response. - // Otherwise, the system may wait (for a bounded amount of time) until at - // least one message is available, rather than returning no messages. The - // client may cancel the request if it does not wish to wait any longer for - // the response. - ReturnImmediately bool `protobuf:"varint,2,opt,name=return_immediately,json=returnImmediately" json:"return_immediately,omitempty"` - // The maximum number of messages returned for this request. The Pub/Sub - // system may return fewer than the number specified. - MaxMessages int32 `protobuf:"varint,3,opt,name=max_messages,json=maxMessages" json:"max_messages,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PullRequest) Reset() { *m = PullRequest{} } -func (m *PullRequest) String() string { return proto.CompactTextString(m) } -func (*PullRequest) ProtoMessage() {} -func (*PullRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{20} -} -func (m *PullRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PullRequest.Unmarshal(m, b) -} -func (m *PullRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PullRequest.Marshal(b, m, deterministic) -} -func (dst *PullRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_PullRequest.Merge(dst, src) -} -func (m *PullRequest) XXX_Size() int { - return xxx_messageInfo_PullRequest.Size(m) -} -func (m *PullRequest) XXX_DiscardUnknown() { - xxx_messageInfo_PullRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_PullRequest proto.InternalMessageInfo - -func (m *PullRequest) GetSubscription() string { - if m != nil { - return m.Subscription - } - return "" -} - -func (m *PullRequest) GetReturnImmediately() bool { - if m != nil { - return m.ReturnImmediately - } - return false -} - -func (m *PullRequest) GetMaxMessages() int32 { - if m != nil { - return m.MaxMessages - } - return 0 -} - -// Response for the `Pull` method. -type PullResponse struct { - // Received Pub/Sub messages. The Pub/Sub system will return zero messages if - // there are no more available in the backlog. The Pub/Sub system may return - // fewer than the `maxMessages` requested even if there are more messages - // available in the backlog. - ReceivedMessages []*ReceivedMessage `protobuf:"bytes,1,rep,name=received_messages,json=receivedMessages" json:"received_messages,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *PullResponse) Reset() { *m = PullResponse{} } -func (m *PullResponse) String() string { return proto.CompactTextString(m) } -func (*PullResponse) ProtoMessage() {} -func (*PullResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{21} -} -func (m *PullResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_PullResponse.Unmarshal(m, b) -} -func (m *PullResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_PullResponse.Marshal(b, m, deterministic) -} -func (dst *PullResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_PullResponse.Merge(dst, src) -} -func (m *PullResponse) XXX_Size() int { - return xxx_messageInfo_PullResponse.Size(m) -} -func (m *PullResponse) XXX_DiscardUnknown() { - xxx_messageInfo_PullResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_PullResponse proto.InternalMessageInfo - -func (m *PullResponse) GetReceivedMessages() []*ReceivedMessage { - if m != nil { - return m.ReceivedMessages - } - return nil -} - -// Request for the ModifyAckDeadline method. -type ModifyAckDeadlineRequest struct { - // The name of the subscription. - // Format is `projects/{project}/subscriptions/{sub}`. - Subscription string `protobuf:"bytes,1,opt,name=subscription" json:"subscription,omitempty"` - // List of acknowledgment IDs. - AckIds []string `protobuf:"bytes,4,rep,name=ack_ids,json=ackIds" json:"ack_ids,omitempty"` - // The new ack deadline with respect to the time this request was sent to - // the Pub/Sub system. For example, if the value is 10, the new - // ack deadline will expire 10 seconds after the `ModifyAckDeadline` call - // was made. Specifying zero may immediately make the message available for - // another pull request. - // The minimum deadline you can specify is 0 seconds. - // The maximum deadline you can specify is 600 seconds (10 minutes). - AckDeadlineSeconds int32 `protobuf:"varint,3,opt,name=ack_deadline_seconds,json=ackDeadlineSeconds" json:"ack_deadline_seconds,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ModifyAckDeadlineRequest) Reset() { *m = ModifyAckDeadlineRequest{} } -func (m *ModifyAckDeadlineRequest) String() string { return proto.CompactTextString(m) } -func (*ModifyAckDeadlineRequest) ProtoMessage() {} -func (*ModifyAckDeadlineRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{22} -} -func (m *ModifyAckDeadlineRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ModifyAckDeadlineRequest.Unmarshal(m, b) -} -func (m *ModifyAckDeadlineRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ModifyAckDeadlineRequest.Marshal(b, m, deterministic) -} -func (dst *ModifyAckDeadlineRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ModifyAckDeadlineRequest.Merge(dst, src) -} -func (m *ModifyAckDeadlineRequest) XXX_Size() int { - return xxx_messageInfo_ModifyAckDeadlineRequest.Size(m) -} -func (m *ModifyAckDeadlineRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ModifyAckDeadlineRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_ModifyAckDeadlineRequest proto.InternalMessageInfo - -func (m *ModifyAckDeadlineRequest) GetSubscription() string { - if m != nil { - return m.Subscription - } - return "" -} - -func (m *ModifyAckDeadlineRequest) GetAckIds() []string { - if m != nil { - return m.AckIds - } - return nil -} - -func (m *ModifyAckDeadlineRequest) GetAckDeadlineSeconds() int32 { - if m != nil { - return m.AckDeadlineSeconds - } - return 0 -} - -// Request for the Acknowledge method. -type AcknowledgeRequest struct { - // The subscription whose message is being acknowledged. - // Format is `projects/{project}/subscriptions/{sub}`. - Subscription string `protobuf:"bytes,1,opt,name=subscription" json:"subscription,omitempty"` - // The acknowledgment ID for the messages being acknowledged that was returned - // by the Pub/Sub system in the `Pull` response. Must not be empty. - AckIds []string `protobuf:"bytes,2,rep,name=ack_ids,json=ackIds" json:"ack_ids,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *AcknowledgeRequest) Reset() { *m = AcknowledgeRequest{} } -func (m *AcknowledgeRequest) String() string { return proto.CompactTextString(m) } -func (*AcknowledgeRequest) ProtoMessage() {} -func (*AcknowledgeRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{23} -} -func (m *AcknowledgeRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_AcknowledgeRequest.Unmarshal(m, b) -} -func (m *AcknowledgeRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_AcknowledgeRequest.Marshal(b, m, deterministic) -} -func (dst *AcknowledgeRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_AcknowledgeRequest.Merge(dst, src) -} -func (m *AcknowledgeRequest) XXX_Size() int { - return xxx_messageInfo_AcknowledgeRequest.Size(m) -} -func (m *AcknowledgeRequest) XXX_DiscardUnknown() { - xxx_messageInfo_AcknowledgeRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_AcknowledgeRequest proto.InternalMessageInfo - -func (m *AcknowledgeRequest) GetSubscription() string { - if m != nil { - return m.Subscription - } - return "" -} - -func (m *AcknowledgeRequest) GetAckIds() []string { - if m != nil { - return m.AckIds - } - return nil -} - -// Request for the `StreamingPull` streaming RPC method. This request is used to -// establish the initial stream as well as to stream acknowledgements and ack -// deadline modifications from the client to the server. -type StreamingPullRequest struct { - // The subscription for which to initialize the new stream. This must be - // provided in the first request on the stream, and must not be set in - // subsequent requests from client to server. - // Format is `projects/{project}/subscriptions/{sub}`. - Subscription string `protobuf:"bytes,1,opt,name=subscription" json:"subscription,omitempty"` - // List of acknowledgement IDs for acknowledging previously received messages - // (received on this stream or a different stream). If an ack ID has expired, - // the corresponding message may be redelivered later. Acknowledging a message - // more than once will not result in an error. If the acknowledgement ID is - // malformed, the stream will be aborted with status `INVALID_ARGUMENT`. - AckIds []string `protobuf:"bytes,2,rep,name=ack_ids,json=ackIds" json:"ack_ids,omitempty"` - // The list of new ack deadlines for the IDs listed in - // `modify_deadline_ack_ids`. The size of this list must be the same as the - // size of `modify_deadline_ack_ids`. If it differs the stream will be aborted - // with `INVALID_ARGUMENT`. Each element in this list is applied to the - // element in the same position in `modify_deadline_ack_ids`. The new ack - // deadline is with respect to the time this request was sent to the Pub/Sub - // system. Must be >= 0. For example, if the value is 10, the new ack deadline - // will expire 10 seconds after this request is received. If the value is 0, - // the message is immediately made available for another streaming or - // non-streaming pull request. If the value is < 0 (an error), the stream will - // be aborted with status `INVALID_ARGUMENT`. - ModifyDeadlineSeconds []int32 `protobuf:"varint,3,rep,packed,name=modify_deadline_seconds,json=modifyDeadlineSeconds" json:"modify_deadline_seconds,omitempty"` - // List of acknowledgement IDs whose deadline will be modified based on the - // corresponding element in `modify_deadline_seconds`. This field can be used - // to indicate that more time is needed to process a message by the - // subscriber, or to make the message available for redelivery if the - // processing was interrupted. - ModifyDeadlineAckIds []string `protobuf:"bytes,4,rep,name=modify_deadline_ack_ids,json=modifyDeadlineAckIds" json:"modify_deadline_ack_ids,omitempty"` - // The ack deadline to use for the stream. This must be provided in the - // first request on the stream, but it can also be updated on subsequent - // requests from client to server. The minimum deadline you can specify is 10 - // seconds. The maximum deadline you can specify is 600 seconds (10 minutes). - StreamAckDeadlineSeconds int32 `protobuf:"varint,5,opt,name=stream_ack_deadline_seconds,json=streamAckDeadlineSeconds" json:"stream_ack_deadline_seconds,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *StreamingPullRequest) Reset() { *m = StreamingPullRequest{} } -func (m *StreamingPullRequest) String() string { return proto.CompactTextString(m) } -func (*StreamingPullRequest) ProtoMessage() {} -func (*StreamingPullRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{24} -} -func (m *StreamingPullRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_StreamingPullRequest.Unmarshal(m, b) -} -func (m *StreamingPullRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_StreamingPullRequest.Marshal(b, m, deterministic) -} -func (dst *StreamingPullRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_StreamingPullRequest.Merge(dst, src) -} -func (m *StreamingPullRequest) XXX_Size() int { - return xxx_messageInfo_StreamingPullRequest.Size(m) -} -func (m *StreamingPullRequest) XXX_DiscardUnknown() { - xxx_messageInfo_StreamingPullRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_StreamingPullRequest proto.InternalMessageInfo - -func (m *StreamingPullRequest) GetSubscription() string { - if m != nil { - return m.Subscription - } - return "" -} - -func (m *StreamingPullRequest) GetAckIds() []string { - if m != nil { - return m.AckIds - } - return nil -} - -func (m *StreamingPullRequest) GetModifyDeadlineSeconds() []int32 { - if m != nil { - return m.ModifyDeadlineSeconds - } - return nil -} - -func (m *StreamingPullRequest) GetModifyDeadlineAckIds() []string { - if m != nil { - return m.ModifyDeadlineAckIds - } - return nil -} - -func (m *StreamingPullRequest) GetStreamAckDeadlineSeconds() int32 { - if m != nil { - return m.StreamAckDeadlineSeconds - } - return 0 -} - -// Response for the `StreamingPull` method. This response is used to stream -// messages from the server to the client. -type StreamingPullResponse struct { - // Received Pub/Sub messages. This will not be empty. - ReceivedMessages []*ReceivedMessage `protobuf:"bytes,1,rep,name=received_messages,json=receivedMessages" json:"received_messages,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *StreamingPullResponse) Reset() { *m = StreamingPullResponse{} } -func (m *StreamingPullResponse) String() string { return proto.CompactTextString(m) } -func (*StreamingPullResponse) ProtoMessage() {} -func (*StreamingPullResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{25} -} -func (m *StreamingPullResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_StreamingPullResponse.Unmarshal(m, b) -} -func (m *StreamingPullResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_StreamingPullResponse.Marshal(b, m, deterministic) -} -func (dst *StreamingPullResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_StreamingPullResponse.Merge(dst, src) -} -func (m *StreamingPullResponse) XXX_Size() int { - return xxx_messageInfo_StreamingPullResponse.Size(m) -} -func (m *StreamingPullResponse) XXX_DiscardUnknown() { - xxx_messageInfo_StreamingPullResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_StreamingPullResponse proto.InternalMessageInfo - -func (m *StreamingPullResponse) GetReceivedMessages() []*ReceivedMessage { - if m != nil { - return m.ReceivedMessages - } - return nil -} - -// Request for the `CreateSnapshot` method. -type CreateSnapshotRequest struct { - // Optional user-provided name for this snapshot. - // If the name is not provided in the request, the server will assign a random - // name for this snapshot on the same project as the subscription. - // Note that for REST API requests, you must specify a name. - // Format is `projects/{project}/snapshots/{snap}`. - Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - // The subscription whose backlog the snapshot retains. - // Specifically, the created snapshot is guaranteed to retain: - // (a) The existing backlog on the subscription. More precisely, this is - // defined as the messages in the subscription's backlog that are - // unacknowledged upon the successful completion of the - // `CreateSnapshot` request; as well as: - // (b) Any messages published to the subscription's topic following the - // successful completion of the CreateSnapshot request. - // Format is `projects/{project}/subscriptions/{sub}`. - Subscription string `protobuf:"bytes,2,opt,name=subscription" json:"subscription,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *CreateSnapshotRequest) Reset() { *m = CreateSnapshotRequest{} } -func (m *CreateSnapshotRequest) String() string { return proto.CompactTextString(m) } -func (*CreateSnapshotRequest) ProtoMessage() {} -func (*CreateSnapshotRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{26} -} -func (m *CreateSnapshotRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_CreateSnapshotRequest.Unmarshal(m, b) -} -func (m *CreateSnapshotRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_CreateSnapshotRequest.Marshal(b, m, deterministic) -} -func (dst *CreateSnapshotRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_CreateSnapshotRequest.Merge(dst, src) -} -func (m *CreateSnapshotRequest) XXX_Size() int { - return xxx_messageInfo_CreateSnapshotRequest.Size(m) -} -func (m *CreateSnapshotRequest) XXX_DiscardUnknown() { - xxx_messageInfo_CreateSnapshotRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_CreateSnapshotRequest proto.InternalMessageInfo - -func (m *CreateSnapshotRequest) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *CreateSnapshotRequest) GetSubscription() string { - if m != nil { - return m.Subscription - } - return "" -} - -// Request for the UpdateSnapshot method. -type UpdateSnapshotRequest struct { - // The updated snpashot object. - Snapshot *Snapshot `protobuf:"bytes,1,opt,name=snapshot" json:"snapshot,omitempty"` - // Indicates which fields in the provided snapshot to update. - // Must be specified and non-empty. - UpdateMask *field_mask.FieldMask `protobuf:"bytes,2,opt,name=update_mask,json=updateMask" json:"update_mask,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *UpdateSnapshotRequest) Reset() { *m = UpdateSnapshotRequest{} } -func (m *UpdateSnapshotRequest) String() string { return proto.CompactTextString(m) } -func (*UpdateSnapshotRequest) ProtoMessage() {} -func (*UpdateSnapshotRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{27} -} -func (m *UpdateSnapshotRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_UpdateSnapshotRequest.Unmarshal(m, b) -} -func (m *UpdateSnapshotRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_UpdateSnapshotRequest.Marshal(b, m, deterministic) -} -func (dst *UpdateSnapshotRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_UpdateSnapshotRequest.Merge(dst, src) -} -func (m *UpdateSnapshotRequest) XXX_Size() int { - return xxx_messageInfo_UpdateSnapshotRequest.Size(m) -} -func (m *UpdateSnapshotRequest) XXX_DiscardUnknown() { - xxx_messageInfo_UpdateSnapshotRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_UpdateSnapshotRequest proto.InternalMessageInfo - -func (m *UpdateSnapshotRequest) GetSnapshot() *Snapshot { - if m != nil { - return m.Snapshot - } - return nil -} - -func (m *UpdateSnapshotRequest) GetUpdateMask() *field_mask.FieldMask { - if m != nil { - return m.UpdateMask - } - return nil -} - -// A snapshot resource. -type Snapshot struct { - // The name of the snapshot. - Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - // The name of the topic from which this snapshot is retaining messages. - Topic string `protobuf:"bytes,2,opt,name=topic" json:"topic,omitempty"` - // The snapshot is guaranteed to exist up until this time. - // A newly-created snapshot expires no later than 7 days from the time of its - // creation. Its exact lifetime is determined at creation by the existing - // backlog in the source subscription. Specifically, the lifetime of the - // snapshot is `7 days - (age of oldest unacked message in the subscription)`. - // For example, consider a subscription whose oldest unacked message is 3 days - // old. If a snapshot is created from this subscription, the snapshot -- which - // will always capture this 3-day-old backlog as long as the snapshot - // exists -- will expire in 4 days. - ExpireTime *timestamp.Timestamp `protobuf:"bytes,3,opt,name=expire_time,json=expireTime" json:"expire_time,omitempty"` - // User labels. - Labels map[string]string `protobuf:"bytes,4,rep,name=labels" json:"labels,omitempty" protobuf_key:"bytes,1,opt,name=key" protobuf_val:"bytes,2,opt,name=value"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Snapshot) Reset() { *m = Snapshot{} } -func (m *Snapshot) String() string { return proto.CompactTextString(m) } -func (*Snapshot) ProtoMessage() {} -func (*Snapshot) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{28} -} -func (m *Snapshot) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Snapshot.Unmarshal(m, b) -} -func (m *Snapshot) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Snapshot.Marshal(b, m, deterministic) -} -func (dst *Snapshot) XXX_Merge(src proto.Message) { - xxx_messageInfo_Snapshot.Merge(dst, src) -} -func (m *Snapshot) XXX_Size() int { - return xxx_messageInfo_Snapshot.Size(m) -} -func (m *Snapshot) XXX_DiscardUnknown() { - xxx_messageInfo_Snapshot.DiscardUnknown(m) -} - -var xxx_messageInfo_Snapshot proto.InternalMessageInfo - -func (m *Snapshot) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -func (m *Snapshot) GetTopic() string { - if m != nil { - return m.Topic - } - return "" -} - -func (m *Snapshot) GetExpireTime() *timestamp.Timestamp { - if m != nil { - return m.ExpireTime - } - return nil -} - -func (m *Snapshot) GetLabels() map[string]string { - if m != nil { - return m.Labels - } - return nil -} - -// Request for the `ListSnapshots` method. -type ListSnapshotsRequest struct { - // The name of the cloud project that snapshots belong to. - // Format is `projects/{project}`. - Project string `protobuf:"bytes,1,opt,name=project" json:"project,omitempty"` - // Maximum number of snapshots to return. - PageSize int32 `protobuf:"varint,2,opt,name=page_size,json=pageSize" json:"page_size,omitempty"` - // The value returned by the last `ListSnapshotsResponse`; indicates that this - // is a continuation of a prior `ListSnapshots` call, and that the system - // should return the next page of data. - PageToken string `protobuf:"bytes,3,opt,name=page_token,json=pageToken" json:"page_token,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ListSnapshotsRequest) Reset() { *m = ListSnapshotsRequest{} } -func (m *ListSnapshotsRequest) String() string { return proto.CompactTextString(m) } -func (*ListSnapshotsRequest) ProtoMessage() {} -func (*ListSnapshotsRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{29} -} -func (m *ListSnapshotsRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ListSnapshotsRequest.Unmarshal(m, b) -} -func (m *ListSnapshotsRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ListSnapshotsRequest.Marshal(b, m, deterministic) -} -func (dst *ListSnapshotsRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListSnapshotsRequest.Merge(dst, src) -} -func (m *ListSnapshotsRequest) XXX_Size() int { - return xxx_messageInfo_ListSnapshotsRequest.Size(m) -} -func (m *ListSnapshotsRequest) XXX_DiscardUnknown() { - xxx_messageInfo_ListSnapshotsRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_ListSnapshotsRequest proto.InternalMessageInfo - -func (m *ListSnapshotsRequest) GetProject() string { - if m != nil { - return m.Project - } - return "" -} - -func (m *ListSnapshotsRequest) GetPageSize() int32 { - if m != nil { - return m.PageSize - } - return 0 -} - -func (m *ListSnapshotsRequest) GetPageToken() string { - if m != nil { - return m.PageToken - } - return "" -} - -// Response for the `ListSnapshots` method. -type ListSnapshotsResponse struct { - // The resulting snapshots. - Snapshots []*Snapshot `protobuf:"bytes,1,rep,name=snapshots" json:"snapshots,omitempty"` - // If not empty, indicates that there may be more snapshot that match the - // request; this value should be passed in a new `ListSnapshotsRequest`. - NextPageToken string `protobuf:"bytes,2,opt,name=next_page_token,json=nextPageToken" json:"next_page_token,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ListSnapshotsResponse) Reset() { *m = ListSnapshotsResponse{} } -func (m *ListSnapshotsResponse) String() string { return proto.CompactTextString(m) } -func (*ListSnapshotsResponse) ProtoMessage() {} -func (*ListSnapshotsResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{30} -} -func (m *ListSnapshotsResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ListSnapshotsResponse.Unmarshal(m, b) -} -func (m *ListSnapshotsResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ListSnapshotsResponse.Marshal(b, m, deterministic) -} -func (dst *ListSnapshotsResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_ListSnapshotsResponse.Merge(dst, src) -} -func (m *ListSnapshotsResponse) XXX_Size() int { - return xxx_messageInfo_ListSnapshotsResponse.Size(m) -} -func (m *ListSnapshotsResponse) XXX_DiscardUnknown() { - xxx_messageInfo_ListSnapshotsResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_ListSnapshotsResponse proto.InternalMessageInfo - -func (m *ListSnapshotsResponse) GetSnapshots() []*Snapshot { - if m != nil { - return m.Snapshots - } - return nil -} - -func (m *ListSnapshotsResponse) GetNextPageToken() string { - if m != nil { - return m.NextPageToken - } - return "" -} - -// Request for the `DeleteSnapshot` method. -type DeleteSnapshotRequest struct { - // The name of the snapshot to delete. - // Format is `projects/{project}/snapshots/{snap}`. - Snapshot string `protobuf:"bytes,1,opt,name=snapshot" json:"snapshot,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *DeleteSnapshotRequest) Reset() { *m = DeleteSnapshotRequest{} } -func (m *DeleteSnapshotRequest) String() string { return proto.CompactTextString(m) } -func (*DeleteSnapshotRequest) ProtoMessage() {} -func (*DeleteSnapshotRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{31} -} -func (m *DeleteSnapshotRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_DeleteSnapshotRequest.Unmarshal(m, b) -} -func (m *DeleteSnapshotRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_DeleteSnapshotRequest.Marshal(b, m, deterministic) -} -func (dst *DeleteSnapshotRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_DeleteSnapshotRequest.Merge(dst, src) -} -func (m *DeleteSnapshotRequest) XXX_Size() int { - return xxx_messageInfo_DeleteSnapshotRequest.Size(m) -} -func (m *DeleteSnapshotRequest) XXX_DiscardUnknown() { - xxx_messageInfo_DeleteSnapshotRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_DeleteSnapshotRequest proto.InternalMessageInfo - -func (m *DeleteSnapshotRequest) GetSnapshot() string { - if m != nil { - return m.Snapshot - } - return "" -} - -// Request for the `Seek` method. -type SeekRequest struct { - // The subscription to affect. - Subscription string `protobuf:"bytes,1,opt,name=subscription" json:"subscription,omitempty"` - // Types that are valid to be assigned to Target: - // *SeekRequest_Time - // *SeekRequest_Snapshot - Target isSeekRequest_Target `protobuf_oneof:"target"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *SeekRequest) Reset() { *m = SeekRequest{} } -func (m *SeekRequest) String() string { return proto.CompactTextString(m) } -func (*SeekRequest) ProtoMessage() {} -func (*SeekRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{32} -} -func (m *SeekRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SeekRequest.Unmarshal(m, b) -} -func (m *SeekRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SeekRequest.Marshal(b, m, deterministic) -} -func (dst *SeekRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_SeekRequest.Merge(dst, src) -} -func (m *SeekRequest) XXX_Size() int { - return xxx_messageInfo_SeekRequest.Size(m) -} -func (m *SeekRequest) XXX_DiscardUnknown() { - xxx_messageInfo_SeekRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_SeekRequest proto.InternalMessageInfo - -type isSeekRequest_Target interface { - isSeekRequest_Target() -} - -type SeekRequest_Time struct { - Time *timestamp.Timestamp `protobuf:"bytes,2,opt,name=time,oneof"` -} -type SeekRequest_Snapshot struct { - Snapshot string `protobuf:"bytes,3,opt,name=snapshot,oneof"` -} - -func (*SeekRequest_Time) isSeekRequest_Target() {} -func (*SeekRequest_Snapshot) isSeekRequest_Target() {} - -func (m *SeekRequest) GetTarget() isSeekRequest_Target { - if m != nil { - return m.Target - } - return nil -} - -func (m *SeekRequest) GetSubscription() string { - if m != nil { - return m.Subscription - } - return "" -} - -func (m *SeekRequest) GetTime() *timestamp.Timestamp { - if x, ok := m.GetTarget().(*SeekRequest_Time); ok { - return x.Time - } - return nil -} - -func (m *SeekRequest) GetSnapshot() string { - if x, ok := m.GetTarget().(*SeekRequest_Snapshot); ok { - return x.Snapshot - } - return "" -} - -// XXX_OneofFuncs is for the internal use of the proto package. -func (*SeekRequest) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _SeekRequest_OneofMarshaler, _SeekRequest_OneofUnmarshaler, _SeekRequest_OneofSizer, []interface{}{ - (*SeekRequest_Time)(nil), - (*SeekRequest_Snapshot)(nil), - } -} - -func _SeekRequest_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*SeekRequest) - // target - switch x := m.Target.(type) { - case *SeekRequest_Time: - b.EncodeVarint(2<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.Time); err != nil { - return err - } - case *SeekRequest_Snapshot: - b.EncodeVarint(3<<3 | proto.WireBytes) - b.EncodeStringBytes(x.Snapshot) - case nil: - default: - return fmt.Errorf("SeekRequest.Target has unexpected type %T", x) - } - return nil -} - -func _SeekRequest_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*SeekRequest) - switch tag { - case 2: // target.time - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(timestamp.Timestamp) - err := b.DecodeMessage(msg) - m.Target = &SeekRequest_Time{msg} - return true, err - case 3: // target.snapshot - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - x, err := b.DecodeStringBytes() - m.Target = &SeekRequest_Snapshot{x} - return true, err - default: - return false, nil - } -} - -func _SeekRequest_OneofSizer(msg proto.Message) (n int) { - m := msg.(*SeekRequest) - // target - switch x := m.Target.(type) { - case *SeekRequest_Time: - s := proto.Size(x.Time) - n += 1 // tag and wire - n += proto.SizeVarint(uint64(s)) - n += s - case *SeekRequest_Snapshot: - n += 1 // tag and wire - n += proto.SizeVarint(uint64(len(x.Snapshot))) - n += len(x.Snapshot) - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - -type SeekResponse struct { - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *SeekResponse) Reset() { *m = SeekResponse{} } -func (m *SeekResponse) String() string { return proto.CompactTextString(m) } -func (*SeekResponse) ProtoMessage() {} -func (*SeekResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_pubsub_2c3603090b805ba2, []int{33} -} -func (m *SeekResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_SeekResponse.Unmarshal(m, b) -} -func (m *SeekResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_SeekResponse.Marshal(b, m, deterministic) -} -func (dst *SeekResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_SeekResponse.Merge(dst, src) -} -func (m *SeekResponse) XXX_Size() int { - return xxx_messageInfo_SeekResponse.Size(m) -} -func (m *SeekResponse) XXX_DiscardUnknown() { - xxx_messageInfo_SeekResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_SeekResponse proto.InternalMessageInfo - -func init() { - proto.RegisterType((*Topic)(nil), "google.pubsub.v1.Topic") - proto.RegisterMapType((map[string]string)(nil), "google.pubsub.v1.Topic.LabelsEntry") - proto.RegisterType((*PubsubMessage)(nil), "google.pubsub.v1.PubsubMessage") - proto.RegisterMapType((map[string]string)(nil), "google.pubsub.v1.PubsubMessage.AttributesEntry") - proto.RegisterType((*GetTopicRequest)(nil), "google.pubsub.v1.GetTopicRequest") - proto.RegisterType((*UpdateTopicRequest)(nil), "google.pubsub.v1.UpdateTopicRequest") - proto.RegisterType((*PublishRequest)(nil), "google.pubsub.v1.PublishRequest") - proto.RegisterType((*PublishResponse)(nil), "google.pubsub.v1.PublishResponse") - proto.RegisterType((*ListTopicsRequest)(nil), "google.pubsub.v1.ListTopicsRequest") - proto.RegisterType((*ListTopicsResponse)(nil), "google.pubsub.v1.ListTopicsResponse") - proto.RegisterType((*ListTopicSubscriptionsRequest)(nil), "google.pubsub.v1.ListTopicSubscriptionsRequest") - proto.RegisterType((*ListTopicSubscriptionsResponse)(nil), "google.pubsub.v1.ListTopicSubscriptionsResponse") - proto.RegisterType((*DeleteTopicRequest)(nil), "google.pubsub.v1.DeleteTopicRequest") - proto.RegisterType((*Subscription)(nil), "google.pubsub.v1.Subscription") - proto.RegisterMapType((map[string]string)(nil), "google.pubsub.v1.Subscription.LabelsEntry") - proto.RegisterType((*PushConfig)(nil), "google.pubsub.v1.PushConfig") - proto.RegisterMapType((map[string]string)(nil), "google.pubsub.v1.PushConfig.AttributesEntry") - proto.RegisterType((*ReceivedMessage)(nil), "google.pubsub.v1.ReceivedMessage") - proto.RegisterType((*GetSubscriptionRequest)(nil), "google.pubsub.v1.GetSubscriptionRequest") - proto.RegisterType((*UpdateSubscriptionRequest)(nil), "google.pubsub.v1.UpdateSubscriptionRequest") - proto.RegisterType((*ListSubscriptionsRequest)(nil), "google.pubsub.v1.ListSubscriptionsRequest") - proto.RegisterType((*ListSubscriptionsResponse)(nil), "google.pubsub.v1.ListSubscriptionsResponse") - proto.RegisterType((*DeleteSubscriptionRequest)(nil), "google.pubsub.v1.DeleteSubscriptionRequest") - proto.RegisterType((*ModifyPushConfigRequest)(nil), "google.pubsub.v1.ModifyPushConfigRequest") - proto.RegisterType((*PullRequest)(nil), "google.pubsub.v1.PullRequest") - proto.RegisterType((*PullResponse)(nil), "google.pubsub.v1.PullResponse") - proto.RegisterType((*ModifyAckDeadlineRequest)(nil), "google.pubsub.v1.ModifyAckDeadlineRequest") - proto.RegisterType((*AcknowledgeRequest)(nil), "google.pubsub.v1.AcknowledgeRequest") - proto.RegisterType((*StreamingPullRequest)(nil), "google.pubsub.v1.StreamingPullRequest") - proto.RegisterType((*StreamingPullResponse)(nil), "google.pubsub.v1.StreamingPullResponse") - proto.RegisterType((*CreateSnapshotRequest)(nil), "google.pubsub.v1.CreateSnapshotRequest") - proto.RegisterType((*UpdateSnapshotRequest)(nil), "google.pubsub.v1.UpdateSnapshotRequest") - proto.RegisterType((*Snapshot)(nil), "google.pubsub.v1.Snapshot") - proto.RegisterMapType((map[string]string)(nil), "google.pubsub.v1.Snapshot.LabelsEntry") - proto.RegisterType((*ListSnapshotsRequest)(nil), "google.pubsub.v1.ListSnapshotsRequest") - proto.RegisterType((*ListSnapshotsResponse)(nil), "google.pubsub.v1.ListSnapshotsResponse") - proto.RegisterType((*DeleteSnapshotRequest)(nil), "google.pubsub.v1.DeleteSnapshotRequest") - proto.RegisterType((*SeekRequest)(nil), "google.pubsub.v1.SeekRequest") - proto.RegisterType((*SeekResponse)(nil), "google.pubsub.v1.SeekResponse") -} - -// Reference imports to suppress errors if they are not otherwise used. -var _ context.Context -var _ grpc.ClientConn - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the grpc package it is being compiled against. -const _ = grpc.SupportPackageIsVersion4 - -// SubscriberClient is the client API for Subscriber service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type SubscriberClient interface { - // Creates a subscription to a given topic. - // If the subscription already exists, returns `ALREADY_EXISTS`. - // If the corresponding topic doesn't exist, returns `NOT_FOUND`. - // - // If the name is not provided in the request, the server will assign a random - // name for this subscription on the same project as the topic, conforming - // to the - // [resource name format](https://cloud.google.com/pubsub/docs/overview#names). - // The generated name is populated in the returned Subscription object. - // Note that for REST API requests, you must specify a name in the request. - CreateSubscription(ctx context.Context, in *Subscription, opts ...grpc.CallOption) (*Subscription, error) - // Gets the configuration details of a subscription. - GetSubscription(ctx context.Context, in *GetSubscriptionRequest, opts ...grpc.CallOption) (*Subscription, error) - // Updates an existing subscription. Note that certain properties of a - // subscription, such as its topic, are not modifiable. - // NOTE: The style guide requires body: "subscription" instead of body: "*". - // Keeping the latter for internal consistency in V1, however it should be - // corrected in V2. See - // https://cloud.google.com/apis/design/standard_methods#update for details. - UpdateSubscription(ctx context.Context, in *UpdateSubscriptionRequest, opts ...grpc.CallOption) (*Subscription, error) - // Lists matching subscriptions. - ListSubscriptions(ctx context.Context, in *ListSubscriptionsRequest, opts ...grpc.CallOption) (*ListSubscriptionsResponse, error) - // Deletes an existing subscription. All messages retained in the subscription - // are immediately dropped. Calls to `Pull` after deletion will return - // `NOT_FOUND`. After a subscription is deleted, a new one may be created with - // the same name, but the new one has no association with the old - // subscription or its topic unless the same topic is specified. - DeleteSubscription(ctx context.Context, in *DeleteSubscriptionRequest, opts ...grpc.CallOption) (*empty.Empty, error) - // Modifies the ack deadline for a specific message. This method is useful - // to indicate that more time is needed to process a message by the - // subscriber, or to make the message available for redelivery if the - // processing was interrupted. Note that this does not modify the - // subscription-level `ackDeadlineSeconds` used for subsequent messages. - ModifyAckDeadline(ctx context.Context, in *ModifyAckDeadlineRequest, opts ...grpc.CallOption) (*empty.Empty, error) - // Acknowledges the messages associated with the `ack_ids` in the - // `AcknowledgeRequest`. The Pub/Sub system can remove the relevant messages - // from the subscription. - // - // Acknowledging a message whose ack deadline has expired may succeed, - // but such a message may be redelivered later. Acknowledging a message more - // than once will not result in an error. - Acknowledge(ctx context.Context, in *AcknowledgeRequest, opts ...grpc.CallOption) (*empty.Empty, error) - // Pulls messages from the server. Returns an empty list if there are no - // messages available in the backlog. The server may return `UNAVAILABLE` if - // there are too many concurrent pull requests pending for the given - // subscription. - Pull(ctx context.Context, in *PullRequest, opts ...grpc.CallOption) (*PullResponse, error) - // (EXPERIMENTAL) StreamingPull is an experimental feature. This RPC will - // respond with UNIMPLEMENTED errors unless you have been invited to test - // this feature. Contact cloud-pubsub@google.com with any questions. - // - // Establishes a stream with the server, which sends messages down to the - // client. The client streams acknowledgements and ack deadline modifications - // back to the server. The server will close the stream and return the status - // on any error. The server may close the stream with status `OK` to reassign - // server-side resources, in which case, the client should re-establish the - // stream. `UNAVAILABLE` may also be returned in the case of a transient error - // (e.g., a server restart). These should also be retried by the client. Flow - // control can be achieved by configuring the underlying RPC channel. - StreamingPull(ctx context.Context, opts ...grpc.CallOption) (Subscriber_StreamingPullClient, error) - // Modifies the `PushConfig` for a specified subscription. - // - // This may be used to change a push subscription to a pull one (signified by - // an empty `PushConfig`) or vice versa, or change the endpoint URL and other - // attributes of a push subscription. Messages will accumulate for delivery - // continuously through the call regardless of changes to the `PushConfig`. - ModifyPushConfig(ctx context.Context, in *ModifyPushConfigRequest, opts ...grpc.CallOption) (*empty.Empty, error) - // Lists the existing snapshots. - ListSnapshots(ctx context.Context, in *ListSnapshotsRequest, opts ...grpc.CallOption) (*ListSnapshotsResponse, error) - // Creates a snapshot from the requested subscription. - // If the snapshot already exists, returns `ALREADY_EXISTS`. - // If the requested subscription doesn't exist, returns `NOT_FOUND`. - // - // If the name is not provided in the request, the server will assign a random - // name for this snapshot on the same project as the subscription, conforming - // to the - // [resource name format](https://cloud.google.com/pubsub/docs/overview#names). - // The generated name is populated in the returned Snapshot object. - // Note that for REST API requests, you must specify a name in the request. - CreateSnapshot(ctx context.Context, in *CreateSnapshotRequest, opts ...grpc.CallOption) (*Snapshot, error) - // Updates an existing snapshot. Note that certain properties of a snapshot - // are not modifiable. - // NOTE: The style guide requires body: "snapshot" instead of body: "*". - // Keeping the latter for internal consistency in V1, however it should be - // corrected in V2. See - // https://cloud.google.com/apis/design/standard_methods#update for details. - UpdateSnapshot(ctx context.Context, in *UpdateSnapshotRequest, opts ...grpc.CallOption) (*Snapshot, error) - // Removes an existing snapshot. All messages retained in the snapshot - // are immediately dropped. After a snapshot is deleted, a new one may be - // created with the same name, but the new one has no association with the old - // snapshot or its subscription, unless the same subscription is specified. - DeleteSnapshot(ctx context.Context, in *DeleteSnapshotRequest, opts ...grpc.CallOption) (*empty.Empty, error) - // Seeks an existing subscription to a point in time or to a given snapshot, - // whichever is provided in the request. - Seek(ctx context.Context, in *SeekRequest, opts ...grpc.CallOption) (*SeekResponse, error) -} - -type subscriberClient struct { - cc *grpc.ClientConn -} - -func NewSubscriberClient(cc *grpc.ClientConn) SubscriberClient { - return &subscriberClient{cc} -} - -func (c *subscriberClient) CreateSubscription(ctx context.Context, in *Subscription, opts ...grpc.CallOption) (*Subscription, error) { - out := new(Subscription) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/CreateSubscription", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *subscriberClient) GetSubscription(ctx context.Context, in *GetSubscriptionRequest, opts ...grpc.CallOption) (*Subscription, error) { - out := new(Subscription) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/GetSubscription", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *subscriberClient) UpdateSubscription(ctx context.Context, in *UpdateSubscriptionRequest, opts ...grpc.CallOption) (*Subscription, error) { - out := new(Subscription) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/UpdateSubscription", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *subscriberClient) ListSubscriptions(ctx context.Context, in *ListSubscriptionsRequest, opts ...grpc.CallOption) (*ListSubscriptionsResponse, error) { - out := new(ListSubscriptionsResponse) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/ListSubscriptions", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *subscriberClient) DeleteSubscription(ctx context.Context, in *DeleteSubscriptionRequest, opts ...grpc.CallOption) (*empty.Empty, error) { - out := new(empty.Empty) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/DeleteSubscription", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *subscriberClient) ModifyAckDeadline(ctx context.Context, in *ModifyAckDeadlineRequest, opts ...grpc.CallOption) (*empty.Empty, error) { - out := new(empty.Empty) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/ModifyAckDeadline", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *subscriberClient) Acknowledge(ctx context.Context, in *AcknowledgeRequest, opts ...grpc.CallOption) (*empty.Empty, error) { - out := new(empty.Empty) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/Acknowledge", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *subscriberClient) Pull(ctx context.Context, in *PullRequest, opts ...grpc.CallOption) (*PullResponse, error) { - out := new(PullResponse) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/Pull", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *subscriberClient) StreamingPull(ctx context.Context, opts ...grpc.CallOption) (Subscriber_StreamingPullClient, error) { - stream, err := c.cc.NewStream(ctx, &_Subscriber_serviceDesc.Streams[0], "/google.pubsub.v1.Subscriber/StreamingPull", opts...) - if err != nil { - return nil, err - } - x := &subscriberStreamingPullClient{stream} - return x, nil -} - -type Subscriber_StreamingPullClient interface { - Send(*StreamingPullRequest) error - Recv() (*StreamingPullResponse, error) - grpc.ClientStream -} - -type subscriberStreamingPullClient struct { - grpc.ClientStream -} - -func (x *subscriberStreamingPullClient) Send(m *StreamingPullRequest) error { - return x.ClientStream.SendMsg(m) -} - -func (x *subscriberStreamingPullClient) Recv() (*StreamingPullResponse, error) { - m := new(StreamingPullResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func (c *subscriberClient) ModifyPushConfig(ctx context.Context, in *ModifyPushConfigRequest, opts ...grpc.CallOption) (*empty.Empty, error) { - out := new(empty.Empty) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/ModifyPushConfig", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *subscriberClient) ListSnapshots(ctx context.Context, in *ListSnapshotsRequest, opts ...grpc.CallOption) (*ListSnapshotsResponse, error) { - out := new(ListSnapshotsResponse) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/ListSnapshots", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *subscriberClient) CreateSnapshot(ctx context.Context, in *CreateSnapshotRequest, opts ...grpc.CallOption) (*Snapshot, error) { - out := new(Snapshot) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/CreateSnapshot", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *subscriberClient) UpdateSnapshot(ctx context.Context, in *UpdateSnapshotRequest, opts ...grpc.CallOption) (*Snapshot, error) { - out := new(Snapshot) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/UpdateSnapshot", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *subscriberClient) DeleteSnapshot(ctx context.Context, in *DeleteSnapshotRequest, opts ...grpc.CallOption) (*empty.Empty, error) { - out := new(empty.Empty) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/DeleteSnapshot", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *subscriberClient) Seek(ctx context.Context, in *SeekRequest, opts ...grpc.CallOption) (*SeekResponse, error) { - out := new(SeekResponse) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Subscriber/Seek", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for Subscriber service - -type SubscriberServer interface { - // Creates a subscription to a given topic. - // If the subscription already exists, returns `ALREADY_EXISTS`. - // If the corresponding topic doesn't exist, returns `NOT_FOUND`. - // - // If the name is not provided in the request, the server will assign a random - // name for this subscription on the same project as the topic, conforming - // to the - // [resource name format](https://cloud.google.com/pubsub/docs/overview#names). - // The generated name is populated in the returned Subscription object. - // Note that for REST API requests, you must specify a name in the request. - CreateSubscription(context.Context, *Subscription) (*Subscription, error) - // Gets the configuration details of a subscription. - GetSubscription(context.Context, *GetSubscriptionRequest) (*Subscription, error) - // Updates an existing subscription. Note that certain properties of a - // subscription, such as its topic, are not modifiable. - // NOTE: The style guide requires body: "subscription" instead of body: "*". - // Keeping the latter for internal consistency in V1, however it should be - // corrected in V2. See - // https://cloud.google.com/apis/design/standard_methods#update for details. - UpdateSubscription(context.Context, *UpdateSubscriptionRequest) (*Subscription, error) - // Lists matching subscriptions. - ListSubscriptions(context.Context, *ListSubscriptionsRequest) (*ListSubscriptionsResponse, error) - // Deletes an existing subscription. All messages retained in the subscription - // are immediately dropped. Calls to `Pull` after deletion will return - // `NOT_FOUND`. After a subscription is deleted, a new one may be created with - // the same name, but the new one has no association with the old - // subscription or its topic unless the same topic is specified. - DeleteSubscription(context.Context, *DeleteSubscriptionRequest) (*empty.Empty, error) - // Modifies the ack deadline for a specific message. This method is useful - // to indicate that more time is needed to process a message by the - // subscriber, or to make the message available for redelivery if the - // processing was interrupted. Note that this does not modify the - // subscription-level `ackDeadlineSeconds` used for subsequent messages. - ModifyAckDeadline(context.Context, *ModifyAckDeadlineRequest) (*empty.Empty, error) - // Acknowledges the messages associated with the `ack_ids` in the - // `AcknowledgeRequest`. The Pub/Sub system can remove the relevant messages - // from the subscription. - // - // Acknowledging a message whose ack deadline has expired may succeed, - // but such a message may be redelivered later. Acknowledging a message more - // than once will not result in an error. - Acknowledge(context.Context, *AcknowledgeRequest) (*empty.Empty, error) - // Pulls messages from the server. Returns an empty list if there are no - // messages available in the backlog. The server may return `UNAVAILABLE` if - // there are too many concurrent pull requests pending for the given - // subscription. - Pull(context.Context, *PullRequest) (*PullResponse, error) - // (EXPERIMENTAL) StreamingPull is an experimental feature. This RPC will - // respond with UNIMPLEMENTED errors unless you have been invited to test - // this feature. Contact cloud-pubsub@google.com with any questions. - // - // Establishes a stream with the server, which sends messages down to the - // client. The client streams acknowledgements and ack deadline modifications - // back to the server. The server will close the stream and return the status - // on any error. The server may close the stream with status `OK` to reassign - // server-side resources, in which case, the client should re-establish the - // stream. `UNAVAILABLE` may also be returned in the case of a transient error - // (e.g., a server restart). These should also be retried by the client. Flow - // control can be achieved by configuring the underlying RPC channel. - StreamingPull(Subscriber_StreamingPullServer) error - // Modifies the `PushConfig` for a specified subscription. - // - // This may be used to change a push subscription to a pull one (signified by - // an empty `PushConfig`) or vice versa, or change the endpoint URL and other - // attributes of a push subscription. Messages will accumulate for delivery - // continuously through the call regardless of changes to the `PushConfig`. - ModifyPushConfig(context.Context, *ModifyPushConfigRequest) (*empty.Empty, error) - // Lists the existing snapshots. - ListSnapshots(context.Context, *ListSnapshotsRequest) (*ListSnapshotsResponse, error) - // Creates a snapshot from the requested subscription. - // If the snapshot already exists, returns `ALREADY_EXISTS`. - // If the requested subscription doesn't exist, returns `NOT_FOUND`. - // - // If the name is not provided in the request, the server will assign a random - // name for this snapshot on the same project as the subscription, conforming - // to the - // [resource name format](https://cloud.google.com/pubsub/docs/overview#names). - // The generated name is populated in the returned Snapshot object. - // Note that for REST API requests, you must specify a name in the request. - CreateSnapshot(context.Context, *CreateSnapshotRequest) (*Snapshot, error) - // Updates an existing snapshot. Note that certain properties of a snapshot - // are not modifiable. - // NOTE: The style guide requires body: "snapshot" instead of body: "*". - // Keeping the latter for internal consistency in V1, however it should be - // corrected in V2. See - // https://cloud.google.com/apis/design/standard_methods#update for details. - UpdateSnapshot(context.Context, *UpdateSnapshotRequest) (*Snapshot, error) - // Removes an existing snapshot. All messages retained in the snapshot - // are immediately dropped. After a snapshot is deleted, a new one may be - // created with the same name, but the new one has no association with the old - // snapshot or its subscription, unless the same subscription is specified. - DeleteSnapshot(context.Context, *DeleteSnapshotRequest) (*empty.Empty, error) - // Seeks an existing subscription to a point in time or to a given snapshot, - // whichever is provided in the request. - Seek(context.Context, *SeekRequest) (*SeekResponse, error) -} - -func RegisterSubscriberServer(s *grpc.Server, srv SubscriberServer) { - s.RegisterService(&_Subscriber_serviceDesc, srv) -} - -func _Subscriber_CreateSubscription_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Subscription) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).CreateSubscription(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/CreateSubscription", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).CreateSubscription(ctx, req.(*Subscription)) - } - return interceptor(ctx, in, info, handler) -} - -func _Subscriber_GetSubscription_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetSubscriptionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).GetSubscription(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/GetSubscription", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).GetSubscription(ctx, req.(*GetSubscriptionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Subscriber_UpdateSubscription_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateSubscriptionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).UpdateSubscription(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/UpdateSubscription", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).UpdateSubscription(ctx, req.(*UpdateSubscriptionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Subscriber_ListSubscriptions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListSubscriptionsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).ListSubscriptions(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/ListSubscriptions", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).ListSubscriptions(ctx, req.(*ListSubscriptionsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Subscriber_DeleteSubscription_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteSubscriptionRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).DeleteSubscription(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/DeleteSubscription", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).DeleteSubscription(ctx, req.(*DeleteSubscriptionRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Subscriber_ModifyAckDeadline_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ModifyAckDeadlineRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).ModifyAckDeadline(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/ModifyAckDeadline", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).ModifyAckDeadline(ctx, req.(*ModifyAckDeadlineRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Subscriber_Acknowledge_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(AcknowledgeRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).Acknowledge(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/Acknowledge", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).Acknowledge(ctx, req.(*AcknowledgeRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Subscriber_Pull_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PullRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).Pull(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/Pull", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).Pull(ctx, req.(*PullRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Subscriber_StreamingPull_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(SubscriberServer).StreamingPull(&subscriberStreamingPullServer{stream}) -} - -type Subscriber_StreamingPullServer interface { - Send(*StreamingPullResponse) error - Recv() (*StreamingPullRequest, error) - grpc.ServerStream -} - -type subscriberStreamingPullServer struct { - grpc.ServerStream -} - -func (x *subscriberStreamingPullServer) Send(m *StreamingPullResponse) error { - return x.ServerStream.SendMsg(m) -} - -func (x *subscriberStreamingPullServer) Recv() (*StreamingPullRequest, error) { - m := new(StreamingPullRequest) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func _Subscriber_ModifyPushConfig_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ModifyPushConfigRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).ModifyPushConfig(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/ModifyPushConfig", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).ModifyPushConfig(ctx, req.(*ModifyPushConfigRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Subscriber_ListSnapshots_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListSnapshotsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).ListSnapshots(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/ListSnapshots", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).ListSnapshots(ctx, req.(*ListSnapshotsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Subscriber_CreateSnapshot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(CreateSnapshotRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).CreateSnapshot(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/CreateSnapshot", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).CreateSnapshot(ctx, req.(*CreateSnapshotRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Subscriber_UpdateSnapshot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateSnapshotRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).UpdateSnapshot(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/UpdateSnapshot", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).UpdateSnapshot(ctx, req.(*UpdateSnapshotRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Subscriber_DeleteSnapshot_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteSnapshotRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).DeleteSnapshot(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/DeleteSnapshot", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).DeleteSnapshot(ctx, req.(*DeleteSnapshotRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Subscriber_Seek_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(SeekRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(SubscriberServer).Seek(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Subscriber/Seek", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(SubscriberServer).Seek(ctx, req.(*SeekRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _Subscriber_serviceDesc = grpc.ServiceDesc{ - ServiceName: "google.pubsub.v1.Subscriber", - HandlerType: (*SubscriberServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "CreateSubscription", - Handler: _Subscriber_CreateSubscription_Handler, - }, - { - MethodName: "GetSubscription", - Handler: _Subscriber_GetSubscription_Handler, - }, - { - MethodName: "UpdateSubscription", - Handler: _Subscriber_UpdateSubscription_Handler, - }, - { - MethodName: "ListSubscriptions", - Handler: _Subscriber_ListSubscriptions_Handler, - }, - { - MethodName: "DeleteSubscription", - Handler: _Subscriber_DeleteSubscription_Handler, - }, - { - MethodName: "ModifyAckDeadline", - Handler: _Subscriber_ModifyAckDeadline_Handler, - }, - { - MethodName: "Acknowledge", - Handler: _Subscriber_Acknowledge_Handler, - }, - { - MethodName: "Pull", - Handler: _Subscriber_Pull_Handler, - }, - { - MethodName: "ModifyPushConfig", - Handler: _Subscriber_ModifyPushConfig_Handler, - }, - { - MethodName: "ListSnapshots", - Handler: _Subscriber_ListSnapshots_Handler, - }, - { - MethodName: "CreateSnapshot", - Handler: _Subscriber_CreateSnapshot_Handler, - }, - { - MethodName: "UpdateSnapshot", - Handler: _Subscriber_UpdateSnapshot_Handler, - }, - { - MethodName: "DeleteSnapshot", - Handler: _Subscriber_DeleteSnapshot_Handler, - }, - { - MethodName: "Seek", - Handler: _Subscriber_Seek_Handler, - }, - }, - Streams: []grpc.StreamDesc{ - { - StreamName: "StreamingPull", - Handler: _Subscriber_StreamingPull_Handler, - ServerStreams: true, - ClientStreams: true, - }, - }, - Metadata: "google/pubsub/v1/pubsub.proto", -} - -// PublisherClient is the client API for Publisher service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type PublisherClient interface { - // Creates the given topic with the given name. - CreateTopic(ctx context.Context, in *Topic, opts ...grpc.CallOption) (*Topic, error) - // Updates an existing topic. Note that certain properties of a topic are not - // modifiable. Options settings follow the style guide: - // NOTE: The style guide requires body: "topic" instead of body: "*". - // Keeping the latter for internal consistency in V1, however it should be - // corrected in V2. See - // https://cloud.google.com/apis/design/standard_methods#update for details. - UpdateTopic(ctx context.Context, in *UpdateTopicRequest, opts ...grpc.CallOption) (*Topic, error) - // Adds one or more messages to the topic. Returns `NOT_FOUND` if the topic - // does not exist. The message payload must not be empty; it must contain - // either a non-empty data field, or at least one attribute. - Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*PublishResponse, error) - // Gets the configuration of a topic. - GetTopic(ctx context.Context, in *GetTopicRequest, opts ...grpc.CallOption) (*Topic, error) - // Lists matching topics. - ListTopics(ctx context.Context, in *ListTopicsRequest, opts ...grpc.CallOption) (*ListTopicsResponse, error) - // Lists the name of the subscriptions for this topic. - ListTopicSubscriptions(ctx context.Context, in *ListTopicSubscriptionsRequest, opts ...grpc.CallOption) (*ListTopicSubscriptionsResponse, error) - // Deletes the topic with the given name. Returns `NOT_FOUND` if the topic - // does not exist. After a topic is deleted, a new topic may be created with - // the same name; this is an entirely new topic with none of the old - // configuration or subscriptions. Existing subscriptions to this topic are - // not deleted, but their `topic` field is set to `_deleted-topic_`. - DeleteTopic(ctx context.Context, in *DeleteTopicRequest, opts ...grpc.CallOption) (*empty.Empty, error) -} - -type publisherClient struct { - cc *grpc.ClientConn -} - -func NewPublisherClient(cc *grpc.ClientConn) PublisherClient { - return &publisherClient{cc} -} - -func (c *publisherClient) CreateTopic(ctx context.Context, in *Topic, opts ...grpc.CallOption) (*Topic, error) { - out := new(Topic) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Publisher/CreateTopic", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *publisherClient) UpdateTopic(ctx context.Context, in *UpdateTopicRequest, opts ...grpc.CallOption) (*Topic, error) { - out := new(Topic) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Publisher/UpdateTopic", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *publisherClient) Publish(ctx context.Context, in *PublishRequest, opts ...grpc.CallOption) (*PublishResponse, error) { - out := new(PublishResponse) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Publisher/Publish", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *publisherClient) GetTopic(ctx context.Context, in *GetTopicRequest, opts ...grpc.CallOption) (*Topic, error) { - out := new(Topic) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Publisher/GetTopic", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *publisherClient) ListTopics(ctx context.Context, in *ListTopicsRequest, opts ...grpc.CallOption) (*ListTopicsResponse, error) { - out := new(ListTopicsResponse) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Publisher/ListTopics", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *publisherClient) ListTopicSubscriptions(ctx context.Context, in *ListTopicSubscriptionsRequest, opts ...grpc.CallOption) (*ListTopicSubscriptionsResponse, error) { - out := new(ListTopicSubscriptionsResponse) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Publisher/ListTopicSubscriptions", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -func (c *publisherClient) DeleteTopic(ctx context.Context, in *DeleteTopicRequest, opts ...grpc.CallOption) (*empty.Empty, error) { - out := new(empty.Empty) - err := c.cc.Invoke(ctx, "/google.pubsub.v1.Publisher/DeleteTopic", in, out, opts...) - if err != nil { - return nil, err - } - return out, nil -} - -// Server API for Publisher service - -type PublisherServer interface { - // Creates the given topic with the given name. - CreateTopic(context.Context, *Topic) (*Topic, error) - // Updates an existing topic. Note that certain properties of a topic are not - // modifiable. Options settings follow the style guide: - // NOTE: The style guide requires body: "topic" instead of body: "*". - // Keeping the latter for internal consistency in V1, however it should be - // corrected in V2. See - // https://cloud.google.com/apis/design/standard_methods#update for details. - UpdateTopic(context.Context, *UpdateTopicRequest) (*Topic, error) - // Adds one or more messages to the topic. Returns `NOT_FOUND` if the topic - // does not exist. The message payload must not be empty; it must contain - // either a non-empty data field, or at least one attribute. - Publish(context.Context, *PublishRequest) (*PublishResponse, error) - // Gets the configuration of a topic. - GetTopic(context.Context, *GetTopicRequest) (*Topic, error) - // Lists matching topics. - ListTopics(context.Context, *ListTopicsRequest) (*ListTopicsResponse, error) - // Lists the name of the subscriptions for this topic. - ListTopicSubscriptions(context.Context, *ListTopicSubscriptionsRequest) (*ListTopicSubscriptionsResponse, error) - // Deletes the topic with the given name. Returns `NOT_FOUND` if the topic - // does not exist. After a topic is deleted, a new topic may be created with - // the same name; this is an entirely new topic with none of the old - // configuration or subscriptions. Existing subscriptions to this topic are - // not deleted, but their `topic` field is set to `_deleted-topic_`. - DeleteTopic(context.Context, *DeleteTopicRequest) (*empty.Empty, error) -} - -func RegisterPublisherServer(s *grpc.Server, srv PublisherServer) { - s.RegisterService(&_Publisher_serviceDesc, srv) -} - -func _Publisher_CreateTopic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(Topic) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PublisherServer).CreateTopic(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Publisher/CreateTopic", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PublisherServer).CreateTopic(ctx, req.(*Topic)) - } - return interceptor(ctx, in, info, handler) -} - -func _Publisher_UpdateTopic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(UpdateTopicRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PublisherServer).UpdateTopic(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Publisher/UpdateTopic", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PublisherServer).UpdateTopic(ctx, req.(*UpdateTopicRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Publisher_Publish_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(PublishRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PublisherServer).Publish(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Publisher/Publish", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PublisherServer).Publish(ctx, req.(*PublishRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Publisher_GetTopic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(GetTopicRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PublisherServer).GetTopic(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Publisher/GetTopic", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PublisherServer).GetTopic(ctx, req.(*GetTopicRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Publisher_ListTopics_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListTopicsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PublisherServer).ListTopics(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Publisher/ListTopics", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PublisherServer).ListTopics(ctx, req.(*ListTopicsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Publisher_ListTopicSubscriptions_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(ListTopicSubscriptionsRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PublisherServer).ListTopicSubscriptions(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Publisher/ListTopicSubscriptions", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PublisherServer).ListTopicSubscriptions(ctx, req.(*ListTopicSubscriptionsRequest)) - } - return interceptor(ctx, in, info, handler) -} - -func _Publisher_DeleteTopic_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(DeleteTopicRequest) - if err := dec(in); err != nil { - return nil, err - } - if interceptor == nil { - return srv.(PublisherServer).DeleteTopic(ctx, in) - } - info := &grpc.UnaryServerInfo{ - Server: srv, - FullMethod: "/google.pubsub.v1.Publisher/DeleteTopic", - } - handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(PublisherServer).DeleteTopic(ctx, req.(*DeleteTopicRequest)) - } - return interceptor(ctx, in, info, handler) -} - -var _Publisher_serviceDesc = grpc.ServiceDesc{ - ServiceName: "google.pubsub.v1.Publisher", - HandlerType: (*PublisherServer)(nil), - Methods: []grpc.MethodDesc{ - { - MethodName: "CreateTopic", - Handler: _Publisher_CreateTopic_Handler, - }, - { - MethodName: "UpdateTopic", - Handler: _Publisher_UpdateTopic_Handler, - }, - { - MethodName: "Publish", - Handler: _Publisher_Publish_Handler, - }, - { - MethodName: "GetTopic", - Handler: _Publisher_GetTopic_Handler, - }, - { - MethodName: "ListTopics", - Handler: _Publisher_ListTopics_Handler, - }, - { - MethodName: "ListTopicSubscriptions", - Handler: _Publisher_ListTopicSubscriptions_Handler, - }, - { - MethodName: "DeleteTopic", - Handler: _Publisher_DeleteTopic_Handler, - }, - }, - Streams: []grpc.StreamDesc{}, - Metadata: "google/pubsub/v1/pubsub.proto", -} - -func init() { - proto.RegisterFile("google/pubsub/v1/pubsub.proto", fileDescriptor_pubsub_2c3603090b805ba2) -} - -var fileDescriptor_pubsub_2c3603090b805ba2 = []byte{ - // 2026 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x59, 0xdb, 0x6f, 0xdb, 0xd6, - 0x19, 0xef, 0x91, 0x6f, 0xf2, 0x47, 0xdf, 0x72, 0x66, 0x27, 0x0a, 0x73, 0xb3, 0x19, 0x37, 0x56, - 0xd4, 0x44, 0xb2, 0x55, 0x34, 0x6b, 0xe2, 0x39, 0x85, 0x1d, 0x67, 0x69, 0x86, 0x64, 0xf5, 0xe8, - 0xac, 0x03, 0x86, 0x60, 0x02, 0x25, 0x9e, 0x28, 0xac, 0x24, 0x92, 0x25, 0x29, 0x2f, 0xee, 0x16, - 0xa0, 0x6b, 0x87, 0x01, 0xc3, 0xf2, 0xb0, 0xb6, 0x6f, 0x43, 0x1f, 0x06, 0xec, 0x6d, 0x8f, 0x03, - 0xf6, 0xba, 0x3f, 0x60, 0xaf, 0xfb, 0x17, 0xf6, 0xb8, 0xf7, 0xed, 0x71, 0x38, 0x17, 0x52, 0xbc, - 0x1c, 0x4a, 0x96, 0xd3, 0xbc, 0x91, 0xe7, 0xfb, 0xce, 0xf9, 0x7e, 0xdf, 0xfd, 0x23, 0x0f, 0x5c, - 0x6a, 0x3b, 0x4e, 0xbb, 0x4b, 0x6a, 0x6e, 0xbf, 0xe9, 0xf7, 0x9b, 0xb5, 0xa3, 0x2d, 0xf1, 0x54, - 0x75, 0x3d, 0x27, 0x70, 0xf0, 0x12, 0x27, 0x57, 0xc5, 0xe2, 0xd1, 0x96, 0x7a, 0x51, 0x6c, 0x30, - 0x5c, 0xab, 0x66, 0xd8, 0xb6, 0x13, 0x18, 0x81, 0xe5, 0xd8, 0x3e, 0xe7, 0x57, 0x2f, 0x87, 0xc7, - 0xd1, 0xb7, 0x66, 0xff, 0x59, 0xcd, 0xec, 0x7b, 0x8c, 0x41, 0xd0, 0x2f, 0xa4, 0xe9, 0xa4, 0xe7, - 0x06, 0xc7, 0x82, 0xb8, 0x9a, 0x26, 0x3e, 0xb3, 0x48, 0xd7, 0x6c, 0xf4, 0x0c, 0xbf, 0x23, 0x38, - 0xae, 0xa4, 0x39, 0x02, 0xab, 0x47, 0xfc, 0xc0, 0xe8, 0xb9, 0x9c, 0x41, 0xfb, 0x06, 0xc1, 0xd4, - 0x13, 0xc7, 0xb5, 0x5a, 0x18, 0xc3, 0xa4, 0x6d, 0xf4, 0x48, 0x09, 0xad, 0xa2, 0xf2, 0xac, 0xce, - 0x9e, 0xf1, 0x36, 0x4c, 0x77, 0x8d, 0x26, 0xe9, 0xfa, 0xa5, 0xc2, 0xea, 0x44, 0x59, 0xa9, 0x5f, - 0xad, 0xa6, 0xd5, 0xab, 0xb2, 0xcd, 0xd5, 0x47, 0x8c, 0xeb, 0xbe, 0x1d, 0x78, 0xc7, 0xba, 0xd8, - 0xa2, 0xde, 0x06, 0x25, 0xb6, 0x8c, 0x97, 0x60, 0xa2, 0x43, 0x8e, 0xc5, 0xf1, 0xf4, 0x11, 0x2f, - 0xc3, 0xd4, 0x91, 0xd1, 0xed, 0x93, 0x52, 0x81, 0xad, 0xf1, 0x97, 0x3b, 0x85, 0xf7, 0x91, 0xf6, - 0x55, 0x01, 0xe6, 0x0f, 0x98, 0x88, 0xc7, 0xc4, 0xf7, 0x8d, 0x36, 0xa1, 0xe8, 0x4c, 0x23, 0x30, - 0xd8, 0xf6, 0x39, 0x9d, 0x3d, 0xe3, 0x8f, 0x00, 0x8c, 0x20, 0xf0, 0xac, 0x66, 0x3f, 0x20, 0x21, - 0xc2, 0x5a, 0x16, 0x61, 0xe2, 0xa0, 0xea, 0x6e, 0xb4, 0x83, 0xa3, 0x8d, 0x1d, 0x81, 0x2f, 0x01, - 0xf4, 0x38, 0x5b, 0xc3, 0x32, 0x4b, 0x13, 0x0c, 0xd5, 0xac, 0x58, 0x79, 0x68, 0xe2, 0x1d, 0x98, - 0x73, 0xfb, 0xcd, 0xae, 0xe5, 0x3f, 0x6f, 0x50, 0x33, 0x96, 0x26, 0x57, 0x51, 0x59, 0xa9, 0xab, - 0x91, 0x44, 0x61, 0xe3, 0xea, 0x93, 0xd0, 0xc6, 0xba, 0x22, 0xf8, 0xe9, 0x8a, 0xba, 0x03, 0x8b, - 0x29, 0xe1, 0x63, 0xd9, 0x64, 0x03, 0x16, 0x1f, 0x90, 0x80, 0x99, 0x5b, 0x27, 0x9f, 0xf6, 0x89, - 0x1f, 0x50, 0xe6, 0x80, 0xbe, 0x8b, 0x03, 0xf8, 0x8b, 0xf6, 0x39, 0x02, 0xfc, 0x53, 0xd7, 0x34, - 0x02, 0x92, 0x60, 0xbe, 0x19, 0x67, 0x56, 0xea, 0xe7, 0x72, 0x5c, 0x29, 0x4e, 0xc1, 0xdb, 0xa0, - 0xf4, 0xd9, 0x21, 0x2c, 0x9c, 0x18, 0x1c, 0x99, 0xae, 0x3f, 0xa4, 0x11, 0xf7, 0xd8, 0xf0, 0x3b, - 0x3a, 0x70, 0x76, 0xfa, 0xac, 0xb5, 0x60, 0xe1, 0x80, 0x6b, 0x3e, 0x14, 0x2a, 0xde, 0x86, 0xa2, - 0x30, 0x6f, 0xe8, 0xbf, 0x2b, 0x23, 0xfc, 0xa7, 0x47, 0x1b, 0xb4, 0x3a, 0x2c, 0x46, 0x42, 0x7c, - 0xd7, 0xb1, 0x7d, 0x82, 0xaf, 0x80, 0x32, 0x70, 0xa0, 0x5f, 0x42, 0xab, 0x13, 0xe5, 0x59, 0x1d, - 0x22, 0x0f, 0xfa, 0x9a, 0x05, 0x67, 0x1e, 0x59, 0x3e, 0xb7, 0xa2, 0x1f, 0x62, 0x2b, 0xc1, 0x8c, - 0xeb, 0x39, 0x9f, 0x90, 0x56, 0x20, 0xd0, 0x85, 0xaf, 0xf8, 0x02, 0xcc, 0xba, 0xf4, 0x30, 0xdf, - 0xfa, 0x8c, 0x7b, 0x64, 0x4a, 0x2f, 0xd2, 0x85, 0x43, 0xeb, 0x33, 0x42, 0xa3, 0x85, 0x11, 0x03, - 0xa7, 0x43, 0xec, 0x30, 0x5a, 0xe8, 0xca, 0x13, 0xba, 0xa0, 0xf5, 0x00, 0xc7, 0x45, 0x09, 0x84, - 0x35, 0x98, 0x66, 0xaa, 0x73, 0x70, 0x43, 0xdc, 0x20, 0xd8, 0xf0, 0x35, 0x58, 0xb4, 0xc9, 0x8b, - 0xa0, 0x11, 0x13, 0xc5, 0x43, 0x63, 0x9e, 0x2e, 0x1f, 0x44, 0xe2, 0x3e, 0x85, 0x4b, 0x91, 0xb8, - 0xc3, 0x7e, 0xd3, 0x6f, 0x79, 0x96, 0xcb, 0x0a, 0xcd, 0x70, 0x0f, 0xbc, 0x8e, 0x86, 0x36, 0x5c, - 0xce, 0x13, 0x29, 0xb4, 0x5d, 0x87, 0x79, 0x3f, 0x4e, 0x10, 0x1e, 0x49, 0x2e, 0x9e, 0x58, 0xc5, - 0x0a, 0xe0, 0x7d, 0xd2, 0x25, 0xa9, 0xb8, 0x96, 0x27, 0xc1, 0xdf, 0x27, 0x60, 0x2e, 0x8e, 0x49, - 0x5a, 0xde, 0xa2, 0xad, 0x85, 0xb8, 0x49, 0x76, 0x40, 0x71, 0xfb, 0xfe, 0xf3, 0x46, 0xcb, 0xb1, - 0x9f, 0x59, 0x6d, 0x91, 0xe5, 0x17, 0x65, 0x71, 0xe9, 0x3f, 0xbf, 0xc7, 0x78, 0x74, 0x70, 0xa3, - 0x67, 0xbc, 0x09, 0xcb, 0x46, 0xab, 0xd3, 0x30, 0x89, 0x61, 0x76, 0x2d, 0x9b, 0x34, 0x7c, 0xd2, - 0x72, 0x6c, 0xd3, 0x2f, 0x4d, 0x31, 0xe3, 0x62, 0xa3, 0xd5, 0xd9, 0x17, 0xa4, 0x43, 0x4e, 0xc1, - 0x75, 0x58, 0xf1, 0x48, 0x60, 0x58, 0x76, 0xc3, 0x68, 0x75, 0x88, 0xd9, 0x88, 0x52, 0x62, 0x66, - 0x15, 0x95, 0x8b, 0xfa, 0xf7, 0x38, 0x71, 0x97, 0xd2, 0x44, 0x16, 0xf8, 0xf8, 0x67, 0xa0, 0x86, - 0x91, 0xee, 0x91, 0x80, 0xd8, 0x54, 0xc7, 0x46, 0xd8, 0x3b, 0x4a, 0x45, 0x86, 0xf9, 0x7c, 0x26, - 0x5b, 0xf7, 0x05, 0x83, 0x5e, 0x12, 0x9b, 0xf5, 0x70, 0x6f, 0x48, 0xc1, 0x7b, 0x51, 0xc9, 0x9f, - 0x65, 0x01, 0x5a, 0xc9, 0x2a, 0x1e, 0xb7, 0xeb, 0x77, 0x5d, 0xf9, 0xff, 0x81, 0x00, 0x06, 0x86, - 0xc5, 0x57, 0x61, 0x9e, 0xf9, 0x82, 0xd8, 0xa6, 0xeb, 0x58, 0x76, 0x98, 0xa0, 0x73, 0x74, 0xf1, - 0xbe, 0x58, 0xc3, 0x8f, 0x24, 0x7d, 0xe0, 0xc6, 0x30, 0x7f, 0x0d, 0x6b, 0x02, 0xaf, 0x5b, 0xa6, - 0x5b, 0xb0, 0xa8, 0x93, 0x16, 0xb1, 0x8e, 0x22, 0x67, 0xe1, 0x15, 0x98, 0xa6, 0x11, 0x61, 0x99, - 0x61, 0x88, 0x1a, 0xad, 0xce, 0x43, 0x13, 0xdf, 0x86, 0x19, 0xe1, 0x05, 0x51, 0x5d, 0x47, 0xd6, - 0xbe, 0x90, 0x5f, 0xfb, 0x01, 0x9c, 0x7d, 0x40, 0x82, 0xb8, 0x1f, 0xc2, 0x6c, 0xd0, 0x60, 0x2e, - 0x9e, 0x5c, 0xa1, 0xbd, 0xe2, 0x6b, 0xda, 0xb7, 0x08, 0xce, 0xf3, 0x06, 0x21, 0x3b, 0x61, 0x4f, - 0x72, 0x82, 0x52, 0xbf, 0x3c, 0x3c, 0x0c, 0x92, 0x12, 0x5e, 0xaf, 0x79, 0xb8, 0x50, 0xa2, 0x65, - 0x45, 0x5a, 0xc4, 0xde, 0x4c, 0xa9, 0xfe, 0x3d, 0x82, 0xf3, 0x12, 0x91, 0xa2, 0x88, 0xed, 0xcb, - 0x8a, 0xd8, 0x68, 0x8b, 0x9c, 0xb2, 0xc8, 0x7d, 0x00, 0xe7, 0x79, 0x91, 0x3b, 0xad, 0x77, 0x7f, - 0x0d, 0xe7, 0x1e, 0x3b, 0xa6, 0xf5, 0xec, 0x38, 0x56, 0x9f, 0x4e, 0xbe, 0x3d, 0x5d, 0xfd, 0x0a, - 0xe3, 0x55, 0x3f, 0xed, 0x4b, 0x04, 0xca, 0x41, 0xbf, 0xdb, 0x1d, 0x47, 0xe4, 0x4d, 0xc0, 0x1e, - 0x09, 0xfa, 0x9e, 0xdd, 0xb0, 0x7a, 0x3d, 0x62, 0x5a, 0x46, 0x40, 0xba, 0xc7, 0x4c, 0x72, 0x51, - 0x3f, 0xc3, 0x29, 0x0f, 0x07, 0x04, 0xbc, 0x06, 0x73, 0x3d, 0xe3, 0xc5, 0xa0, 0x4a, 0x4e, 0x30, - 0x67, 0x2b, 0x3d, 0xe3, 0x45, 0x58, 0x1d, 0xb5, 0x5f, 0xc0, 0x1c, 0x07, 0x21, 0x5c, 0xf8, 0x63, - 0x38, 0xe3, 0x89, 0xa4, 0x1c, 0xec, 0xe3, 0x6e, 0x5c, 0xcb, 0xaa, 0x96, 0xca, 0x5f, 0x7d, 0xc9, - 0x4b, 0x2e, 0xf8, 0x34, 0x60, 0x4a, 0xdc, 0xc8, 0xbb, 0x83, 0x72, 0x3e, 0x8e, 0xca, 0xe7, 0x60, - 0x86, 0x97, 0x04, 0xbf, 0x34, 0xc9, 0x5a, 0xe2, 0x34, 0xab, 0x09, 0x7e, 0x6e, 0xf7, 0x98, 0xc8, - 0xeb, 0x1e, 0xda, 0x4f, 0x00, 0xef, 0xb6, 0x3a, 0xb6, 0xf3, 0xcb, 0x2e, 0x31, 0xdb, 0xa7, 0x05, - 0x51, 0x88, 0x83, 0xd0, 0x7e, 0x53, 0x80, 0xe5, 0xc3, 0xc0, 0x23, 0x46, 0xcf, 0xb2, 0xdb, 0xe3, - 0x7a, 0x33, 0xef, 0x54, 0x7c, 0x0b, 0xce, 0xf5, 0x98, 0xcd, 0x64, 0xda, 0x4d, 0x94, 0xa7, 0xf4, - 0x15, 0x4e, 0x4e, 0xb7, 0xc7, 0xf7, 0xb2, 0xfb, 0x92, 0xb6, 0x5b, 0x4e, 0xee, 0xdb, 0xe5, 0xe2, - 0x76, 0xe0, 0x82, 0xcf, 0x74, 0x68, 0x0c, 0x69, 0xc7, 0x25, 0xce, 0xb2, 0x9b, 0x35, 0x6b, 0x1b, - 0x56, 0x52, 0x26, 0x78, 0x43, 0xb1, 0xf4, 0x11, 0xac, 0xdc, 0xf3, 0x08, 0x2d, 0xc6, 0xb6, 0xe1, - 0xfa, 0xcf, 0x9d, 0x20, 0x34, 0xb6, 0x6c, 0x62, 0x49, 0x3b, 0xa0, 0x20, 0x29, 0x00, 0xaf, 0x10, - 0xac, 0x88, 0xf2, 0x9e, 0x3a, 0xf1, 0x16, 0x14, 0x7d, 0xb1, 0x24, 0xca, 0xba, 0x2a, 0x29, 0x62, - 0xe1, 0xa6, 0x88, 0xf7, 0xf5, 0xca, 0xf9, 0x7f, 0x10, 0x14, 0xc3, 0x33, 0xc7, 0x98, 0xc2, 0xb6, - 0x41, 0x21, 0x2f, 0x5c, 0xcb, 0x23, 0xfc, 0x5b, 0x6b, 0x62, 0xe4, 0xb7, 0x16, 0x70, 0x76, 0xba, - 0x80, 0xef, 0x46, 0x43, 0xcc, 0x24, 0x73, 0xcc, 0xb5, 0x7c, 0x35, 0xbf, 0xeb, 0x01, 0xa6, 0x0b, - 0xcb, 0xac, 0x95, 0x88, 0xe3, 0xdf, 0x70, 0xe7, 0x3a, 0x86, 0x95, 0x94, 0x34, 0x11, 0xa5, 0xef, - 0xc3, 0x6c, 0xe8, 0xbe, 0x30, 0x3a, 0x87, 0xf9, 0x7a, 0xc0, 0x7c, 0xe2, 0x46, 0xf5, 0x2e, 0xac, - 0x88, 0x46, 0x95, 0x8a, 0x32, 0x35, 0x15, 0x65, 0xb3, 0x83, 0x48, 0xd2, 0xfe, 0x80, 0x40, 0x39, - 0x24, 0xa4, 0x33, 0x4e, 0x41, 0xd9, 0x84, 0x49, 0x16, 0x02, 0x85, 0x51, 0x21, 0xf0, 0xe1, 0x5b, - 0x3a, 0xe3, 0xc4, 0x17, 0x63, 0x08, 0x98, 0xc9, 0x3e, 0x7c, 0x6b, 0x80, 0x61, 0xaf, 0x08, 0xd3, - 0x81, 0xe1, 0xb5, 0x49, 0xa0, 0x2d, 0xc0, 0x1c, 0x07, 0xc3, 0x8d, 0x56, 0xff, 0xef, 0x12, 0x80, - 0x68, 0xbb, 0x4d, 0xe2, 0xe1, 0xdf, 0x21, 0xc0, 0x22, 0x35, 0xe3, 0x78, 0x46, 0x34, 0x7e, 0x75, - 0x04, 0x5d, 0xdb, 0xfc, 0xe2, 0x5f, 0xff, 0xfe, 0xa6, 0x50, 0x51, 0xdf, 0xae, 0x1d, 0x6d, 0xd5, - 0x7e, 0x45, 0x53, 0x60, 0x47, 0x84, 0x82, 0x5f, 0xab, 0xd4, 0x12, 0x53, 0x43, 0xad, 0xf2, 0xf2, - 0x0e, 0xaa, 0xe0, 0xaf, 0x11, 0xfb, 0xf6, 0x4f, 0xa0, 0x28, 0x67, 0xa5, 0xc8, 0x47, 0xc2, 0x91, - 0x78, 0xde, 0x63, 0x78, 0x6a, 0xf8, 0x26, 0xc3, 0x13, 0x97, 0x3f, 0x0c, 0x17, 0xfe, 0x73, 0xf4, - 0x9b, 0x21, 0x81, 0xeb, 0x9d, 0xac, 0xb4, 0xdc, 0x59, 0x73, 0x24, 0xb4, 0x1d, 0x06, 0xed, 0xfb, - 0xf5, 0x7a, 0x06, 0x5a, 0xf5, 0x24, 0x76, 0xfb, 0x16, 0xf1, 0xcf, 0xfd, 0xc4, 0x5c, 0x87, 0x25, - 0x5f, 0x34, 0x79, 0xf3, 0xa6, 0xfa, 0xce, 0x89, 0x78, 0x79, 0xf8, 0x68, 0x55, 0x86, 0xb6, 0x8c, - 0xaf, 0x31, 0xb4, 0x02, 0x5b, 0x0c, 0xe3, 0xcb, 0x24, 0x48, 0xfc, 0x47, 0x14, 0x7e, 0xd0, 0x8e, - 0xb2, 0x60, 0xee, 0x44, 0xa8, 0x9e, 0xcd, 0xa4, 0xc3, 0xfd, 0x9e, 0x1b, 0x1c, 0x87, 0x4e, 0xad, - 0x8c, 0xe9, 0xd4, 0xbf, 0x20, 0x38, 0x93, 0x19, 0x6c, 0x64, 0x16, 0xcb, 0x9b, 0x7e, 0x72, 0x01, - 0xfd, 0x88, 0x01, 0xda, 0xd7, 0x3e, 0x18, 0x0b, 0xd0, 0x9d, 0x5e, 0x5a, 0x0e, 0xf5, 0xeb, 0x57, - 0x08, 0x94, 0xd8, 0xcc, 0x83, 0xd7, 0xb3, 0xf8, 0xb2, 0x23, 0x51, 0x2e, 0xb2, 0x7d, 0x86, 0xec, - 0xae, 0x76, 0x7b, 0x3c, 0x64, 0xc6, 0x40, 0x02, 0xc5, 0xf4, 0x5b, 0x04, 0x93, 0x74, 0x4e, 0xc0, - 0x97, 0x64, 0xb3, 0x72, 0x34, 0x42, 0xc9, 0x42, 0x3e, 0x3e, 0x5e, 0x84, 0x21, 0xaf, 0xd5, 0xc7, - 0x43, 0xe3, 0xf6, 0xbb, 0x5d, 0x0a, 0xc3, 0x84, 0xf9, 0xc4, 0xd8, 0x82, 0x65, 0xad, 0x4f, 0x32, - 0xda, 0xa9, 0x1b, 0x23, 0xf9, 0x38, 0xc0, 0x32, 0xda, 0x44, 0x34, 0xf7, 0x97, 0xd2, 0x1f, 0x19, - 0xf8, 0x7a, 0x5e, 0x94, 0x64, 0x3e, 0x44, 0x72, 0x5d, 0xf1, 0x90, 0x29, 0x7f, 0x4f, 0xbb, 0x7b, - 0x9a, 0x20, 0x19, 0x88, 0xa1, 0x86, 0x78, 0x85, 0x60, 0x3e, 0xd1, 0x1a, 0x65, 0x96, 0x90, 0x75, - 0x6a, 0x99, 0x25, 0xa4, 0x3d, 0x56, 0xab, 0x30, 0xb4, 0xeb, 0x58, 0xcb, 0xcf, 0xf7, 0x48, 0xf8, - 0x97, 0x08, 0x16, 0x92, 0x63, 0x1e, 0x96, 0xc8, 0x91, 0x0e, 0x82, 0xea, 0x90, 0xc6, 0xad, 0xdd, - 0x60, 0x18, 0xae, 0xa9, 0x6b, 0xf2, 0x66, 0x12, 0xca, 0x17, 0x05, 0xf1, 0x15, 0x82, 0x85, 0xe4, - 0x68, 0x28, 0x43, 0x21, 0x1d, 0x1e, 0x87, 0xa2, 0x10, 0xd5, 0xa6, 0x5e, 0xe1, 0x7e, 0x0b, 0x47, - 0xab, 0x51, 0x70, 0x3e, 0x47, 0xb0, 0x90, 0x9c, 0x21, 0x64, 0x70, 0xa4, 0x53, 0x46, 0x6e, 0x08, - 0xdd, 0x64, 0x50, 0x36, 0x2a, 0x6f, 0x27, 0xa0, 0xe4, 0xa1, 0x60, 0x69, 0x4b, 0x67, 0x00, 0x59, - 0xda, 0xc6, 0x06, 0x15, 0x69, 0xa7, 0x8a, 0x8d, 0x0e, 0xa7, 0x4d, 0x5b, 0x9f, 0x90, 0xce, 0x1d, - 0x54, 0xa9, 0xff, 0x69, 0x06, 0x66, 0xc5, 0xcf, 0x6c, 0xe2, 0xe1, 0x4f, 0x40, 0xe1, 0x91, 0xc0, - 0x6f, 0x66, 0xf2, 0xfe, 0x11, 0xab, 0x79, 0x04, 0xed, 0x3a, 0x43, 0x73, 0x55, 0xbd, 0x2c, 0x8d, - 0x0a, 0xfe, 0x67, 0x59, 0xf8, 0xe0, 0x25, 0x28, 0xb1, 0xcb, 0x02, 0x59, 0x29, 0xcd, 0xde, 0x25, - 0xe4, 0x0b, 0xae, 0x31, 0xc1, 0xd7, 0xeb, 0xeb, 0x4c, 0x30, 0x13, 0x54, 0x1d, 0x2a, 0xfe, 0x0b, - 0x04, 0x33, 0x42, 0x71, 0xbc, 0x2a, 0xfd, 0xff, 0x15, 0xbb, 0x45, 0x50, 0xd7, 0x86, 0x70, 0x08, - 0x47, 0xd4, 0x19, 0x82, 0x1b, 0xda, 0xc6, 0x00, 0x81, 0x5c, 0xb8, 0xb8, 0x9a, 0xa1, 0x20, 0x1c, - 0x28, 0x86, 0x57, 0x2b, 0x78, 0x4d, 0x3a, 0x57, 0x9d, 0x4c, 0xfb, 0x0d, 0x26, 0x7b, 0x0d, 0x5f, - 0x19, 0x21, 0x9b, 0x06, 0x3e, 0x0c, 0x2e, 0x07, 0xf0, 0x55, 0x79, 0xc5, 0x49, 0xdc, 0x52, 0xa8, - 0xeb, 0xc3, 0x99, 0x84, 0xfa, 0x49, 0x08, 0xb2, 0x9a, 0x24, 0xee, 0x15, 0xfe, 0x86, 0xe0, 0xac, - 0xfc, 0xef, 0x3d, 0xae, 0x0d, 0x91, 0x24, 0x9d, 0x92, 0x36, 0x4f, 0xbe, 0x41, 0xc0, 0x4c, 0xce, - 0x9c, 0xf9, 0x96, 0x4a, 0x4d, 0x4c, 0x01, 0x28, 0xb1, 0x1b, 0x00, 0x59, 0xb0, 0x66, 0x2f, 0x08, - 0x72, 0x2b, 0x85, 0x30, 0x55, 0x65, 0x94, 0xb7, 0xf6, 0xbe, 0x46, 0xb0, 0xdc, 0x72, 0x7a, 0x19, - 0x61, 0x7b, 0x0a, 0xff, 0x3d, 0x7b, 0x40, 0xcf, 0x3d, 0x40, 0x3f, 0xbf, 0x25, 0x18, 0xda, 0x4e, - 0xd7, 0xb0, 0xdb, 0x55, 0xc7, 0x6b, 0xd7, 0xda, 0xc4, 0x66, 0x52, 0x6b, 0x9c, 0x64, 0xb8, 0x96, - 0x3f, 0xb8, 0x39, 0xde, 0xe6, 0x4f, 0xff, 0x43, 0xe8, 0xaf, 0x85, 0xb3, 0x0f, 0xf8, 0xde, 0x7b, - 0x5d, 0xa7, 0x6f, 0xd2, 0xa0, 0x3e, 0xec, 0x37, 0xab, 0x1f, 0x6f, 0xfd, 0x33, 0x24, 0x3c, 0x65, - 0x84, 0xa7, 0x9c, 0xf0, 0xf4, 0xe3, 0xad, 0xe6, 0x34, 0x3b, 0xf7, 0xdd, 0xff, 0x07, 0x00, 0x00, - 0xff, 0xff, 0xcb, 0xe1, 0xb8, 0xca, 0x90, 0x1e, 0x00, 0x00, -} diff --git a/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go b/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go deleted file mode 100644 index fb104e6dfb8..00000000000 --- a/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go +++ /dev/null @@ -1,156 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: google/rpc/status.proto - -package status // import "google.golang.org/genproto/googleapis/rpc/status" - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" -import any "github.com/golang/protobuf/ptypes/any" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -// The `Status` type defines a logical error model that is suitable for different -// programming environments, including REST APIs and RPC APIs. It is used by -// [gRPC](https://github.com/grpc). The error model is designed to be: -// -// - Simple to use and understand for most users -// - Flexible enough to meet unexpected needs -// -// # Overview -// -// The `Status` message contains three pieces of data: error code, error message, -// and error details. The error code should be an enum value of -// [google.rpc.Code][google.rpc.Code], but it may accept additional error codes if needed. The -// error message should be a developer-facing English message that helps -// developers *understand* and *resolve* the error. If a localized user-facing -// error message is needed, put the localized message in the error details or -// localize it in the client. The optional error details may contain arbitrary -// information about the error. There is a predefined set of error detail types -// in the package `google.rpc` that can be used for common error conditions. -// -// # Language mapping -// -// The `Status` message is the logical representation of the error model, but it -// is not necessarily the actual wire format. When the `Status` message is -// exposed in different client libraries and different wire protocols, it can be -// mapped differently. For example, it will likely be mapped to some exceptions -// in Java, but more likely mapped to some error codes in C. -// -// # Other uses -// -// The error model and the `Status` message can be used in a variety of -// environments, either with or without APIs, to provide a -// consistent developer experience across different environments. -// -// Example uses of this error model include: -// -// - Partial errors. If a service needs to return partial errors to the client, -// it may embed the `Status` in the normal response to indicate the partial -// errors. -// -// - Workflow errors. A typical workflow has multiple steps. Each step may -// have a `Status` message for error reporting. -// -// - Batch operations. If a client uses batch request and batch response, the -// `Status` message should be used directly inside batch response, one for -// each error sub-response. -// -// - Asynchronous operations. If an API call embeds asynchronous operation -// results in its response, the status of those operations should be -// represented directly using the `Status` message. -// -// - Logging. If some API errors are stored in logs, the message `Status` could -// be used directly after any stripping needed for security/privacy reasons. -type Status struct { - // The status code, which should be an enum value of [google.rpc.Code][google.rpc.Code]. - Code int32 `protobuf:"varint,1,opt,name=code" json:"code,omitempty"` - // A developer-facing error message, which should be in English. Any - // user-facing error message should be localized and sent in the - // [google.rpc.Status.details][google.rpc.Status.details] field, or localized by the client. - Message string `protobuf:"bytes,2,opt,name=message" json:"message,omitempty"` - // A list of messages that carry the error details. There is a common set of - // message types for APIs to use. - Details []*any.Any `protobuf:"bytes,3,rep,name=details" json:"details,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Status) Reset() { *m = Status{} } -func (m *Status) String() string { return proto.CompactTextString(m) } -func (*Status) ProtoMessage() {} -func (*Status) Descriptor() ([]byte, []int) { - return fileDescriptor_status_c656c685916bdf47, []int{0} -} -func (m *Status) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Status.Unmarshal(m, b) -} -func (m *Status) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Status.Marshal(b, m, deterministic) -} -func (dst *Status) XXX_Merge(src proto.Message) { - xxx_messageInfo_Status.Merge(dst, src) -} -func (m *Status) XXX_Size() int { - return xxx_messageInfo_Status.Size(m) -} -func (m *Status) XXX_DiscardUnknown() { - xxx_messageInfo_Status.DiscardUnknown(m) -} - -var xxx_messageInfo_Status proto.InternalMessageInfo - -func (m *Status) GetCode() int32 { - if m != nil { - return m.Code - } - return 0 -} - -func (m *Status) GetMessage() string { - if m != nil { - return m.Message - } - return "" -} - -func (m *Status) GetDetails() []*any.Any { - if m != nil { - return m.Details - } - return nil -} - -func init() { - proto.RegisterType((*Status)(nil), "google.rpc.Status") -} - -func init() { proto.RegisterFile("google/rpc/status.proto", fileDescriptor_status_c656c685916bdf47) } - -var fileDescriptor_status_c656c685916bdf47 = []byte{ - // 209 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4f, 0xcf, 0xcf, 0x4f, - 0xcf, 0x49, 0xd5, 0x2f, 0x2a, 0x48, 0xd6, 0x2f, 0x2e, 0x49, 0x2c, 0x29, 0x2d, 0xd6, 0x2b, 0x28, - 0xca, 0x2f, 0xc9, 0x17, 0xe2, 0x82, 0x48, 0xe8, 0x15, 0x15, 0x24, 0x4b, 0x49, 0x42, 0x15, 0x81, - 0x65, 0x92, 0x4a, 0xd3, 0xf4, 0x13, 0xf3, 0x2a, 0x21, 0xca, 0x94, 0xd2, 0xb8, 0xd8, 0x82, 0xc1, - 0xda, 0x84, 0x84, 0xb8, 0x58, 0x92, 0xf3, 0x53, 0x52, 0x25, 0x18, 0x15, 0x18, 0x35, 0x58, 0x83, - 0xc0, 0x6c, 0x21, 0x09, 0x2e, 0xf6, 0xdc, 0xd4, 0xe2, 0xe2, 0xc4, 0xf4, 0x54, 0x09, 0x26, 0x05, - 0x46, 0x0d, 0xce, 0x20, 0x18, 0x57, 0x48, 0x8f, 0x8b, 0x3d, 0x25, 0xb5, 0x24, 0x31, 0x33, 0xa7, - 0x58, 0x82, 0x59, 0x81, 0x59, 0x83, 0xdb, 0x48, 0x44, 0x0f, 0x6a, 0x21, 0xcc, 0x12, 0x3d, 0xc7, - 0xbc, 0xca, 0x20, 0x98, 0x22, 0xa7, 0x38, 0x2e, 0xbe, 0xe4, 0xfc, 0x5c, 0x3d, 0x84, 0xa3, 0x9c, - 0xb8, 0x21, 0xf6, 0x06, 0x80, 0x94, 0x07, 0x30, 0x46, 0x99, 0x43, 0xa5, 0xd2, 0xf3, 0x73, 0x12, - 0xf3, 0xd2, 0xf5, 0xf2, 0x8b, 0xd2, 0xf5, 0xd3, 0x53, 0xf3, 0xc0, 0x86, 0xe9, 0x43, 0xa4, 0x12, - 0x0b, 0x32, 0x8b, 0x91, 0xfc, 0x69, 0x0d, 0xa1, 0x16, 0x31, 0x31, 0x07, 0x05, 0x38, 0x27, 0xb1, - 0x81, 0x55, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, 0xff, 0xa4, 0x53, 0xf0, 0x7c, 0x10, 0x01, 0x00, - 0x00, -} diff --git a/vendor/google.golang.org/genproto/protobuf/field_mask/field_mask.pb.go b/vendor/google.golang.org/genproto/protobuf/field_mask/field_mask.pb.go deleted file mode 100644 index ace751f68d1..00000000000 --- a/vendor/google.golang.org/genproto/protobuf/field_mask/field_mask.pb.go +++ /dev/null @@ -1,288 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: google/protobuf/field_mask.proto - -package field_mask // import "google.golang.org/genproto/protobuf/field_mask" - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -// `FieldMask` represents a set of symbolic field paths, for example: -// -// paths: "f.a" -// paths: "f.b.d" -// -// Here `f` represents a field in some root message, `a` and `b` -// fields in the message found in `f`, and `d` a field found in the -// message in `f.b`. -// -// Field masks are used to specify a subset of fields that should be -// returned by a get operation or modified by an update operation. -// Field masks also have a custom JSON encoding (see below). -// -// # Field Masks in Projections -// -// When used in the context of a projection, a response message or -// sub-message is filtered by the API to only contain those fields as -// specified in the mask. For example, if the mask in the previous -// example is applied to a response message as follows: -// -// f { -// a : 22 -// b { -// d : 1 -// x : 2 -// } -// y : 13 -// } -// z: 8 -// -// The result will not contain specific values for fields x,y and z -// (their value will be set to the default, and omitted in proto text -// output): -// -// -// f { -// a : 22 -// b { -// d : 1 -// } -// } -// -// A repeated field is not allowed except at the last position of a -// paths string. -// -// If a FieldMask object is not present in a get operation, the -// operation applies to all fields (as if a FieldMask of all fields -// had been specified). -// -// Note that a field mask does not necessarily apply to the -// top-level response message. In case of a REST get operation, the -// field mask applies directly to the response, but in case of a REST -// list operation, the mask instead applies to each individual message -// in the returned resource list. In case of a REST custom method, -// other definitions may be used. Where the mask applies will be -// clearly documented together with its declaration in the API. In -// any case, the effect on the returned resource/resources is required -// behavior for APIs. -// -// # Field Masks in Update Operations -// -// A field mask in update operations specifies which fields of the -// targeted resource are going to be updated. The API is required -// to only change the values of the fields as specified in the mask -// and leave the others untouched. If a resource is passed in to -// describe the updated values, the API ignores the values of all -// fields not covered by the mask. -// -// If a repeated field is specified for an update operation, the existing -// repeated values in the target resource will be overwritten by the new values. -// Note that a repeated field is only allowed in the last position of a `paths` -// string. -// -// If a sub-message is specified in the last position of the field mask for an -// update operation, then the existing sub-message in the target resource is -// overwritten. Given the target message: -// -// f { -// b { -// d : 1 -// x : 2 -// } -// c : 1 -// } -// -// And an update message: -// -// f { -// b { -// d : 10 -// } -// } -// -// then if the field mask is: -// -// paths: "f.b" -// -// then the result will be: -// -// f { -// b { -// d : 10 -// } -// c : 1 -// } -// -// However, if the update mask was: -// -// paths: "f.b.d" -// -// then the result would be: -// -// f { -// b { -// d : 10 -// x : 2 -// } -// c : 1 -// } -// -// In order to reset a field's value to the default, the field must -// be in the mask and set to the default value in the provided resource. -// Hence, in order to reset all fields of a resource, provide a default -// instance of the resource and set all fields in the mask, or do -// not provide a mask as described below. -// -// If a field mask is not present on update, the operation applies to -// all fields (as if a field mask of all fields has been specified). -// Note that in the presence of schema evolution, this may mean that -// fields the client does not know and has therefore not filled into -// the request will be reset to their default. If this is unwanted -// behavior, a specific service may require a client to always specify -// a field mask, producing an error if not. -// -// As with get operations, the location of the resource which -// describes the updated values in the request message depends on the -// operation kind. In any case, the effect of the field mask is -// required to be honored by the API. -// -// ## Considerations for HTTP REST -// -// The HTTP kind of an update operation which uses a field mask must -// be set to PATCH instead of PUT in order to satisfy HTTP semantics -// (PUT must only be used for full updates). -// -// # JSON Encoding of Field Masks -// -// In JSON, a field mask is encoded as a single string where paths are -// separated by a comma. Fields name in each path are converted -// to/from lower-camel naming conventions. -// -// As an example, consider the following message declarations: -// -// message Profile { -// User user = 1; -// Photo photo = 2; -// } -// message User { -// string display_name = 1; -// string address = 2; -// } -// -// In proto a field mask for `Profile` may look as such: -// -// mask { -// paths: "user.display_name" -// paths: "photo" -// } -// -// In JSON, the same mask is represented as below: -// -// { -// mask: "user.displayName,photo" -// } -// -// # Field Masks and Oneof Fields -// -// Field masks treat fields in oneofs just as regular fields. Consider the -// following message: -// -// message SampleMessage { -// oneof test_oneof { -// string name = 4; -// SubMessage sub_message = 9; -// } -// } -// -// The field mask can be: -// -// mask { -// paths: "name" -// } -// -// Or: -// -// mask { -// paths: "sub_message" -// } -// -// Note that oneof type names ("test_oneof" in this case) cannot be used in -// paths. -// -// ## Field Mask Verification -// -// The implementation of any API method which has a FieldMask type field in the -// request should verify the included field paths, and return an -// `INVALID_ARGUMENT` error if any path is duplicated or unmappable. -type FieldMask struct { - // The set of field mask paths. - Paths []string `protobuf:"bytes,1,rep,name=paths" json:"paths,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *FieldMask) Reset() { *m = FieldMask{} } -func (m *FieldMask) String() string { return proto.CompactTextString(m) } -func (*FieldMask) ProtoMessage() {} -func (*FieldMask) Descriptor() ([]byte, []int) { - return fileDescriptor_field_mask_56ec45e1ddac5c77, []int{0} -} -func (m *FieldMask) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_FieldMask.Unmarshal(m, b) -} -func (m *FieldMask) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_FieldMask.Marshal(b, m, deterministic) -} -func (dst *FieldMask) XXX_Merge(src proto.Message) { - xxx_messageInfo_FieldMask.Merge(dst, src) -} -func (m *FieldMask) XXX_Size() int { - return xxx_messageInfo_FieldMask.Size(m) -} -func (m *FieldMask) XXX_DiscardUnknown() { - xxx_messageInfo_FieldMask.DiscardUnknown(m) -} - -var xxx_messageInfo_FieldMask proto.InternalMessageInfo - -func (m *FieldMask) GetPaths() []string { - if m != nil { - return m.Paths - } - return nil -} - -func init() { - proto.RegisterType((*FieldMask)(nil), "google.protobuf.FieldMask") -} - -func init() { - proto.RegisterFile("google/protobuf/field_mask.proto", fileDescriptor_field_mask_56ec45e1ddac5c77) -} - -var fileDescriptor_field_mask_56ec45e1ddac5c77 = []byte{ - // 171 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x52, 0x48, 0xcf, 0xcf, 0x4f, - 0xcf, 0x49, 0xd5, 0x2f, 0x28, 0xca, 0x2f, 0xc9, 0x4f, 0x2a, 0x4d, 0xd3, 0x4f, 0xcb, 0x4c, 0xcd, - 0x49, 0x89, 0xcf, 0x4d, 0x2c, 0xce, 0xd6, 0x03, 0x8b, 0x09, 0xf1, 0x43, 0x54, 0xe8, 0xc1, 0x54, - 0x28, 0x29, 0x72, 0x71, 0xba, 0x81, 0x14, 0xf9, 0x26, 0x16, 0x67, 0x0b, 0x89, 0x70, 0xb1, 0x16, - 0x24, 0x96, 0x64, 0x14, 0x4b, 0x30, 0x2a, 0x30, 0x6b, 0x70, 0x06, 0x41, 0x38, 0x4e, 0x9d, 0x8c, - 0x5c, 0xc2, 0xc9, 0xf9, 0xb9, 0x7a, 0x68, 0x5a, 0x9d, 0xf8, 0xe0, 0x1a, 0x03, 0x40, 0x42, 0x01, - 0x8c, 0x51, 0x96, 0x50, 0x25, 0xe9, 0xf9, 0x39, 0x89, 0x79, 0xe9, 0x7a, 0xf9, 0x45, 0xe9, 0xfa, - 0xe9, 0xa9, 0x79, 0x60, 0x0d, 0xd8, 0xdc, 0x64, 0x8d, 0x60, 0x2e, 0x62, 0x62, 0x76, 0x0f, 0x70, - 0x5a, 0xc5, 0x24, 0xe7, 0x0e, 0x31, 0x21, 0x00, 0xaa, 0x5a, 0x2f, 0x3c, 0x35, 0x27, 0xc7, 0x3b, - 0x2f, 0xbf, 0x3c, 0x2f, 0xa4, 0xb2, 0x20, 0xb5, 0x38, 0x89, 0x0d, 0x6c, 0x8c, 0x31, 0x20, 0x00, - 0x00, 0xff, 0xff, 0x5a, 0xdb, 0x3a, 0xc0, 0xea, 0x00, 0x00, 0x00, -} diff --git a/vendor/google.golang.org/grpc/AUTHORS b/vendor/google.golang.org/grpc/AUTHORS deleted file mode 100644 index e491a9e7f78..00000000000 --- a/vendor/google.golang.org/grpc/AUTHORS +++ /dev/null @@ -1 +0,0 @@ -Google Inc. diff --git a/vendor/google.golang.org/grpc/LICENSE b/vendor/google.golang.org/grpc/LICENSE deleted file mode 100644 index d6456956733..00000000000 --- a/vendor/google.golang.org/grpc/LICENSE +++ /dev/null @@ -1,202 +0,0 @@ - - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - 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. diff --git a/vendor/google.golang.org/grpc/backoff.go b/vendor/google.golang.org/grpc/backoff.go deleted file mode 100644 index c40facce510..00000000000 --- a/vendor/google.golang.org/grpc/backoff.go +++ /dev/null @@ -1,96 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "math/rand" - "time" -) - -// DefaultBackoffConfig uses values specified for backoff in -// https://github.com/grpc/grpc/blob/master/doc/connection-backoff.md. -var DefaultBackoffConfig = BackoffConfig{ - MaxDelay: 120 * time.Second, - baseDelay: 1.0 * time.Second, - factor: 1.6, - jitter: 0.2, -} - -// backoffStrategy defines the methodology for backing off after a grpc -// connection failure. -// -// This is unexported until the gRPC project decides whether or not to allow -// alternative backoff strategies. Once a decision is made, this type and its -// method may be exported. -type backoffStrategy interface { - // backoff returns the amount of time to wait before the next retry given - // the number of consecutive failures. - backoff(retries int) time.Duration -} - -// BackoffConfig defines the parameters for the default gRPC backoff strategy. -type BackoffConfig struct { - // MaxDelay is the upper bound of backoff delay. - MaxDelay time.Duration - - // TODO(stevvooe): The following fields are not exported, as allowing - // changes would violate the current gRPC specification for backoff. If - // gRPC decides to allow more interesting backoff strategies, these fields - // may be opened up in the future. - - // baseDelay is the amount of time to wait before retrying after the first - // failure. - baseDelay time.Duration - - // factor is applied to the backoff after each retry. - factor float64 - - // jitter provides a range to randomize backoff delays. - jitter float64 -} - -func setDefaults(bc *BackoffConfig) { - md := bc.MaxDelay - *bc = DefaultBackoffConfig - - if md > 0 { - bc.MaxDelay = md - } -} - -func (bc BackoffConfig) backoff(retries int) time.Duration { - if retries == 0 { - return bc.baseDelay - } - backoff, max := float64(bc.baseDelay), float64(bc.MaxDelay) - for backoff < max && retries > 0 { - backoff *= bc.factor - retries-- - } - if backoff > max { - backoff = max - } - // Randomize backoff delays so that if a cluster of requests start at - // the same time, they won't operate in lockstep. - backoff *= 1 + bc.jitter*(rand.Float64()*2-1) - if backoff < 0 { - return 0 - } - return time.Duration(backoff) -} diff --git a/vendor/google.golang.org/grpc/balancer.go b/vendor/google.golang.org/grpc/balancer.go deleted file mode 100644 index e1730166cde..00000000000 --- a/vendor/google.golang.org/grpc/balancer.go +++ /dev/null @@ -1,416 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "fmt" - "net" - "sync" - - "golang.org/x/net/context" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/naming" - "google.golang.org/grpc/status" -) - -// Address represents a server the client connects to. -// -// Deprecated: please use package balancer. -type Address struct { - // Addr is the server address on which a connection will be established. - Addr string - // Metadata is the information associated with Addr, which may be used - // to make load balancing decision. - Metadata interface{} -} - -// BalancerConfig specifies the configurations for Balancer. -// -// Deprecated: please use package balancer. -type BalancerConfig struct { - // DialCreds is the transport credential the Balancer implementation can - // use to dial to a remote load balancer server. The Balancer implementations - // can ignore this if it does not need to talk to another party securely. - DialCreds credentials.TransportCredentials - // Dialer is the custom dialer the Balancer implementation can use to dial - // to a remote load balancer server. The Balancer implementations - // can ignore this if it doesn't need to talk to remote balancer. - Dialer func(context.Context, string) (net.Conn, error) -} - -// BalancerGetOptions configures a Get call. -// -// Deprecated: please use package balancer. -type BalancerGetOptions struct { - // BlockingWait specifies whether Get should block when there is no - // connected address. - BlockingWait bool -} - -// Balancer chooses network addresses for RPCs. -// -// Deprecated: please use package balancer. -type Balancer interface { - // Start does the initialization work to bootstrap a Balancer. For example, - // this function may start the name resolution and watch the updates. It will - // be called when dialing. - Start(target string, config BalancerConfig) error - // Up informs the Balancer that gRPC has a connection to the server at - // addr. It returns down which is called once the connection to addr gets - // lost or closed. - // TODO: It is not clear how to construct and take advantage of the meaningful error - // parameter for down. Need realistic demands to guide. - Up(addr Address) (down func(error)) - // Get gets the address of a server for the RPC corresponding to ctx. - // i) If it returns a connected address, gRPC internals issues the RPC on the - // connection to this address; - // ii) If it returns an address on which the connection is under construction - // (initiated by Notify(...)) but not connected, gRPC internals - // * fails RPC if the RPC is fail-fast and connection is in the TransientFailure or - // Shutdown state; - // or - // * issues RPC on the connection otherwise. - // iii) If it returns an address on which the connection does not exist, gRPC - // internals treats it as an error and will fail the corresponding RPC. - // - // Therefore, the following is the recommended rule when writing a custom Balancer. - // If opts.BlockingWait is true, it should return a connected address or - // block if there is no connected address. It should respect the timeout or - // cancellation of ctx when blocking. If opts.BlockingWait is false (for fail-fast - // RPCs), it should return an address it has notified via Notify(...) immediately - // instead of blocking. - // - // The function returns put which is called once the rpc has completed or failed. - // put can collect and report RPC stats to a remote load balancer. - // - // This function should only return the errors Balancer cannot recover by itself. - // gRPC internals will fail the RPC if an error is returned. - Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error) - // Notify returns a channel that is used by gRPC internals to watch the addresses - // gRPC needs to connect. The addresses might be from a name resolver or remote - // load balancer. gRPC internals will compare it with the existing connected - // addresses. If the address Balancer notified is not in the existing connected - // addresses, gRPC starts to connect the address. If an address in the existing - // connected addresses is not in the notification list, the corresponding connection - // is shutdown gracefully. Otherwise, there are no operations to take. Note that - // the Address slice must be the full list of the Addresses which should be connected. - // It is NOT delta. - Notify() <-chan []Address - // Close shuts down the balancer. - Close() error -} - -// downErr implements net.Error. It is constructed by gRPC internals and passed to the down -// call of Balancer. -type downErr struct { - timeout bool - temporary bool - desc string -} - -func (e downErr) Error() string { return e.desc } -func (e downErr) Timeout() bool { return e.timeout } -func (e downErr) Temporary() bool { return e.temporary } - -func downErrorf(timeout, temporary bool, format string, a ...interface{}) downErr { - return downErr{ - timeout: timeout, - temporary: temporary, - desc: fmt.Sprintf(format, a...), - } -} - -// RoundRobin returns a Balancer that selects addresses round-robin. It uses r to watch -// the name resolution updates and updates the addresses available correspondingly. -// -// Deprecated: please use package balancer/roundrobin. -func RoundRobin(r naming.Resolver) Balancer { - return &roundRobin{r: r} -} - -type addrInfo struct { - addr Address - connected bool -} - -type roundRobin struct { - r naming.Resolver - w naming.Watcher - addrs []*addrInfo // all the addresses the client should potentially connect - mu sync.Mutex - addrCh chan []Address // the channel to notify gRPC internals the list of addresses the client should connect to. - next int // index of the next address to return for Get() - waitCh chan struct{} // the channel to block when there is no connected address available - done bool // The Balancer is closed. -} - -func (rr *roundRobin) watchAddrUpdates() error { - updates, err := rr.w.Next() - if err != nil { - grpclog.Warningf("grpc: the naming watcher stops working due to %v.", err) - return err - } - rr.mu.Lock() - defer rr.mu.Unlock() - for _, update := range updates { - addr := Address{ - Addr: update.Addr, - Metadata: update.Metadata, - } - switch update.Op { - case naming.Add: - var exist bool - for _, v := range rr.addrs { - if addr == v.addr { - exist = true - grpclog.Infoln("grpc: The name resolver wanted to add an existing address: ", addr) - break - } - } - if exist { - continue - } - rr.addrs = append(rr.addrs, &addrInfo{addr: addr}) - case naming.Delete: - for i, v := range rr.addrs { - if addr == v.addr { - copy(rr.addrs[i:], rr.addrs[i+1:]) - rr.addrs = rr.addrs[:len(rr.addrs)-1] - break - } - } - default: - grpclog.Errorln("Unknown update.Op ", update.Op) - } - } - // Make a copy of rr.addrs and write it onto rr.addrCh so that gRPC internals gets notified. - open := make([]Address, len(rr.addrs)) - for i, v := range rr.addrs { - open[i] = v.addr - } - if rr.done { - return ErrClientConnClosing - } - select { - case <-rr.addrCh: - default: - } - rr.addrCh <- open - return nil -} - -func (rr *roundRobin) Start(target string, config BalancerConfig) error { - rr.mu.Lock() - defer rr.mu.Unlock() - if rr.done { - return ErrClientConnClosing - } - if rr.r == nil { - // If there is no name resolver installed, it is not needed to - // do name resolution. In this case, target is added into rr.addrs - // as the only address available and rr.addrCh stays nil. - rr.addrs = append(rr.addrs, &addrInfo{addr: Address{Addr: target}}) - return nil - } - w, err := rr.r.Resolve(target) - if err != nil { - return err - } - rr.w = w - rr.addrCh = make(chan []Address, 1) - go func() { - for { - if err := rr.watchAddrUpdates(); err != nil { - return - } - } - }() - return nil -} - -// Up sets the connected state of addr and sends notification if there are pending -// Get() calls. -func (rr *roundRobin) Up(addr Address) func(error) { - rr.mu.Lock() - defer rr.mu.Unlock() - var cnt int - for _, a := range rr.addrs { - if a.addr == addr { - if a.connected { - return nil - } - a.connected = true - } - if a.connected { - cnt++ - } - } - // addr is only one which is connected. Notify the Get() callers who are blocking. - if cnt == 1 && rr.waitCh != nil { - close(rr.waitCh) - rr.waitCh = nil - } - return func(err error) { - rr.down(addr, err) - } -} - -// down unsets the connected state of addr. -func (rr *roundRobin) down(addr Address, err error) { - rr.mu.Lock() - defer rr.mu.Unlock() - for _, a := range rr.addrs { - if addr == a.addr { - a.connected = false - break - } - } -} - -// Get returns the next addr in the rotation. -func (rr *roundRobin) Get(ctx context.Context, opts BalancerGetOptions) (addr Address, put func(), err error) { - var ch chan struct{} - rr.mu.Lock() - if rr.done { - rr.mu.Unlock() - err = ErrClientConnClosing - return - } - - if len(rr.addrs) > 0 { - if rr.next >= len(rr.addrs) { - rr.next = 0 - } - next := rr.next - for { - a := rr.addrs[next] - next = (next + 1) % len(rr.addrs) - if a.connected { - addr = a.addr - rr.next = next - rr.mu.Unlock() - return - } - if next == rr.next { - // Has iterated all the possible address but none is connected. - break - } - } - } - if !opts.BlockingWait { - if len(rr.addrs) == 0 { - rr.mu.Unlock() - err = status.Errorf(codes.Unavailable, "there is no address available") - return - } - // Returns the next addr on rr.addrs for failfast RPCs. - addr = rr.addrs[rr.next].addr - rr.next++ - rr.mu.Unlock() - return - } - // Wait on rr.waitCh for non-failfast RPCs. - if rr.waitCh == nil { - ch = make(chan struct{}) - rr.waitCh = ch - } else { - ch = rr.waitCh - } - rr.mu.Unlock() - for { - select { - case <-ctx.Done(): - err = ctx.Err() - return - case <-ch: - rr.mu.Lock() - if rr.done { - rr.mu.Unlock() - err = ErrClientConnClosing - return - } - - if len(rr.addrs) > 0 { - if rr.next >= len(rr.addrs) { - rr.next = 0 - } - next := rr.next - for { - a := rr.addrs[next] - next = (next + 1) % len(rr.addrs) - if a.connected { - addr = a.addr - rr.next = next - rr.mu.Unlock() - return - } - if next == rr.next { - // Has iterated all the possible address but none is connected. - break - } - } - } - // The newly added addr got removed by Down() again. - if rr.waitCh == nil { - ch = make(chan struct{}) - rr.waitCh = ch - } else { - ch = rr.waitCh - } - rr.mu.Unlock() - } - } -} - -func (rr *roundRobin) Notify() <-chan []Address { - return rr.addrCh -} - -func (rr *roundRobin) Close() error { - rr.mu.Lock() - defer rr.mu.Unlock() - if rr.done { - return errBalancerClosed - } - rr.done = true - if rr.w != nil { - rr.w.Close() - } - if rr.waitCh != nil { - close(rr.waitCh) - rr.waitCh = nil - } - if rr.addrCh != nil { - close(rr.addrCh) - } - return nil -} - -// pickFirst is used to test multi-addresses in one addrConn in which all addresses share the same addrConn. -// It is a wrapper around roundRobin balancer. The logic of all methods works fine because balancer.Get() -// returns the only address Up by resetTransport(). -type pickFirst struct { - *roundRobin -} - -func pickFirstBalancerV1(r naming.Resolver) Balancer { - return &pickFirst{&roundRobin{r: r}} -} diff --git a/vendor/google.golang.org/grpc/balancer/balancer.go b/vendor/google.golang.org/grpc/balancer/balancer.go deleted file mode 100644 index 63b8d71371e..00000000000 --- a/vendor/google.golang.org/grpc/balancer/balancer.go +++ /dev/null @@ -1,228 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package balancer defines APIs for load balancing in gRPC. -// All APIs in this package are experimental. -package balancer - -import ( - "errors" - "net" - "strings" - - "golang.org/x/net/context" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/resolver" -) - -var ( - // m is a map from name to balancer builder. - m = make(map[string]Builder) -) - -// Register registers the balancer builder to the balancer map. b.Name -// (lowercased) will be used as the name registered with this builder. -// -// NOTE: this function must only be called during initialization time (i.e. in -// an init() function), and is not thread-safe. If multiple Balancers are -// registered with the same name, the one registered last will take effect. -func Register(b Builder) { - m[strings.ToLower(b.Name())] = b -} - -// Get returns the resolver builder registered with the given name. -// Note that the compare is done in a case-insenstive fashion. -// If no builder is register with the name, nil will be returned. -func Get(name string) Builder { - if b, ok := m[strings.ToLower(name)]; ok { - return b - } - return nil -} - -// SubConn represents a gRPC sub connection. -// Each sub connection contains a list of addresses. gRPC will -// try to connect to them (in sequence), and stop trying the -// remainder once one connection is successful. -// -// The reconnect backoff will be applied on the list, not a single address. -// For example, try_on_all_addresses -> backoff -> try_on_all_addresses. -// -// All SubConns start in IDLE, and will not try to connect. To trigger -// the connecting, Balancers must call Connect. -// When the connection encounters an error, it will reconnect immediately. -// When the connection becomes IDLE, it will not reconnect unless Connect is -// called. -// -// This interface is to be implemented by gRPC. Users should not need a -// brand new implementation of this interface. For the situations like -// testing, the new implementation should embed this interface. This allows -// gRPC to add new methods to this interface. -type SubConn interface { - // UpdateAddresses updates the addresses used in this SubConn. - // gRPC checks if currently-connected address is still in the new list. - // If it's in the list, the connection will be kept. - // If it's not in the list, the connection will gracefully closed, and - // a new connection will be created. - // - // This will trigger a state transition for the SubConn. - UpdateAddresses([]resolver.Address) - // Connect starts the connecting for this SubConn. - Connect() -} - -// NewSubConnOptions contains options to create new SubConn. -type NewSubConnOptions struct{} - -// ClientConn represents a gRPC ClientConn. -// -// This interface is to be implemented by gRPC. Users should not need a -// brand new implementation of this interface. For the situations like -// testing, the new implementation should embed this interface. This allows -// gRPC to add new methods to this interface. -type ClientConn interface { - // NewSubConn is called by balancer to create a new SubConn. - // It doesn't block and wait for the connections to be established. - // Behaviors of the SubConn can be controlled by options. - NewSubConn([]resolver.Address, NewSubConnOptions) (SubConn, error) - // RemoveSubConn removes the SubConn from ClientConn. - // The SubConn will be shutdown. - RemoveSubConn(SubConn) - - // UpdateBalancerState is called by balancer to nofity gRPC that some internal - // state in balancer has changed. - // - // gRPC will update the connectivity state of the ClientConn, and will call pick - // on the new picker to pick new SubConn. - UpdateBalancerState(s connectivity.State, p Picker) - - // ResolveNow is called by balancer to notify gRPC to do a name resolving. - ResolveNow(resolver.ResolveNowOption) - - // Target returns the dial target for this ClientConn. - Target() string -} - -// BuildOptions contains additional information for Build. -type BuildOptions struct { - // DialCreds is the transport credential the Balancer implementation can - // use to dial to a remote load balancer server. The Balancer implementations - // can ignore this if it does not need to talk to another party securely. - DialCreds credentials.TransportCredentials - // Dialer is the custom dialer the Balancer implementation can use to dial - // to a remote load balancer server. The Balancer implementations - // can ignore this if it doesn't need to talk to remote balancer. - Dialer func(context.Context, string) (net.Conn, error) - // ChannelzParentID is the entity parent's channelz unique identification number. - ChannelzParentID int64 -} - -// Builder creates a balancer. -type Builder interface { - // Build creates a new balancer with the ClientConn. - Build(cc ClientConn, opts BuildOptions) Balancer - // Name returns the name of balancers built by this builder. - // It will be used to pick balancers (for example in service config). - Name() string -} - -// PickOptions contains addition information for the Pick operation. -type PickOptions struct{} - -// DoneInfo contains additional information for done. -type DoneInfo struct { - // Err is the rpc error the RPC finished with. It could be nil. - Err error - // BytesSent indicates if any bytes have been sent to the server. - BytesSent bool - // BytesReceived indicates if any byte has been received from the server. - BytesReceived bool -} - -var ( - // ErrNoSubConnAvailable indicates no SubConn is available for pick(). - // gRPC will block the RPC until a new picker is available via UpdateBalancerState(). - ErrNoSubConnAvailable = errors.New("no SubConn is available") - // ErrTransientFailure indicates all SubConns are in TransientFailure. - // WaitForReady RPCs will block, non-WaitForReady RPCs will fail. - ErrTransientFailure = errors.New("all SubConns are in TransientFailure") -) - -// Picker is used by gRPC to pick a SubConn to send an RPC. -// Balancer is expected to generate a new picker from its snapshot every time its -// internal state has changed. -// -// The pickers used by gRPC can be updated by ClientConn.UpdateBalancerState(). -type Picker interface { - // Pick returns the SubConn to be used to send the RPC. - // The returned SubConn must be one returned by NewSubConn(). - // - // This functions is expected to return: - // - a SubConn that is known to be READY; - // - ErrNoSubConnAvailable if no SubConn is available, but progress is being - // made (for example, some SubConn is in CONNECTING mode); - // - other errors if no active connecting is happening (for example, all SubConn - // are in TRANSIENT_FAILURE mode). - // - // If a SubConn is returned: - // - If it is READY, gRPC will send the RPC on it; - // - If it is not ready, or becomes not ready after it's returned, gRPC will block - // until UpdateBalancerState() is called and will call pick on the new picker. - // - // If the returned error is not nil: - // - If the error is ErrNoSubConnAvailable, gRPC will block until UpdateBalancerState() - // - If the error is ErrTransientFailure: - // - If the RPC is wait-for-ready, gRPC will block until UpdateBalancerState() - // is called to pick again; - // - Otherwise, RPC will fail with unavailable error. - // - Else (error is other non-nil error): - // - The RPC will fail with unavailable error. - // - // The returned done() function will be called once the rpc has finished, with the - // final status of that RPC. - // done may be nil if balancer doesn't care about the RPC status. - Pick(ctx context.Context, opts PickOptions) (conn SubConn, done func(DoneInfo), err error) -} - -// Balancer takes input from gRPC, manages SubConns, and collects and aggregates -// the connectivity states. -// -// It also generates and updates the Picker used by gRPC to pick SubConns for RPCs. -// -// HandleSubConnectionStateChange, HandleResolvedAddrs and Close are guaranteed -// to be called synchronously from the same goroutine. -// There's no guarantee on picker.Pick, it may be called anytime. -type Balancer interface { - // HandleSubConnStateChange is called by gRPC when the connectivity state - // of sc has changed. - // Balancer is expected to aggregate all the state of SubConn and report - // that back to gRPC. - // Balancer should also generate and update Pickers when its internal state has - // been changed by the new state. - HandleSubConnStateChange(sc SubConn, state connectivity.State) - // HandleResolvedAddrs is called by gRPC to send updated resolved addresses to - // balancers. - // Balancer can create new SubConn or remove SubConn with the addresses. - // An empty address slice and a non-nil error will be passed if the resolver returns - // non-nil error to gRPC. - HandleResolvedAddrs([]resolver.Address, error) - // Close closes the balancer. The balancer is not required to call - // ClientConn.RemoveSubConn for its existing SubConns. - Close() -} diff --git a/vendor/google.golang.org/grpc/balancer/base/balancer.go b/vendor/google.golang.org/grpc/balancer/base/balancer.go deleted file mode 100644 index 23d13511bb2..00000000000 --- a/vendor/google.golang.org/grpc/balancer/base/balancer.go +++ /dev/null @@ -1,208 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package base - -import ( - "golang.org/x/net/context" - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/resolver" -) - -type baseBuilder struct { - name string - pickerBuilder PickerBuilder -} - -func (bb *baseBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer { - return &baseBalancer{ - cc: cc, - pickerBuilder: bb.pickerBuilder, - - subConns: make(map[resolver.Address]balancer.SubConn), - scStates: make(map[balancer.SubConn]connectivity.State), - csEvltr: &connectivityStateEvaluator{}, - // Initialize picker to a picker that always return - // ErrNoSubConnAvailable, because when state of a SubConn changes, we - // may call UpdateBalancerState with this picker. - picker: NewErrPicker(balancer.ErrNoSubConnAvailable), - } -} - -func (bb *baseBuilder) Name() string { - return bb.name -} - -type baseBalancer struct { - cc balancer.ClientConn - pickerBuilder PickerBuilder - - csEvltr *connectivityStateEvaluator - state connectivity.State - - subConns map[resolver.Address]balancer.SubConn - scStates map[balancer.SubConn]connectivity.State - picker balancer.Picker -} - -func (b *baseBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) { - if err != nil { - grpclog.Infof("base.baseBalancer: HandleResolvedAddrs called with error %v", err) - return - } - grpclog.Infoln("base.baseBalancer: got new resolved addresses: ", addrs) - // addrsSet is the set converted from addrs, it's used for quick lookup of an address. - addrsSet := make(map[resolver.Address]struct{}) - for _, a := range addrs { - addrsSet[a] = struct{}{} - if _, ok := b.subConns[a]; !ok { - // a is a new address (not existing in b.subConns). - sc, err := b.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{}) - if err != nil { - grpclog.Warningf("base.baseBalancer: failed to create new SubConn: %v", err) - continue - } - b.subConns[a] = sc - b.scStates[sc] = connectivity.Idle - sc.Connect() - } - } - for a, sc := range b.subConns { - // a was removed by resolver. - if _, ok := addrsSet[a]; !ok { - b.cc.RemoveSubConn(sc) - delete(b.subConns, a) - // Keep the state of this sc in b.scStates until sc's state becomes Shutdown. - // The entry will be deleted in HandleSubConnStateChange. - } - } -} - -// regeneratePicker takes a snapshot of the balancer, and generates a picker -// from it. The picker is -// - errPicker with ErrTransientFailure if the balancer is in TransientFailure, -// - built by the pickerBuilder with all READY SubConns otherwise. -func (b *baseBalancer) regeneratePicker() { - if b.state == connectivity.TransientFailure { - b.picker = NewErrPicker(balancer.ErrTransientFailure) - return - } - readySCs := make(map[resolver.Address]balancer.SubConn) - - // Filter out all ready SCs from full subConn map. - for addr, sc := range b.subConns { - if st, ok := b.scStates[sc]; ok && st == connectivity.Ready { - readySCs[addr] = sc - } - } - b.picker = b.pickerBuilder.Build(readySCs) -} - -func (b *baseBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) { - grpclog.Infof("base.baseBalancer: handle SubConn state change: %p, %v", sc, s) - oldS, ok := b.scStates[sc] - if !ok { - grpclog.Infof("base.baseBalancer: got state changes for an unknown SubConn: %p, %v", sc, s) - return - } - b.scStates[sc] = s - switch s { - case connectivity.Idle: - sc.Connect() - case connectivity.Shutdown: - // When an address was removed by resolver, b called RemoveSubConn but - // kept the sc's state in scStates. Remove state for this sc here. - delete(b.scStates, sc) - } - - oldAggrState := b.state - b.state = b.csEvltr.recordTransition(oldS, s) - - // Regenerate picker when one of the following happens: - // - this sc became ready from not-ready - // - this sc became not-ready from ready - // - the aggregated state of balancer became TransientFailure from non-TransientFailure - // - the aggregated state of balancer became non-TransientFailure from TransientFailure - if (s == connectivity.Ready) != (oldS == connectivity.Ready) || - (b.state == connectivity.TransientFailure) != (oldAggrState == connectivity.TransientFailure) { - b.regeneratePicker() - } - - b.cc.UpdateBalancerState(b.state, b.picker) -} - -// Close is a nop because base balancer doesn't have internal state to clean up, -// and it doesn't need to call RemoveSubConn for the SubConns. -func (b *baseBalancer) Close() { -} - -// NewErrPicker returns a picker that always returns err on Pick(). -func NewErrPicker(err error) balancer.Picker { - return &errPicker{err: err} -} - -type errPicker struct { - err error // Pick() always returns this err. -} - -func (p *errPicker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) { - return nil, nil, p.err -} - -// connectivityStateEvaluator gets updated by addrConns when their -// states transition, based on which it evaluates the state of -// ClientConn. -type connectivityStateEvaluator struct { - numReady uint64 // Number of addrConns in ready state. - numConnecting uint64 // Number of addrConns in connecting state. - numTransientFailure uint64 // Number of addrConns in transientFailure. -} - -// recordTransition records state change happening in every subConn and based on -// that it evaluates what aggregated state should be. -// It can only transition between Ready, Connecting and TransientFailure. Other states, -// Idle and Shutdown are transitioned into by ClientConn; in the beginning of the connection -// before any subConn is created ClientConn is in idle state. In the end when ClientConn -// closes it is in Shutdown state. -// -// recordTransition should only be called synchronously from the same goroutine. -func (cse *connectivityStateEvaluator) recordTransition(oldState, newState connectivity.State) connectivity.State { - // Update counters. - for idx, state := range []connectivity.State{oldState, newState} { - updateVal := 2*uint64(idx) - 1 // -1 for oldState and +1 for new. - switch state { - case connectivity.Ready: - cse.numReady += updateVal - case connectivity.Connecting: - cse.numConnecting += updateVal - case connectivity.TransientFailure: - cse.numTransientFailure += updateVal - } - } - - // Evaluate. - if cse.numReady > 0 { - return connectivity.Ready - } - if cse.numConnecting > 0 { - return connectivity.Connecting - } - return connectivity.TransientFailure -} diff --git a/vendor/google.golang.org/grpc/balancer/base/base.go b/vendor/google.golang.org/grpc/balancer/base/base.go deleted file mode 100644 index 012ace2f2f7..00000000000 --- a/vendor/google.golang.org/grpc/balancer/base/base.go +++ /dev/null @@ -1,52 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package base defines a balancer base that can be used to build balancers with -// different picking algorithms. -// -// The base balancer creates a new SubConn for each resolved address. The -// provided picker will only be notified about READY SubConns. -// -// This package is the base of round_robin balancer, its purpose is to be used -// to build round_robin like balancers with complex picking algorithms. -// Balancers with more complicated logic should try to implement a balancer -// builder from scratch. -// -// All APIs in this package are experimental. -package base - -import ( - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/resolver" -) - -// PickerBuilder creates balancer.Picker. -type PickerBuilder interface { - // Build takes a slice of ready SubConns, and returns a picker that will be - // used by gRPC to pick a SubConn. - Build(readySCs map[resolver.Address]balancer.SubConn) balancer.Picker -} - -// NewBalancerBuilder returns a balancer builder. The balancers -// built by this builder will use the picker builder to build pickers. -func NewBalancerBuilder(name string, pb PickerBuilder) balancer.Builder { - return &baseBuilder{ - name: name, - pickerBuilder: pb, - } -} diff --git a/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go b/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go deleted file mode 100644 index 2eda0a1c210..00000000000 --- a/vendor/google.golang.org/grpc/balancer/roundrobin/roundrobin.go +++ /dev/null @@ -1,79 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package roundrobin defines a roundrobin balancer. Roundrobin balancer is -// installed as one of the default balancers in gRPC, users don't need to -// explicitly install this balancer. -package roundrobin - -import ( - "sync" - - "golang.org/x/net/context" - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/balancer/base" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/resolver" -) - -// Name is the name of round_robin balancer. -const Name = "round_robin" - -// newBuilder creates a new roundrobin balancer builder. -func newBuilder() balancer.Builder { - return base.NewBalancerBuilder(Name, &rrPickerBuilder{}) -} - -func init() { - balancer.Register(newBuilder()) -} - -type rrPickerBuilder struct{} - -func (*rrPickerBuilder) Build(readySCs map[resolver.Address]balancer.SubConn) balancer.Picker { - grpclog.Infof("roundrobinPicker: newPicker called with readySCs: %v", readySCs) - var scs []balancer.SubConn - for _, sc := range readySCs { - scs = append(scs, sc) - } - return &rrPicker{ - subConns: scs, - } -} - -type rrPicker struct { - // subConns is the snapshot of the roundrobin balancer when this picker was - // created. The slice is immutable. Each Get() will do a round robin - // selection from it and return the selected SubConn. - subConns []balancer.SubConn - - mu sync.Mutex - next int -} - -func (p *rrPicker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) { - if len(p.subConns) <= 0 { - return nil, nil, balancer.ErrNoSubConnAvailable - } - - p.mu.Lock() - sc := p.subConns[p.next] - p.next = (p.next + 1) % len(p.subConns) - p.mu.Unlock() - return sc, nil, nil -} diff --git a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go b/vendor/google.golang.org/grpc/balancer_conn_wrappers.go deleted file mode 100644 index c23f81706fb..00000000000 --- a/vendor/google.golang.org/grpc/balancer_conn_wrappers.go +++ /dev/null @@ -1,300 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "fmt" - "sync" - - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/resolver" -) - -// scStateUpdate contains the subConn and the new state it changed to. -type scStateUpdate struct { - sc balancer.SubConn - state connectivity.State -} - -// scStateUpdateBuffer is an unbounded channel for scStateChangeTuple. -// TODO make a general purpose buffer that uses interface{}. -type scStateUpdateBuffer struct { - c chan *scStateUpdate - mu sync.Mutex - backlog []*scStateUpdate -} - -func newSCStateUpdateBuffer() *scStateUpdateBuffer { - return &scStateUpdateBuffer{ - c: make(chan *scStateUpdate, 1), - } -} - -func (b *scStateUpdateBuffer) put(t *scStateUpdate) { - b.mu.Lock() - defer b.mu.Unlock() - if len(b.backlog) == 0 { - select { - case b.c <- t: - return - default: - } - } - b.backlog = append(b.backlog, t) -} - -func (b *scStateUpdateBuffer) load() { - b.mu.Lock() - defer b.mu.Unlock() - if len(b.backlog) > 0 { - select { - case b.c <- b.backlog[0]: - b.backlog[0] = nil - b.backlog = b.backlog[1:] - default: - } - } -} - -// get returns the channel that the scStateUpdate will be sent to. -// -// Upon receiving, the caller should call load to send another -// scStateChangeTuple onto the channel if there is any. -func (b *scStateUpdateBuffer) get() <-chan *scStateUpdate { - return b.c -} - -// resolverUpdate contains the new resolved addresses or error if there's -// any. -type resolverUpdate struct { - addrs []resolver.Address - err error -} - -// ccBalancerWrapper is a wrapper on top of cc for balancers. -// It implements balancer.ClientConn interface. -type ccBalancerWrapper struct { - cc *ClientConn - balancer balancer.Balancer - stateChangeQueue *scStateUpdateBuffer - resolverUpdateCh chan *resolverUpdate - done chan struct{} - - mu sync.Mutex - subConns map[*acBalancerWrapper]struct{} -} - -func newCCBalancerWrapper(cc *ClientConn, b balancer.Builder, bopts balancer.BuildOptions) *ccBalancerWrapper { - ccb := &ccBalancerWrapper{ - cc: cc, - stateChangeQueue: newSCStateUpdateBuffer(), - resolverUpdateCh: make(chan *resolverUpdate, 1), - done: make(chan struct{}), - subConns: make(map[*acBalancerWrapper]struct{}), - } - go ccb.watcher() - ccb.balancer = b.Build(ccb, bopts) - return ccb -} - -// watcher balancer functions sequentially, so the balancer can be implemented -// lock-free. -func (ccb *ccBalancerWrapper) watcher() { - for { - select { - case t := <-ccb.stateChangeQueue.get(): - ccb.stateChangeQueue.load() - select { - case <-ccb.done: - ccb.balancer.Close() - return - default: - } - ccb.balancer.HandleSubConnStateChange(t.sc, t.state) - case t := <-ccb.resolverUpdateCh: - select { - case <-ccb.done: - ccb.balancer.Close() - return - default: - } - ccb.balancer.HandleResolvedAddrs(t.addrs, t.err) - case <-ccb.done: - } - - select { - case <-ccb.done: - ccb.balancer.Close() - ccb.mu.Lock() - scs := ccb.subConns - ccb.subConns = nil - ccb.mu.Unlock() - for acbw := range scs { - ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain) - } - return - default: - } - } -} - -func (ccb *ccBalancerWrapper) close() { - close(ccb.done) -} - -func (ccb *ccBalancerWrapper) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State) { - // When updating addresses for a SubConn, if the address in use is not in - // the new addresses, the old ac will be tearDown() and a new ac will be - // created. tearDown() generates a state change with Shutdown state, we - // don't want the balancer to receive this state change. So before - // tearDown() on the old ac, ac.acbw (acWrapper) will be set to nil, and - // this function will be called with (nil, Shutdown). We don't need to call - // balancer method in this case. - if sc == nil { - return - } - ccb.stateChangeQueue.put(&scStateUpdate{ - sc: sc, - state: s, - }) -} - -func (ccb *ccBalancerWrapper) handleResolvedAddrs(addrs []resolver.Address, err error) { - select { - case <-ccb.resolverUpdateCh: - default: - } - ccb.resolverUpdateCh <- &resolverUpdate{ - addrs: addrs, - err: err, - } -} - -func (ccb *ccBalancerWrapper) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { - if len(addrs) <= 0 { - return nil, fmt.Errorf("grpc: cannot create SubConn with empty address list") - } - ccb.mu.Lock() - defer ccb.mu.Unlock() - if ccb.subConns == nil { - return nil, fmt.Errorf("grpc: ClientConn balancer wrapper was closed") - } - ac, err := ccb.cc.newAddrConn(addrs) - if err != nil { - return nil, err - } - acbw := &acBalancerWrapper{ac: ac} - acbw.ac.mu.Lock() - ac.acbw = acbw - acbw.ac.mu.Unlock() - ccb.subConns[acbw] = struct{}{} - return acbw, nil -} - -func (ccb *ccBalancerWrapper) RemoveSubConn(sc balancer.SubConn) { - acbw, ok := sc.(*acBalancerWrapper) - if !ok { - return - } - ccb.mu.Lock() - defer ccb.mu.Unlock() - if ccb.subConns == nil { - return - } - delete(ccb.subConns, acbw) - ccb.cc.removeAddrConn(acbw.getAddrConn(), errConnDrain) -} - -func (ccb *ccBalancerWrapper) UpdateBalancerState(s connectivity.State, p balancer.Picker) { - ccb.mu.Lock() - defer ccb.mu.Unlock() - if ccb.subConns == nil { - return - } - ccb.cc.csMgr.updateState(s) - ccb.cc.blockingpicker.updatePicker(p) -} - -func (ccb *ccBalancerWrapper) ResolveNow(o resolver.ResolveNowOption) { - ccb.cc.resolveNow(o) -} - -func (ccb *ccBalancerWrapper) Target() string { - return ccb.cc.target -} - -// acBalancerWrapper is a wrapper on top of ac for balancers. -// It implements balancer.SubConn interface. -type acBalancerWrapper struct { - mu sync.Mutex - ac *addrConn -} - -func (acbw *acBalancerWrapper) UpdateAddresses(addrs []resolver.Address) { - acbw.mu.Lock() - defer acbw.mu.Unlock() - if len(addrs) <= 0 { - acbw.ac.tearDown(errConnDrain) - return - } - if !acbw.ac.tryUpdateAddrs(addrs) { - cc := acbw.ac.cc - acbw.ac.mu.Lock() - // Set old ac.acbw to nil so the Shutdown state update will be ignored - // by balancer. - // - // TODO(bar) the state transition could be wrong when tearDown() old ac - // and creating new ac, fix the transition. - acbw.ac.acbw = nil - acbw.ac.mu.Unlock() - acState := acbw.ac.getState() - acbw.ac.tearDown(errConnDrain) - - if acState == connectivity.Shutdown { - return - } - - ac, err := cc.newAddrConn(addrs) - if err != nil { - grpclog.Warningf("acBalancerWrapper: UpdateAddresses: failed to newAddrConn: %v", err) - return - } - acbw.ac = ac - ac.mu.Lock() - ac.acbw = acbw - ac.mu.Unlock() - if acState != connectivity.Idle { - ac.connect() - } - } -} - -func (acbw *acBalancerWrapper) Connect() { - acbw.mu.Lock() - defer acbw.mu.Unlock() - acbw.ac.connect() -} - -func (acbw *acBalancerWrapper) getAddrConn() *addrConn { - acbw.mu.Lock() - defer acbw.mu.Unlock() - return acbw.ac -} diff --git a/vendor/google.golang.org/grpc/balancer_v1_wrapper.go b/vendor/google.golang.org/grpc/balancer_v1_wrapper.go deleted file mode 100644 index b7abc6b7457..00000000000 --- a/vendor/google.golang.org/grpc/balancer_v1_wrapper.go +++ /dev/null @@ -1,372 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "strings" - "sync" - - "golang.org/x/net/context" - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/resolver" - "google.golang.org/grpc/status" -) - -type balancerWrapperBuilder struct { - b Balancer // The v1 balancer. -} - -func (bwb *balancerWrapperBuilder) Build(cc balancer.ClientConn, opts balancer.BuildOptions) balancer.Balancer { - targetAddr := cc.Target() - targetSplitted := strings.Split(targetAddr, ":///") - if len(targetSplitted) >= 2 { - targetAddr = targetSplitted[1] - } - - bwb.b.Start(targetAddr, BalancerConfig{ - DialCreds: opts.DialCreds, - Dialer: opts.Dialer, - }) - _, pickfirst := bwb.b.(*pickFirst) - bw := &balancerWrapper{ - balancer: bwb.b, - pickfirst: pickfirst, - cc: cc, - targetAddr: targetAddr, - startCh: make(chan struct{}), - conns: make(map[resolver.Address]balancer.SubConn), - connSt: make(map[balancer.SubConn]*scState), - csEvltr: &connectivityStateEvaluator{}, - state: connectivity.Idle, - } - cc.UpdateBalancerState(connectivity.Idle, bw) - go bw.lbWatcher() - return bw -} - -func (bwb *balancerWrapperBuilder) Name() string { - return "wrapper" -} - -type scState struct { - addr Address // The v1 address type. - s connectivity.State - down func(error) -} - -type balancerWrapper struct { - balancer Balancer // The v1 balancer. - pickfirst bool - - cc balancer.ClientConn - targetAddr string // Target without the scheme. - - // To aggregate the connectivity state. - csEvltr *connectivityStateEvaluator - state connectivity.State - - mu sync.Mutex - conns map[resolver.Address]balancer.SubConn - connSt map[balancer.SubConn]*scState - // This channel is closed when handling the first resolver result. - // lbWatcher blocks until this is closed, to avoid race between - // - NewSubConn is created, cc wants to notify balancer of state changes; - // - Build hasn't return, cc doesn't have access to balancer. - startCh chan struct{} -} - -// lbWatcher watches the Notify channel of the balancer and manages -// connections accordingly. -func (bw *balancerWrapper) lbWatcher() { - <-bw.startCh - notifyCh := bw.balancer.Notify() - if notifyCh == nil { - // There's no resolver in the balancer. Connect directly. - a := resolver.Address{ - Addr: bw.targetAddr, - Type: resolver.Backend, - } - sc, err := bw.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{}) - if err != nil { - grpclog.Warningf("Error creating connection to %v. Err: %v", a, err) - } else { - bw.mu.Lock() - bw.conns[a] = sc - bw.connSt[sc] = &scState{ - addr: Address{Addr: bw.targetAddr}, - s: connectivity.Idle, - } - bw.mu.Unlock() - sc.Connect() - } - return - } - - for addrs := range notifyCh { - grpclog.Infof("balancerWrapper: got update addr from Notify: %v\n", addrs) - if bw.pickfirst { - var ( - oldA resolver.Address - oldSC balancer.SubConn - ) - bw.mu.Lock() - for oldA, oldSC = range bw.conns { - break - } - bw.mu.Unlock() - if len(addrs) <= 0 { - if oldSC != nil { - // Teardown old sc. - bw.mu.Lock() - delete(bw.conns, oldA) - delete(bw.connSt, oldSC) - bw.mu.Unlock() - bw.cc.RemoveSubConn(oldSC) - } - continue - } - - var newAddrs []resolver.Address - for _, a := range addrs { - newAddr := resolver.Address{ - Addr: a.Addr, - Type: resolver.Backend, // All addresses from balancer are all backends. - ServerName: "", - Metadata: a.Metadata, - } - newAddrs = append(newAddrs, newAddr) - } - if oldSC == nil { - // Create new sc. - sc, err := bw.cc.NewSubConn(newAddrs, balancer.NewSubConnOptions{}) - if err != nil { - grpclog.Warningf("Error creating connection to %v. Err: %v", newAddrs, err) - } else { - bw.mu.Lock() - // For pickfirst, there should be only one SubConn, so the - // address doesn't matter. All states updating (up and down) - // and picking should all happen on that only SubConn. - bw.conns[resolver.Address{}] = sc - bw.connSt[sc] = &scState{ - addr: addrs[0], // Use the first address. - s: connectivity.Idle, - } - bw.mu.Unlock() - sc.Connect() - } - } else { - bw.mu.Lock() - bw.connSt[oldSC].addr = addrs[0] - bw.mu.Unlock() - oldSC.UpdateAddresses(newAddrs) - } - } else { - var ( - add []resolver.Address // Addresses need to setup connections. - del []balancer.SubConn // Connections need to tear down. - ) - resAddrs := make(map[resolver.Address]Address) - for _, a := range addrs { - resAddrs[resolver.Address{ - Addr: a.Addr, - Type: resolver.Backend, // All addresses from balancer are all backends. - ServerName: "", - Metadata: a.Metadata, - }] = a - } - bw.mu.Lock() - for a := range resAddrs { - if _, ok := bw.conns[a]; !ok { - add = append(add, a) - } - } - for a, c := range bw.conns { - if _, ok := resAddrs[a]; !ok { - del = append(del, c) - delete(bw.conns, a) - // Keep the state of this sc in bw.connSt until its state becomes Shutdown. - } - } - bw.mu.Unlock() - for _, a := range add { - sc, err := bw.cc.NewSubConn([]resolver.Address{a}, balancer.NewSubConnOptions{}) - if err != nil { - grpclog.Warningf("Error creating connection to %v. Err: %v", a, err) - } else { - bw.mu.Lock() - bw.conns[a] = sc - bw.connSt[sc] = &scState{ - addr: resAddrs[a], - s: connectivity.Idle, - } - bw.mu.Unlock() - sc.Connect() - } - } - for _, c := range del { - bw.cc.RemoveSubConn(c) - } - } - } -} - -func (bw *balancerWrapper) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) { - bw.mu.Lock() - defer bw.mu.Unlock() - scSt, ok := bw.connSt[sc] - if !ok { - return - } - if s == connectivity.Idle { - sc.Connect() - } - oldS := scSt.s - scSt.s = s - if oldS != connectivity.Ready && s == connectivity.Ready { - scSt.down = bw.balancer.Up(scSt.addr) - } else if oldS == connectivity.Ready && s != connectivity.Ready { - if scSt.down != nil { - scSt.down(errConnClosing) - } - } - sa := bw.csEvltr.recordTransition(oldS, s) - if bw.state != sa { - bw.state = sa - } - bw.cc.UpdateBalancerState(bw.state, bw) - if s == connectivity.Shutdown { - // Remove state for this sc. - delete(bw.connSt, sc) - } -} - -func (bw *balancerWrapper) HandleResolvedAddrs([]resolver.Address, error) { - bw.mu.Lock() - defer bw.mu.Unlock() - select { - case <-bw.startCh: - default: - close(bw.startCh) - } - // There should be a resolver inside the balancer. - // All updates here, if any, are ignored. -} - -func (bw *balancerWrapper) Close() { - bw.mu.Lock() - defer bw.mu.Unlock() - select { - case <-bw.startCh: - default: - close(bw.startCh) - } - bw.balancer.Close() -} - -// The picker is the balancerWrapper itself. -// Pick should never return ErrNoSubConnAvailable. -// It either blocks or returns error, consistent with v1 balancer Get(). -func (bw *balancerWrapper) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) { - failfast := true // Default failfast is true. - if ss, ok := rpcInfoFromContext(ctx); ok { - failfast = ss.failfast - } - a, p, err := bw.balancer.Get(ctx, BalancerGetOptions{BlockingWait: !failfast}) - if err != nil { - return nil, nil, err - } - var done func(balancer.DoneInfo) - if p != nil { - done = func(i balancer.DoneInfo) { p() } - } - var sc balancer.SubConn - bw.mu.Lock() - defer bw.mu.Unlock() - if bw.pickfirst { - // Get the first sc in conns. - for _, sc = range bw.conns { - break - } - } else { - var ok bool - sc, ok = bw.conns[resolver.Address{ - Addr: a.Addr, - Type: resolver.Backend, - ServerName: "", - Metadata: a.Metadata, - }] - if !ok && failfast { - return nil, nil, status.Errorf(codes.Unavailable, "there is no connection available") - } - if s, ok := bw.connSt[sc]; failfast && (!ok || s.s != connectivity.Ready) { - // If the returned sc is not ready and RPC is failfast, - // return error, and this RPC will fail. - return nil, nil, status.Errorf(codes.Unavailable, "there is no connection available") - } - } - - return sc, done, nil -} - -// connectivityStateEvaluator gets updated by addrConns when their -// states transition, based on which it evaluates the state of -// ClientConn. -type connectivityStateEvaluator struct { - mu sync.Mutex - numReady uint64 // Number of addrConns in ready state. - numConnecting uint64 // Number of addrConns in connecting state. - numTransientFailure uint64 // Number of addrConns in transientFailure. -} - -// recordTransition records state change happening in every subConn and based on -// that it evaluates what aggregated state should be. -// It can only transition between Ready, Connecting and TransientFailure. Other states, -// Idle and Shutdown are transitioned into by ClientConn; in the beginning of the connection -// before any subConn is created ClientConn is in idle state. In the end when ClientConn -// closes it is in Shutdown state. -// TODO Note that in later releases, a ClientConn with no activity will be put into an Idle state. -func (cse *connectivityStateEvaluator) recordTransition(oldState, newState connectivity.State) connectivity.State { - cse.mu.Lock() - defer cse.mu.Unlock() - - // Update counters. - for idx, state := range []connectivity.State{oldState, newState} { - updateVal := 2*uint64(idx) - 1 // -1 for oldState and +1 for new. - switch state { - case connectivity.Ready: - cse.numReady += updateVal - case connectivity.Connecting: - cse.numConnecting += updateVal - case connectivity.TransientFailure: - cse.numTransientFailure += updateVal - } - } - - // Evaluate. - if cse.numReady > 0 { - return connectivity.Ready - } - if cse.numConnecting > 0 { - return connectivity.Connecting - } - return connectivity.TransientFailure -} diff --git a/vendor/google.golang.org/grpc/call.go b/vendor/google.golang.org/grpc/call.go deleted file mode 100644 index f73b7d5528f..00000000000 --- a/vendor/google.golang.org/grpc/call.go +++ /dev/null @@ -1,93 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "golang.org/x/net/context" -) - -// Invoke sends the RPC request on the wire and returns after response is -// received. This is typically called by generated code. -// -// All errors returned by Invoke are compatible with the status package. -func (cc *ClientConn) Invoke(ctx context.Context, method string, args, reply interface{}, opts ...CallOption) error { - // allow interceptor to see all applicable call options, which means those - // configured as defaults from dial option as well as per-call options - opts = combine(cc.dopts.callOptions, opts) - - if cc.dopts.unaryInt != nil { - return cc.dopts.unaryInt(ctx, method, args, reply, cc, invoke, opts...) - } - return invoke(ctx, method, args, reply, cc, opts...) -} - -func combine(o1 []CallOption, o2 []CallOption) []CallOption { - // we don't use append because o1 could have extra capacity whose - // elements would be overwritten, which could cause inadvertent - // sharing (and race connditions) between concurrent calls - if len(o1) == 0 { - return o2 - } else if len(o2) == 0 { - return o1 - } - ret := make([]CallOption, len(o1)+len(o2)) - copy(ret, o1) - copy(ret[len(o1):], o2) - return ret -} - -// Invoke sends the RPC request on the wire and returns after response is -// received. This is typically called by generated code. -// -// DEPRECATED: Use ClientConn.Invoke instead. -func Invoke(ctx context.Context, method string, args, reply interface{}, cc *ClientConn, opts ...CallOption) error { - return cc.Invoke(ctx, method, args, reply, opts...) -} - -var unaryStreamDesc = &StreamDesc{ServerStreams: false, ClientStreams: false} - -func invoke(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error { - // TODO: implement retries in clientStream and make this simply - // newClientStream, SendMsg, RecvMsg. - firstAttempt := true - for { - csInt, err := newClientStream(ctx, unaryStreamDesc, cc, method, opts...) - if err != nil { - return err - } - cs := csInt.(*clientStream) - if err := cs.SendMsg(req); err != nil { - if !cs.c.failFast && cs.attempt.s.Unprocessed() && firstAttempt { - // TODO: Add a field to header for grpc-transparent-retry-attempts - firstAttempt = false - continue - } - return err - } - if err := cs.RecvMsg(reply); err != nil { - if !cs.c.failFast && cs.attempt.s.Unprocessed() && firstAttempt { - // TODO: Add a field to header for grpc-transparent-retry-attempts - firstAttempt = false - continue - } - return err - } - return nil - } -} diff --git a/vendor/google.golang.org/grpc/channelz/funcs.go b/vendor/google.golang.org/grpc/channelz/funcs.go deleted file mode 100644 index 586a0336b47..00000000000 --- a/vendor/google.golang.org/grpc/channelz/funcs.go +++ /dev/null @@ -1,573 +0,0 @@ -/* - * - * Copyright 2018 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package channelz defines APIs for enabling channelz service, entry -// registration/deletion, and accessing channelz data. It also defines channelz -// metric struct formats. -// -// All APIs in this package are experimental. -package channelz - -import ( - "sort" - "sync" - "sync/atomic" - - "google.golang.org/grpc/grpclog" -) - -var ( - db dbWrapper - idGen idGenerator - // EntryPerPage defines the number of channelz entries to be shown on a web page. - EntryPerPage = 50 - curState int32 -) - -// TurnOn turns on channelz data collection. -func TurnOn() { - if !IsOn() { - NewChannelzStorage() - atomic.StoreInt32(&curState, 1) - } -} - -// IsOn returns whether channelz data collection is on. -func IsOn() bool { - return atomic.CompareAndSwapInt32(&curState, 1, 1) -} - -// dbWarpper wraps around a reference to internal channelz data storage, and -// provide synchronized functionality to set and get the reference. -type dbWrapper struct { - mu sync.RWMutex - DB *channelMap -} - -func (d *dbWrapper) set(db *channelMap) { - d.mu.Lock() - d.DB = db - d.mu.Unlock() -} - -func (d *dbWrapper) get() *channelMap { - d.mu.RLock() - defer d.mu.RUnlock() - return d.DB -} - -// NewChannelzStorage initializes channelz data storage and id generator. -// -// Note: This function is exported for testing purpose only. User should not call -// it in most cases. -func NewChannelzStorage() { - db.set(&channelMap{ - topLevelChannels: make(map[int64]struct{}), - channels: make(map[int64]*channel), - listenSockets: make(map[int64]*listenSocket), - normalSockets: make(map[int64]*normalSocket), - servers: make(map[int64]*server), - subChannels: make(map[int64]*subChannel), - }) - idGen.reset() -} - -// GetTopChannels returns a slice of top channel's ChannelMetric, along with a -// boolean indicating whether there's more top channels to be queried for. -// -// The arg id specifies that only top channel with id at or above it will be included -// in the result. The returned slice is up to a length of EntryPerPage, and is -// sorted in ascending id order. -func GetTopChannels(id int64) ([]*ChannelMetric, bool) { - return db.get().GetTopChannels(id) -} - -// GetServers returns a slice of server's ServerMetric, along with a -// boolean indicating whether there's more servers to be queried for. -// -// The arg id specifies that only server with id at or above it will be included -// in the result. The returned slice is up to a length of EntryPerPage, and is -// sorted in ascending id order. -func GetServers(id int64) ([]*ServerMetric, bool) { - return db.get().GetServers(id) -} - -// GetServerSockets returns a slice of server's (identified by id) normal socket's -// SocketMetric, along with a boolean indicating whether there's more sockets to -// be queried for. -// -// The arg startID specifies that only sockets with id at or above it will be -// included in the result. The returned slice is up to a length of EntryPerPage, -// and is sorted in ascending id order. -func GetServerSockets(id int64, startID int64) ([]*SocketMetric, bool) { - return db.get().GetServerSockets(id, startID) -} - -// GetChannel returns the ChannelMetric for the channel (identified by id). -func GetChannel(id int64) *ChannelMetric { - return db.get().GetChannel(id) -} - -// GetSubChannel returns the SubChannelMetric for the subchannel (identified by id). -func GetSubChannel(id int64) *SubChannelMetric { - return db.get().GetSubChannel(id) -} - -// GetSocket returns the SocketInternalMetric for the socket (identified by id). -func GetSocket(id int64) *SocketMetric { - return db.get().GetSocket(id) -} - -// RegisterChannel registers the given channel c in channelz database with ref -// as its reference name, and add it to the child list of its parent (identified -// by pid). pid = 0 means no parent. It returns the unique channelz tracking id -// assigned to this channel. -func RegisterChannel(c Channel, pid int64, ref string) int64 { - id := idGen.genID() - cn := &channel{ - refName: ref, - c: c, - subChans: make(map[int64]string), - nestedChans: make(map[int64]string), - id: id, - pid: pid, - } - if pid == 0 { - db.get().addChannel(id, cn, true, pid, ref) - } else { - db.get().addChannel(id, cn, false, pid, ref) - } - return id -} - -// RegisterSubChannel registers the given channel c in channelz database with ref -// as its reference name, and add it to the child list of its parent (identified -// by pid). It returns the unique channelz tracking id assigned to this subchannel. -func RegisterSubChannel(c Channel, pid int64, ref string) int64 { - if pid == 0 { - grpclog.Error("a SubChannel's parent id cannot be 0") - return 0 - } - id := idGen.genID() - sc := &subChannel{ - refName: ref, - c: c, - sockets: make(map[int64]string), - id: id, - pid: pid, - } - db.get().addSubChannel(id, sc, pid, ref) - return id -} - -// RegisterServer registers the given server s in channelz database. It returns -// the unique channelz tracking id assigned to this server. -func RegisterServer(s Server, ref string) int64 { - id := idGen.genID() - svr := &server{ - refName: ref, - s: s, - sockets: make(map[int64]string), - listenSockets: make(map[int64]string), - id: id, - } - db.get().addServer(id, svr) - return id -} - -// RegisterListenSocket registers the given listen socket s in channelz database -// with ref as its reference name, and add it to the child list of its parent -// (identified by pid). It returns the unique channelz tracking id assigned to -// this listen socket. -func RegisterListenSocket(s Socket, pid int64, ref string) int64 { - if pid == 0 { - grpclog.Error("a ListenSocket's parent id cannot be 0") - return 0 - } - id := idGen.genID() - ls := &listenSocket{refName: ref, s: s, id: id, pid: pid} - db.get().addListenSocket(id, ls, pid, ref) - return id -} - -// RegisterNormalSocket registers the given normal socket s in channelz database -// with ref as its reference name, and add it to the child list of its parent -// (identified by pid). It returns the unique channelz tracking id assigned to -// this normal socket. -func RegisterNormalSocket(s Socket, pid int64, ref string) int64 { - if pid == 0 { - grpclog.Error("a NormalSocket's parent id cannot be 0") - return 0 - } - id := idGen.genID() - ns := &normalSocket{refName: ref, s: s, id: id, pid: pid} - db.get().addNormalSocket(id, ns, pid, ref) - return id -} - -// RemoveEntry removes an entry with unique channelz trakcing id to be id from -// channelz database. -func RemoveEntry(id int64) { - db.get().removeEntry(id) -} - -// channelMap is the storage data structure for channelz. -// Methods of channelMap can be divided in two two categories with respect to locking. -// 1. Methods acquire the global lock. -// 2. Methods that can only be called when global lock is held. -// A second type of method need always to be called inside a first type of method. -type channelMap struct { - mu sync.RWMutex - topLevelChannels map[int64]struct{} - servers map[int64]*server - channels map[int64]*channel - subChannels map[int64]*subChannel - listenSockets map[int64]*listenSocket - normalSockets map[int64]*normalSocket -} - -func (c *channelMap) addServer(id int64, s *server) { - c.mu.Lock() - s.cm = c - c.servers[id] = s - c.mu.Unlock() -} - -func (c *channelMap) addChannel(id int64, cn *channel, isTopChannel bool, pid int64, ref string) { - c.mu.Lock() - cn.cm = c - c.channels[id] = cn - if isTopChannel { - c.topLevelChannels[id] = struct{}{} - } else { - c.findEntry(pid).addChild(id, cn) - } - c.mu.Unlock() -} - -func (c *channelMap) addSubChannel(id int64, sc *subChannel, pid int64, ref string) { - c.mu.Lock() - sc.cm = c - c.subChannels[id] = sc - c.findEntry(pid).addChild(id, sc) - c.mu.Unlock() -} - -func (c *channelMap) addListenSocket(id int64, ls *listenSocket, pid int64, ref string) { - c.mu.Lock() - ls.cm = c - c.listenSockets[id] = ls - c.findEntry(pid).addChild(id, ls) - c.mu.Unlock() -} - -func (c *channelMap) addNormalSocket(id int64, ns *normalSocket, pid int64, ref string) { - c.mu.Lock() - ns.cm = c - c.normalSockets[id] = ns - c.findEntry(pid).addChild(id, ns) - c.mu.Unlock() -} - -// removeEntry triggers the removal of an entry, which may not indeed delete the -// entry, if it has to wait on the deletion of its children, or may lead to a chain -// of entry deletion. For example, deleting the last socket of a gracefully shutting -// down server will lead to the server being also deleted. -func (c *channelMap) removeEntry(id int64) { - c.mu.Lock() - c.findEntry(id).triggerDelete() - c.mu.Unlock() -} - -// c.mu must be held by the caller. -func (c *channelMap) findEntry(id int64) entry { - var v entry - var ok bool - if v, ok = c.channels[id]; ok { - return v - } - if v, ok = c.subChannels[id]; ok { - return v - } - if v, ok = c.servers[id]; ok { - return v - } - if v, ok = c.listenSockets[id]; ok { - return v - } - if v, ok = c.normalSockets[id]; ok { - return v - } - return &dummyEntry{idNotFound: id} -} - -// c.mu must be held by the caller -// deleteEntry simply deletes an entry from the channelMap. Before calling this -// method, caller must check this entry is ready to be deleted, i.e removeEntry() -// has been called on it, and no children still exist. -// Conditionals are ordered by the expected frequency of deletion of each entity -// type, in order to optimize performance. -func (c *channelMap) deleteEntry(id int64) { - var ok bool - if _, ok = c.normalSockets[id]; ok { - delete(c.normalSockets, id) - return - } - if _, ok = c.subChannels[id]; ok { - delete(c.subChannels, id) - return - } - if _, ok = c.channels[id]; ok { - delete(c.channels, id) - delete(c.topLevelChannels, id) - return - } - if _, ok = c.listenSockets[id]; ok { - delete(c.listenSockets, id) - return - } - if _, ok = c.servers[id]; ok { - delete(c.servers, id) - return - } -} - -type int64Slice []int64 - -func (s int64Slice) Len() int { return len(s) } -func (s int64Slice) Swap(i, j int) { s[i], s[j] = s[j], s[i] } -func (s int64Slice) Less(i, j int) bool { return s[i] < s[j] } - -func copyMap(m map[int64]string) map[int64]string { - n := make(map[int64]string) - for k, v := range m { - n[k] = v - } - return n -} - -func min(a, b int) int { - if a < b { - return a - } - return b -} - -func (c *channelMap) GetTopChannels(id int64) ([]*ChannelMetric, bool) { - c.mu.RLock() - l := len(c.topLevelChannels) - ids := make([]int64, 0, l) - cns := make([]*channel, 0, min(l, EntryPerPage)) - - for k := range c.topLevelChannels { - ids = append(ids, k) - } - sort.Sort(int64Slice(ids)) - idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id }) - count := 0 - var end bool - var t []*ChannelMetric - for i, v := range ids[idx:] { - if count == EntryPerPage { - break - } - if cn, ok := c.channels[v]; ok { - cns = append(cns, cn) - t = append(t, &ChannelMetric{ - NestedChans: copyMap(cn.nestedChans), - SubChans: copyMap(cn.subChans), - }) - count++ - } - if i == len(ids[idx:])-1 { - end = true - break - } - } - c.mu.RUnlock() - if count == 0 { - end = true - } - - for i, cn := range cns { - t[i].ChannelData = cn.c.ChannelzMetric() - t[i].ID = cn.id - t[i].RefName = cn.refName - } - return t, end -} - -func (c *channelMap) GetServers(id int64) ([]*ServerMetric, bool) { - c.mu.RLock() - l := len(c.servers) - ids := make([]int64, 0, l) - ss := make([]*server, 0, min(l, EntryPerPage)) - for k := range c.servers { - ids = append(ids, k) - } - sort.Sort(int64Slice(ids)) - idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id }) - count := 0 - var end bool - var s []*ServerMetric - for i, v := range ids[idx:] { - if count == EntryPerPage { - break - } - if svr, ok := c.servers[v]; ok { - ss = append(ss, svr) - s = append(s, &ServerMetric{ - ListenSockets: copyMap(svr.listenSockets), - }) - count++ - } - if i == len(ids[idx:])-1 { - end = true - break - } - } - c.mu.RUnlock() - if count == 0 { - end = true - } - - for i, svr := range ss { - s[i].ServerData = svr.s.ChannelzMetric() - s[i].ID = svr.id - s[i].RefName = svr.refName - } - return s, end -} - -func (c *channelMap) GetServerSockets(id int64, startID int64) ([]*SocketMetric, bool) { - var svr *server - var ok bool - c.mu.RLock() - if svr, ok = c.servers[id]; !ok { - // server with id doesn't exist. - c.mu.RUnlock() - return nil, true - } - svrskts := svr.sockets - l := len(svrskts) - ids := make([]int64, 0, l) - sks := make([]*normalSocket, 0, min(l, EntryPerPage)) - for k := range svrskts { - ids = append(ids, k) - } - sort.Sort((int64Slice(ids))) - idx := sort.Search(len(ids), func(i int) bool { return ids[i] >= id }) - count := 0 - var end bool - for i, v := range ids[idx:] { - if count == EntryPerPage { - break - } - if ns, ok := c.normalSockets[v]; ok { - sks = append(sks, ns) - count++ - } - if i == len(ids[idx:])-1 { - end = true - break - } - } - c.mu.RUnlock() - if count == 0 { - end = true - } - var s []*SocketMetric - for _, ns := range sks { - sm := &SocketMetric{} - sm.SocketData = ns.s.ChannelzMetric() - sm.ID = ns.id - sm.RefName = ns.refName - s = append(s, sm) - } - return s, end -} - -func (c *channelMap) GetChannel(id int64) *ChannelMetric { - cm := &ChannelMetric{} - var cn *channel - var ok bool - c.mu.RLock() - if cn, ok = c.channels[id]; !ok { - // channel with id doesn't exist. - c.mu.RUnlock() - return nil - } - cm.NestedChans = copyMap(cn.nestedChans) - cm.SubChans = copyMap(cn.subChans) - c.mu.RUnlock() - cm.ChannelData = cn.c.ChannelzMetric() - cm.ID = cn.id - cm.RefName = cn.refName - return cm -} - -func (c *channelMap) GetSubChannel(id int64) *SubChannelMetric { - cm := &SubChannelMetric{} - var sc *subChannel - var ok bool - c.mu.RLock() - if sc, ok = c.subChannels[id]; !ok { - // subchannel with id doesn't exist. - c.mu.RUnlock() - return nil - } - cm.Sockets = copyMap(sc.sockets) - c.mu.RUnlock() - cm.ChannelData = sc.c.ChannelzMetric() - cm.ID = sc.id - cm.RefName = sc.refName - return cm -} - -func (c *channelMap) GetSocket(id int64) *SocketMetric { - sm := &SocketMetric{} - c.mu.RLock() - if ls, ok := c.listenSockets[id]; ok { - c.mu.RUnlock() - sm.SocketData = ls.s.ChannelzMetric() - sm.ID = ls.id - sm.RefName = ls.refName - return sm - } - if ns, ok := c.normalSockets[id]; ok { - c.mu.RUnlock() - sm.SocketData = ns.s.ChannelzMetric() - sm.ID = ns.id - sm.RefName = ns.refName - return sm - } - c.mu.RUnlock() - return nil -} - -type idGenerator struct { - id int64 -} - -func (i *idGenerator) reset() { - atomic.StoreInt64(&i.id, 0) -} - -func (i *idGenerator) genID() int64 { - return atomic.AddInt64(&i.id, 1) -} diff --git a/vendor/google.golang.org/grpc/channelz/types.go b/vendor/google.golang.org/grpc/channelz/types.go deleted file mode 100644 index 153d75340e4..00000000000 --- a/vendor/google.golang.org/grpc/channelz/types.go +++ /dev/null @@ -1,418 +0,0 @@ -/* - * - * Copyright 2018 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package channelz - -import ( - "net" - "time" - - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/grpclog" -) - -// entry represents a node in the channelz database. -type entry interface { - // addChild adds a child e, whose channelz id is id to child list - addChild(id int64, e entry) - // deleteChild deletes a child with channelz id to be id from child list - deleteChild(id int64) - // triggerDelete tries to delete self from channelz database. However, if child - // list is not empty, then deletion from the database is on hold until the last - // child is deleted from database. - triggerDelete() - // deleteSelfIfReady check whether triggerDelete() has been called before, and whether child - // list is now empty. If both conditions are met, then delete self from database. - deleteSelfIfReady() -} - -// dummyEntry is a fake entry to handle entry not found case. -type dummyEntry struct { - idNotFound int64 -} - -func (d *dummyEntry) addChild(id int64, e entry) { - // Note: It is possible for a normal program to reach here under race condition. - // For example, there could be a race between ClientConn.Close() info being propagated - // to addrConn and http2Client. ClientConn.Close() cancel the context and result - // in http2Client to error. The error info is then caught by transport monitor - // and before addrConn.tearDown() is called in side ClientConn.Close(). Therefore, - // the addrConn will create a new transport. And when registering the new transport in - // channelz, its parent addrConn could have already been torn down and deleted - // from channelz tracking, and thus reach the code here. - grpclog.Infof("attempt to add child of type %T with id %d to a parent (id=%d) that doesn't currently exist", e, id, d.idNotFound) -} - -func (d *dummyEntry) deleteChild(id int64) { - // It is possible for a normal program to reach here under race condition. - // Refer to the example described in addChild(). - grpclog.Infof("attempt to delete child with id %d from a parent (id=%d) that doesn't currently exist", id, d.idNotFound) -} - -func (d *dummyEntry) triggerDelete() { - grpclog.Warningf("attempt to delete an entry (id=%d) that doesn't currently exist", d.idNotFound) -} - -func (*dummyEntry) deleteSelfIfReady() { - // code should not reach here. deleteSelfIfReady is always called on an existing entry. -} - -// ChannelMetric defines the info channelz provides for a specific Channel, which -// includes ChannelInternalMetric and channelz-specific data, such as channelz id, -// child list, etc. -type ChannelMetric struct { - // ID is the channelz id of this channel. - ID int64 - // RefName is the human readable reference string of this channel. - RefName string - // ChannelData contains channel internal metric reported by the channel through - // ChannelzMetric(). - ChannelData *ChannelInternalMetric - // NestedChans tracks the nested channel type children of this channel in the format of - // a map from nested channel channelz id to corresponding reference string. - NestedChans map[int64]string - // SubChans tracks the subchannel type children of this channel in the format of a - // map from subchannel channelz id to corresponding reference string. - SubChans map[int64]string - // Sockets tracks the socket type children of this channel in the format of a map - // from socket channelz id to corresponding reference string. - // Note current grpc implementation doesn't allow channel having sockets directly, - // therefore, this is field is unused. - Sockets map[int64]string -} - -// SubChannelMetric defines the info channelz provides for a specific SubChannel, -// which includes ChannelInternalMetric and channelz-specific data, such as -// channelz id, child list, etc. -type SubChannelMetric struct { - // ID is the channelz id of this subchannel. - ID int64 - // RefName is the human readable reference string of this subchannel. - RefName string - // ChannelData contains subchannel internal metric reported by the subchannel - // through ChannelzMetric(). - ChannelData *ChannelInternalMetric - // NestedChans tracks the nested channel type children of this subchannel in the format of - // a map from nested channel channelz id to corresponding reference string. - // Note current grpc implementation doesn't allow subchannel to have nested channels - // as children, therefore, this field is unused. - NestedChans map[int64]string - // SubChans tracks the subchannel type children of this subchannel in the format of a - // map from subchannel channelz id to corresponding reference string. - // Note current grpc implementation doesn't allow subchannel to have subchannels - // as children, therefore, this field is unused. - SubChans map[int64]string - // Sockets tracks the socket type children of this subchannel in the format of a map - // from socket channelz id to corresponding reference string. - Sockets map[int64]string -} - -// ChannelInternalMetric defines the struct that the implementor of Channel interface -// should return from ChannelzMetric(). -type ChannelInternalMetric struct { - // current connectivity state of the channel. - State connectivity.State - // The target this channel originally tried to connect to. May be absent - Target string - // The number of calls started on the channel. - CallsStarted int64 - // The number of calls that have completed with an OK status. - CallsSucceeded int64 - // The number of calls that have a completed with a non-OK status. - CallsFailed int64 - // The last time a call was started on the channel. - LastCallStartedTimestamp time.Time - //TODO: trace -} - -// Channel is the interface that should be satisfied in order to be tracked by -// channelz as Channel or SubChannel. -type Channel interface { - ChannelzMetric() *ChannelInternalMetric -} - -type channel struct { - refName string - c Channel - closeCalled bool - nestedChans map[int64]string - subChans map[int64]string - id int64 - pid int64 - cm *channelMap -} - -func (c *channel) addChild(id int64, e entry) { - switch v := e.(type) { - case *subChannel: - c.subChans[id] = v.refName - case *channel: - c.nestedChans[id] = v.refName - default: - grpclog.Errorf("cannot add a child (id = %d) of type %T to a channel", id, e) - } -} - -func (c *channel) deleteChild(id int64) { - delete(c.subChans, id) - delete(c.nestedChans, id) - c.deleteSelfIfReady() -} - -func (c *channel) triggerDelete() { - c.closeCalled = true - c.deleteSelfIfReady() -} - -func (c *channel) deleteSelfIfReady() { - if !c.closeCalled || len(c.subChans)+len(c.nestedChans) != 0 { - return - } - c.cm.deleteEntry(c.id) - // not top channel - if c.pid != 0 { - c.cm.findEntry(c.pid).deleteChild(c.id) - } -} - -type subChannel struct { - refName string - c Channel - closeCalled bool - sockets map[int64]string - id int64 - pid int64 - cm *channelMap -} - -func (sc *subChannel) addChild(id int64, e entry) { - if v, ok := e.(*normalSocket); ok { - sc.sockets[id] = v.refName - } else { - grpclog.Errorf("cannot add a child (id = %d) of type %T to a subChannel", id, e) - } -} - -func (sc *subChannel) deleteChild(id int64) { - delete(sc.sockets, id) - sc.deleteSelfIfReady() -} - -func (sc *subChannel) triggerDelete() { - sc.closeCalled = true - sc.deleteSelfIfReady() -} - -func (sc *subChannel) deleteSelfIfReady() { - if !sc.closeCalled || len(sc.sockets) != 0 { - return - } - sc.cm.deleteEntry(sc.id) - sc.cm.findEntry(sc.pid).deleteChild(sc.id) -} - -// SocketMetric defines the info channelz provides for a specific Socket, which -// includes SocketInternalMetric and channelz-specific data, such as channelz id, etc. -type SocketMetric struct { - // ID is the channelz id of this socket. - ID int64 - // RefName is the human readable reference string of this socket. - RefName string - // SocketData contains socket internal metric reported by the socket through - // ChannelzMetric(). - SocketData *SocketInternalMetric -} - -// SocketInternalMetric defines the struct that the implementor of Socket interface -// should return from ChannelzMetric(). -type SocketInternalMetric struct { - // The number of streams that have been started. - StreamsStarted int64 - // The number of streams that have ended successfully: - // On client side, receiving frame with eos bit set. - // On server side, sending frame with eos bit set. - StreamsSucceeded int64 - // The number of streams that have ended unsuccessfully: - // On client side, termination without receiving frame with eos bit set. - // On server side, termination without sending frame with eos bit set. - StreamsFailed int64 - // The number of messages successfully sent on this socket. - MessagesSent int64 - MessagesReceived int64 - // The number of keep alives sent. This is typically implemented with HTTP/2 - // ping messages. - KeepAlivesSent int64 - // The last time a stream was created by this endpoint. Usually unset for - // servers. - LastLocalStreamCreatedTimestamp time.Time - // The last time a stream was created by the remote endpoint. Usually unset - // for clients. - LastRemoteStreamCreatedTimestamp time.Time - // The last time a message was sent by this endpoint. - LastMessageSentTimestamp time.Time - // The last time a message was received by this endpoint. - LastMessageReceivedTimestamp time.Time - // The amount of window, granted to the local endpoint by the remote endpoint. - // This may be slightly out of date due to network latency. This does NOT - // include stream level or TCP level flow control info. - LocalFlowControlWindow int64 - // The amount of window, granted to the remote endpoint by the local endpoint. - // This may be slightly out of date due to network latency. This does NOT - // include stream level or TCP level flow control info. - RemoteFlowControlWindow int64 - // The locally bound address. - LocalAddr net.Addr - // The remote bound address. May be absent. - RemoteAddr net.Addr - // Optional, represents the name of the remote endpoint, if different than - // the original target name. - RemoteName string - //TODO: socket options - //TODO: Security -} - -// Socket is the interface that should be satisfied in order to be tracked by -// channelz as Socket. -type Socket interface { - ChannelzMetric() *SocketInternalMetric -} - -type listenSocket struct { - refName string - s Socket - id int64 - pid int64 - cm *channelMap -} - -func (ls *listenSocket) addChild(id int64, e entry) { - grpclog.Errorf("cannot add a child (id = %d) of type %T to a listen socket", id, e) -} - -func (ls *listenSocket) deleteChild(id int64) { - grpclog.Errorf("cannot delete a child (id = %d) from a listen socket", id) -} - -func (ls *listenSocket) triggerDelete() { - ls.cm.deleteEntry(ls.id) - ls.cm.findEntry(ls.pid).deleteChild(ls.id) -} - -func (ls *listenSocket) deleteSelfIfReady() { - grpclog.Errorf("cannot call deleteSelfIfReady on a listen socket") -} - -type normalSocket struct { - refName string - s Socket - id int64 - pid int64 - cm *channelMap -} - -func (ns *normalSocket) addChild(id int64, e entry) { - grpclog.Errorf("cannot add a child (id = %d) of type %T to a normal socket", id, e) -} - -func (ns *normalSocket) deleteChild(id int64) { - grpclog.Errorf("cannot delete a child (id = %d) from a normal socket", id) -} - -func (ns *normalSocket) triggerDelete() { - ns.cm.deleteEntry(ns.id) - ns.cm.findEntry(ns.pid).deleteChild(ns.id) -} - -func (ns *normalSocket) deleteSelfIfReady() { - grpclog.Errorf("cannot call deleteSelfIfReady on a normal socket") -} - -// ServerMetric defines the info channelz provides for a specific Server, which -// includes ServerInternalMetric and channelz-specific data, such as channelz id, -// child list, etc. -type ServerMetric struct { - // ID is the channelz id of this server. - ID int64 - // RefName is the human readable reference string of this server. - RefName string - // ServerData contains server internal metric reported by the server through - // ChannelzMetric(). - ServerData *ServerInternalMetric - // ListenSockets tracks the listener socket type children of this server in the - // format of a map from socket channelz id to corresponding reference string. - ListenSockets map[int64]string -} - -// ServerInternalMetric defines the struct that the implementor of Server interface -// should return from ChannelzMetric(). -type ServerInternalMetric struct { - // The number of incoming calls started on the server. - CallsStarted int64 - // The number of incoming calls that have completed with an OK status. - CallsSucceeded int64 - // The number of incoming calls that have a completed with a non-OK status. - CallsFailed int64 - // The last time a call was started on the server. - LastCallStartedTimestamp time.Time - //TODO: trace -} - -// Server is the interface to be satisfied in order to be tracked by channelz as -// Server. -type Server interface { - ChannelzMetric() *ServerInternalMetric -} - -type server struct { - refName string - s Server - closeCalled bool - sockets map[int64]string - listenSockets map[int64]string - id int64 - cm *channelMap -} - -func (s *server) addChild(id int64, e entry) { - switch v := e.(type) { - case *normalSocket: - s.sockets[id] = v.refName - case *listenSocket: - s.listenSockets[id] = v.refName - default: - grpclog.Errorf("cannot add a child (id = %d) of type %T to a server", id, e) - } -} - -func (s *server) deleteChild(id int64) { - delete(s.sockets, id) - delete(s.listenSockets, id) - s.deleteSelfIfReady() -} - -func (s *server) triggerDelete() { - s.closeCalled = true - s.deleteSelfIfReady() -} - -func (s *server) deleteSelfIfReady() { - if !s.closeCalled || len(s.sockets)+len(s.listenSockets) != 0 { - return - } - s.cm.deleteEntry(s.id) -} diff --git a/vendor/google.golang.org/grpc/clientconn.go b/vendor/google.golang.org/grpc/clientconn.go deleted file mode 100644 index e8d95b43b74..00000000000 --- a/vendor/google.golang.org/grpc/clientconn.go +++ /dev/null @@ -1,1591 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "errors" - "fmt" - "math" - "net" - "reflect" - "strings" - "sync" - "time" - - "golang.org/x/net/context" - "golang.org/x/net/trace" - "google.golang.org/grpc/balancer" - _ "google.golang.org/grpc/balancer/roundrobin" // To register roundrobin. - "google.golang.org/grpc/channelz" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/keepalive" - "google.golang.org/grpc/resolver" - _ "google.golang.org/grpc/resolver/dns" // To register dns resolver. - _ "google.golang.org/grpc/resolver/passthrough" // To register passthrough resolver. - "google.golang.org/grpc/stats" - "google.golang.org/grpc/status" - "google.golang.org/grpc/transport" -) - -const ( - // minimum time to give a connection to complete - minConnectTimeout = 20 * time.Second -) - -var ( - // ErrClientConnClosing indicates that the operation is illegal because - // the ClientConn is closing. - // - // Deprecated: this error should not be relied upon by users; use the status - // code of Canceled instead. - ErrClientConnClosing = status.Error(codes.Canceled, "grpc: the client connection is closing") - // errConnDrain indicates that the connection starts to be drained and does not accept any new RPCs. - errConnDrain = errors.New("grpc: the connection is drained") - // errConnClosing indicates that the connection is closing. - errConnClosing = errors.New("grpc: the connection is closing") - // errConnUnavailable indicates that the connection is unavailable. - errConnUnavailable = errors.New("grpc: the connection is unavailable") - // errBalancerClosed indicates that the balancer is closed. - errBalancerClosed = errors.New("grpc: balancer is closed") - // We use an accessor so that minConnectTimeout can be - // atomically read and updated while testing. - getMinConnectTimeout = func() time.Duration { - return minConnectTimeout - } -) - -// The following errors are returned from Dial and DialContext -var ( - // errNoTransportSecurity indicates that there is no transport security - // being set for ClientConn. Users should either set one or explicitly - // call WithInsecure DialOption to disable security. - errNoTransportSecurity = errors.New("grpc: no transport security set (use grpc.WithInsecure() explicitly or set credentials)") - // errTransportCredentialsMissing indicates that users want to transmit security - // information (e.g., oauth2 token) which requires secure connection on an insecure - // connection. - errTransportCredentialsMissing = errors.New("grpc: the credentials require transport level security (use grpc.WithTransportCredentials() to set)") - // errCredentialsConflict indicates that grpc.WithTransportCredentials() - // and grpc.WithInsecure() are both called for a connection. - errCredentialsConflict = errors.New("grpc: transport credentials are set for an insecure connection (grpc.WithTransportCredentials() and grpc.WithInsecure() are both called)") - // errNetworkIO indicates that the connection is down due to some network I/O error. - errNetworkIO = errors.New("grpc: failed with network I/O error") -) - -// dialOptions configure a Dial call. dialOptions are set by the DialOption -// values passed to Dial. -type dialOptions struct { - unaryInt UnaryClientInterceptor - streamInt StreamClientInterceptor - cp Compressor - dc Decompressor - bs backoffStrategy - block bool - insecure bool - timeout time.Duration - scChan <-chan ServiceConfig - copts transport.ConnectOptions - callOptions []CallOption - // This is used by v1 balancer dial option WithBalancer to support v1 - // balancer, and also by WithBalancerName dial option. - balancerBuilder balancer.Builder - // This is to support grpclb. - resolverBuilder resolver.Builder - waitForHandshake bool - channelzParentID int64 - disableServiceConfig bool -} - -const ( - defaultClientMaxReceiveMessageSize = 1024 * 1024 * 4 - defaultClientMaxSendMessageSize = math.MaxInt32 -) - -// RegisterChannelz turns on channelz service. -// This is an EXPERIMENTAL API. -func RegisterChannelz() { - channelz.TurnOn() -} - -// DialOption configures how we set up the connection. -type DialOption func(*dialOptions) - -// WithWaitForHandshake blocks until the initial settings frame is received from the -// server before assigning RPCs to the connection. -// Experimental API. -func WithWaitForHandshake() DialOption { - return func(o *dialOptions) { - o.waitForHandshake = true - } -} - -// WithWriteBufferSize lets you set the size of write buffer, this determines how much data can be batched -// before doing a write on the wire. -func WithWriteBufferSize(s int) DialOption { - return func(o *dialOptions) { - o.copts.WriteBufferSize = s - } -} - -// WithReadBufferSize lets you set the size of read buffer, this determines how much data can be read at most -// for each read syscall. -func WithReadBufferSize(s int) DialOption { - return func(o *dialOptions) { - o.copts.ReadBufferSize = s - } -} - -// WithInitialWindowSize returns a DialOption which sets the value for initial window size on a stream. -// The lower bound for window size is 64K and any value smaller than that will be ignored. -func WithInitialWindowSize(s int32) DialOption { - return func(o *dialOptions) { - o.copts.InitialWindowSize = s - } -} - -// WithInitialConnWindowSize returns a DialOption which sets the value for initial window size on a connection. -// The lower bound for window size is 64K and any value smaller than that will be ignored. -func WithInitialConnWindowSize(s int32) DialOption { - return func(o *dialOptions) { - o.copts.InitialConnWindowSize = s - } -} - -// WithMaxMsgSize returns a DialOption which sets the maximum message size the client can receive. -// -// Deprecated: use WithDefaultCallOptions(MaxCallRecvMsgSize(s)) instead. -func WithMaxMsgSize(s int) DialOption { - return WithDefaultCallOptions(MaxCallRecvMsgSize(s)) -} - -// WithDefaultCallOptions returns a DialOption which sets the default CallOptions for calls over the connection. -func WithDefaultCallOptions(cos ...CallOption) DialOption { - return func(o *dialOptions) { - o.callOptions = append(o.callOptions, cos...) - } -} - -// WithCodec returns a DialOption which sets a codec for message marshaling and unmarshaling. -// -// Deprecated: use WithDefaultCallOptions(CallCustomCodec(c)) instead. -func WithCodec(c Codec) DialOption { - return WithDefaultCallOptions(CallCustomCodec(c)) -} - -// WithCompressor returns a DialOption which sets a Compressor to use for -// message compression. It has lower priority than the compressor set by -// the UseCompressor CallOption. -// -// Deprecated: use UseCompressor instead. -func WithCompressor(cp Compressor) DialOption { - return func(o *dialOptions) { - o.cp = cp - } -} - -// WithDecompressor returns a DialOption which sets a Decompressor to use for -// incoming message decompression. If incoming response messages are encoded -// using the decompressor's Type(), it will be used. Otherwise, the message -// encoding will be used to look up the compressor registered via -// encoding.RegisterCompressor, which will then be used to decompress the -// message. If no compressor is registered for the encoding, an Unimplemented -// status error will be returned. -// -// Deprecated: use encoding.RegisterCompressor instead. -func WithDecompressor(dc Decompressor) DialOption { - return func(o *dialOptions) { - o.dc = dc - } -} - -// WithBalancer returns a DialOption which sets a load balancer with the v1 API. -// Name resolver will be ignored if this DialOption is specified. -// -// Deprecated: use the new balancer APIs in balancer package and WithBalancerName. -func WithBalancer(b Balancer) DialOption { - return func(o *dialOptions) { - o.balancerBuilder = &balancerWrapperBuilder{ - b: b, - } - } -} - -// WithBalancerName sets the balancer that the ClientConn will be initialized -// with. Balancer registered with balancerName will be used. This function -// panics if no balancer was registered by balancerName. -// -// The balancer cannot be overridden by balancer option specified by service -// config. -// -// This is an EXPERIMENTAL API. -func WithBalancerName(balancerName string) DialOption { - builder := balancer.Get(balancerName) - if builder == nil { - panic(fmt.Sprintf("grpc.WithBalancerName: no balancer is registered for name %v", balancerName)) - } - return func(o *dialOptions) { - o.balancerBuilder = builder - } -} - -// withResolverBuilder is only for grpclb. -func withResolverBuilder(b resolver.Builder) DialOption { - return func(o *dialOptions) { - o.resolverBuilder = b - } -} - -// WithServiceConfig returns a DialOption which has a channel to read the service configuration. -// -// Deprecated: service config should be received through name resolver, as specified here. -// https://github.com/grpc/grpc/blob/master/doc/service_config.md -func WithServiceConfig(c <-chan ServiceConfig) DialOption { - return func(o *dialOptions) { - o.scChan = c - } -} - -// WithBackoffMaxDelay configures the dialer to use the provided maximum delay -// when backing off after failed connection attempts. -func WithBackoffMaxDelay(md time.Duration) DialOption { - return WithBackoffConfig(BackoffConfig{MaxDelay: md}) -} - -// WithBackoffConfig configures the dialer to use the provided backoff -// parameters after connection failures. -// -// Use WithBackoffMaxDelay until more parameters on BackoffConfig are opened up -// for use. -func WithBackoffConfig(b BackoffConfig) DialOption { - // Set defaults to ensure that provided BackoffConfig is valid and - // unexported fields get default values. - setDefaults(&b) - return withBackoff(b) -} - -// withBackoff sets the backoff strategy used for connectRetryNum after a -// failed connection attempt. -// -// This can be exported if arbitrary backoff strategies are allowed by gRPC. -func withBackoff(bs backoffStrategy) DialOption { - return func(o *dialOptions) { - o.bs = bs - } -} - -// WithBlock returns a DialOption which makes caller of Dial blocks until the underlying -// connection is up. Without this, Dial returns immediately and connecting the server -// happens in background. -func WithBlock() DialOption { - return func(o *dialOptions) { - o.block = true - } -} - -// WithInsecure returns a DialOption which disables transport security for this ClientConn. -// Note that transport security is required unless WithInsecure is set. -func WithInsecure() DialOption { - return func(o *dialOptions) { - o.insecure = true - } -} - -// WithTransportCredentials returns a DialOption which configures a -// connection level security credentials (e.g., TLS/SSL). -func WithTransportCredentials(creds credentials.TransportCredentials) DialOption { - return func(o *dialOptions) { - o.copts.TransportCredentials = creds - } -} - -// WithPerRPCCredentials returns a DialOption which sets -// credentials and places auth state on each outbound RPC. -func WithPerRPCCredentials(creds credentials.PerRPCCredentials) DialOption { - return func(o *dialOptions) { - o.copts.PerRPCCredentials = append(o.copts.PerRPCCredentials, creds) - } -} - -// WithTimeout returns a DialOption that configures a timeout for dialing a ClientConn -// initially. This is valid if and only if WithBlock() is present. -// -// Deprecated: use DialContext and context.WithTimeout instead. -func WithTimeout(d time.Duration) DialOption { - return func(o *dialOptions) { - o.timeout = d - } -} - -func withContextDialer(f func(context.Context, string) (net.Conn, error)) DialOption { - return func(o *dialOptions) { - o.copts.Dialer = f - } -} - -// WithDialer returns a DialOption that specifies a function to use for dialing network addresses. -// If FailOnNonTempDialError() is set to true, and an error is returned by f, gRPC checks the error's -// Temporary() method to decide if it should try to reconnect to the network address. -func WithDialer(f func(string, time.Duration) (net.Conn, error)) DialOption { - return withContextDialer( - func(ctx context.Context, addr string) (net.Conn, error) { - if deadline, ok := ctx.Deadline(); ok { - return f(addr, deadline.Sub(time.Now())) - } - return f(addr, 0) - }) -} - -// WithStatsHandler returns a DialOption that specifies the stats handler -// for all the RPCs and underlying network connections in this ClientConn. -func WithStatsHandler(h stats.Handler) DialOption { - return func(o *dialOptions) { - o.copts.StatsHandler = h - } -} - -// FailOnNonTempDialError returns a DialOption that specifies if gRPC fails on non-temporary dial errors. -// If f is true, and dialer returns a non-temporary error, gRPC will fail the connection to the network -// address and won't try to reconnect. -// The default value of FailOnNonTempDialError is false. -// This is an EXPERIMENTAL API. -func FailOnNonTempDialError(f bool) DialOption { - return func(o *dialOptions) { - o.copts.FailOnNonTempDialError = f - } -} - -// WithUserAgent returns a DialOption that specifies a user agent string for all the RPCs. -func WithUserAgent(s string) DialOption { - return func(o *dialOptions) { - o.copts.UserAgent = s - } -} - -// WithKeepaliveParams returns a DialOption that specifies keepalive parameters for the client transport. -func WithKeepaliveParams(kp keepalive.ClientParameters) DialOption { - return func(o *dialOptions) { - o.copts.KeepaliveParams = kp - } -} - -// WithUnaryInterceptor returns a DialOption that specifies the interceptor for unary RPCs. -func WithUnaryInterceptor(f UnaryClientInterceptor) DialOption { - return func(o *dialOptions) { - o.unaryInt = f - } -} - -// WithStreamInterceptor returns a DialOption that specifies the interceptor for streaming RPCs. -func WithStreamInterceptor(f StreamClientInterceptor) DialOption { - return func(o *dialOptions) { - o.streamInt = f - } -} - -// WithAuthority returns a DialOption that specifies the value to be used as -// the :authority pseudo-header. This value only works with WithInsecure and -// has no effect if TransportCredentials are present. -func WithAuthority(a string) DialOption { - return func(o *dialOptions) { - o.copts.Authority = a - } -} - -// WithChannelzParentID returns a DialOption that specifies the channelz ID of current ClientConn's -// parent. This function is used in nested channel creation (e.g. grpclb dial). -func WithChannelzParentID(id int64) DialOption { - return func(o *dialOptions) { - o.channelzParentID = id - } -} - -// WithDisableServiceConfig returns a DialOption that causes grpc to ignore any -// service config provided by the resolver and provides a hint to the resolver -// to not fetch service configs. -func WithDisableServiceConfig() DialOption { - return func(o *dialOptions) { - o.disableServiceConfig = true - } -} - -// Dial creates a client connection to the given target. -func Dial(target string, opts ...DialOption) (*ClientConn, error) { - return DialContext(context.Background(), target, opts...) -} - -// DialContext creates a client connection to the given target. By default, it's -// a non-blocking dial (the function won't wait for connections to be -// established, and connecting happens in the background). To make it a blocking -// dial, use WithBlock() dial option. -// -// In the non-blocking case, the ctx does not act against the connection. It -// only controls the setup steps. -// -// In the blocking case, ctx can be used to cancel or expire the pending -// connection. Once this function returns, the cancellation and expiration of -// ctx will be noop. Users should call ClientConn.Close to terminate all the -// pending operations after this function returns. -// -// The target name syntax is defined in -// https://github.com/grpc/grpc/blob/master/doc/naming.md. -// e.g. to use dns resolver, a "dns:///" prefix should be applied to the target. -func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error) { - cc := &ClientConn{ - target: target, - csMgr: &connectivityStateManager{}, - conns: make(map[*addrConn]struct{}), - - blockingpicker: newPickerWrapper(), - } - cc.ctx, cc.cancel = context.WithCancel(context.Background()) - - for _, opt := range opts { - opt(&cc.dopts) - } - - if channelz.IsOn() { - if cc.dopts.channelzParentID != 0 { - cc.channelzID = channelz.RegisterChannel(cc, cc.dopts.channelzParentID, target) - } else { - cc.channelzID = channelz.RegisterChannel(cc, 0, target) - } - } - - if !cc.dopts.insecure { - if cc.dopts.copts.TransportCredentials == nil { - return nil, errNoTransportSecurity - } - } else { - if cc.dopts.copts.TransportCredentials != nil { - return nil, errCredentialsConflict - } - for _, cd := range cc.dopts.copts.PerRPCCredentials { - if cd.RequireTransportSecurity() { - return nil, errTransportCredentialsMissing - } - } - } - - cc.mkp = cc.dopts.copts.KeepaliveParams - - if cc.dopts.copts.Dialer == nil { - cc.dopts.copts.Dialer = newProxyDialer( - func(ctx context.Context, addr string) (net.Conn, error) { - network, addr := parseDialTarget(addr) - return dialContext(ctx, network, addr) - }, - ) - } - - if cc.dopts.copts.UserAgent != "" { - cc.dopts.copts.UserAgent += " " + grpcUA - } else { - cc.dopts.copts.UserAgent = grpcUA - } - - if cc.dopts.timeout > 0 { - var cancel context.CancelFunc - ctx, cancel = context.WithTimeout(ctx, cc.dopts.timeout) - defer cancel() - } - - defer func() { - select { - case <-ctx.Done(): - conn, err = nil, ctx.Err() - default: - } - - if err != nil { - cc.Close() - } - }() - - scSet := false - if cc.dopts.scChan != nil { - // Try to get an initial service config. - select { - case sc, ok := <-cc.dopts.scChan: - if ok { - cc.sc = sc - scSet = true - } - default: - } - } - if cc.dopts.bs == nil { - cc.dopts.bs = DefaultBackoffConfig - } - if cc.dopts.resolverBuilder == nil { - // Only try to parse target when resolver builder is not already set. - cc.parsedTarget = parseTarget(cc.target) - grpclog.Infof("parsed scheme: %q", cc.parsedTarget.Scheme) - cc.dopts.resolverBuilder = resolver.Get(cc.parsedTarget.Scheme) - if cc.dopts.resolverBuilder == nil { - // If resolver builder is still nil, the parse target's scheme is - // not registered. Fallback to default resolver and set Endpoint to - // the original unparsed target. - grpclog.Infof("scheme %q not registered, fallback to default scheme", cc.parsedTarget.Scheme) - cc.parsedTarget = resolver.Target{ - Scheme: resolver.GetDefaultScheme(), - Endpoint: target, - } - cc.dopts.resolverBuilder = resolver.Get(cc.parsedTarget.Scheme) - } - } else { - cc.parsedTarget = resolver.Target{Endpoint: target} - } - creds := cc.dopts.copts.TransportCredentials - if creds != nil && creds.Info().ServerName != "" { - cc.authority = creds.Info().ServerName - } else if cc.dopts.insecure && cc.dopts.copts.Authority != "" { - cc.authority = cc.dopts.copts.Authority - } else { - // Use endpoint from "scheme://authority/endpoint" as the default - // authority for ClientConn. - cc.authority = cc.parsedTarget.Endpoint - } - - if cc.dopts.scChan != nil && !scSet { - // Blocking wait for the initial service config. - select { - case sc, ok := <-cc.dopts.scChan: - if ok { - cc.sc = sc - } - case <-ctx.Done(): - return nil, ctx.Err() - } - } - if cc.dopts.scChan != nil { - go cc.scWatcher() - } - - var credsClone credentials.TransportCredentials - if creds := cc.dopts.copts.TransportCredentials; creds != nil { - credsClone = creds.Clone() - } - cc.balancerBuildOpts = balancer.BuildOptions{ - DialCreds: credsClone, - Dialer: cc.dopts.copts.Dialer, - ChannelzParentID: cc.channelzID, - } - - // Build the resolver. - cc.resolverWrapper, err = newCCResolverWrapper(cc) - if err != nil { - return nil, fmt.Errorf("failed to build resolver: %v", err) - } - // Start the resolver wrapper goroutine after resolverWrapper is created. - // - // If the goroutine is started before resolverWrapper is ready, the - // following may happen: The goroutine sends updates to cc. cc forwards - // those to balancer. Balancer creates new addrConn. addrConn fails to - // connect, and calls resolveNow(). resolveNow() tries to use the non-ready - // resolverWrapper. - cc.resolverWrapper.start() - - // A blocking dial blocks until the clientConn is ready. - if cc.dopts.block { - for { - s := cc.GetState() - if s == connectivity.Ready { - break - } - if !cc.WaitForStateChange(ctx, s) { - // ctx got timeout or canceled. - return nil, ctx.Err() - } - } - } - - return cc, nil -} - -// connectivityStateManager keeps the connectivity.State of ClientConn. -// This struct will eventually be exported so the balancers can access it. -type connectivityStateManager struct { - mu sync.Mutex - state connectivity.State - notifyChan chan struct{} -} - -// updateState updates the connectivity.State of ClientConn. -// If there's a change it notifies goroutines waiting on state change to -// happen. -func (csm *connectivityStateManager) updateState(state connectivity.State) { - csm.mu.Lock() - defer csm.mu.Unlock() - if csm.state == connectivity.Shutdown { - return - } - if csm.state == state { - return - } - csm.state = state - if csm.notifyChan != nil { - // There are other goroutines waiting on this channel. - close(csm.notifyChan) - csm.notifyChan = nil - } -} - -func (csm *connectivityStateManager) getState() connectivity.State { - csm.mu.Lock() - defer csm.mu.Unlock() - return csm.state -} - -func (csm *connectivityStateManager) getNotifyChan() <-chan struct{} { - csm.mu.Lock() - defer csm.mu.Unlock() - if csm.notifyChan == nil { - csm.notifyChan = make(chan struct{}) - } - return csm.notifyChan -} - -// ClientConn represents a client connection to an RPC server. -type ClientConn struct { - ctx context.Context - cancel context.CancelFunc - - target string - parsedTarget resolver.Target - authority string - dopts dialOptions - csMgr *connectivityStateManager - - balancerBuildOpts balancer.BuildOptions - resolverWrapper *ccResolverWrapper - blockingpicker *pickerWrapper - - mu sync.RWMutex - sc ServiceConfig - scRaw string - conns map[*addrConn]struct{} - // Keepalive parameter can be updated if a GoAway is received. - mkp keepalive.ClientParameters - curBalancerName string - preBalancerName string // previous balancer name. - curAddresses []resolver.Address - balancerWrapper *ccBalancerWrapper - - channelzID int64 // channelz unique identification number - czmu sync.RWMutex - callsStarted int64 - callsSucceeded int64 - callsFailed int64 - lastCallStartedTime time.Time -} - -// WaitForStateChange waits until the connectivity.State of ClientConn changes from sourceState or -// ctx expires. A true value is returned in former case and false in latter. -// This is an EXPERIMENTAL API. -func (cc *ClientConn) WaitForStateChange(ctx context.Context, sourceState connectivity.State) bool { - ch := cc.csMgr.getNotifyChan() - if cc.csMgr.getState() != sourceState { - return true - } - select { - case <-ctx.Done(): - return false - case <-ch: - return true - } -} - -// GetState returns the connectivity.State of ClientConn. -// This is an EXPERIMENTAL API. -func (cc *ClientConn) GetState() connectivity.State { - return cc.csMgr.getState() -} - -func (cc *ClientConn) scWatcher() { - for { - select { - case sc, ok := <-cc.dopts.scChan: - if !ok { - return - } - cc.mu.Lock() - // TODO: load balance policy runtime change is ignored. - // We may revist this decision in the future. - cc.sc = sc - cc.scRaw = "" - cc.mu.Unlock() - case <-cc.ctx.Done(): - return - } - } -} - -func (cc *ClientConn) handleResolvedAddrs(addrs []resolver.Address, err error) { - cc.mu.Lock() - defer cc.mu.Unlock() - if cc.conns == nil { - // cc was closed. - return - } - - if reflect.DeepEqual(cc.curAddresses, addrs) { - return - } - - cc.curAddresses = addrs - - if cc.dopts.balancerBuilder == nil { - // Only look at balancer types and switch balancer if balancer dial - // option is not set. - var isGRPCLB bool - for _, a := range addrs { - if a.Type == resolver.GRPCLB { - isGRPCLB = true - break - } - } - var newBalancerName string - if isGRPCLB { - newBalancerName = grpclbName - } else { - // Address list doesn't contain grpclb address. Try to pick a - // non-grpclb balancer. - newBalancerName = cc.curBalancerName - // If current balancer is grpclb, switch to the previous one. - if newBalancerName == grpclbName { - newBalancerName = cc.preBalancerName - } - // The following could be true in two cases: - // - the first time handling resolved addresses - // (curBalancerName="") - // - the first time handling non-grpclb addresses - // (curBalancerName="grpclb", preBalancerName="") - if newBalancerName == "" { - newBalancerName = PickFirstBalancerName - } - } - cc.switchBalancer(newBalancerName) - } else if cc.balancerWrapper == nil { - // Balancer dial option was set, and this is the first time handling - // resolved addresses. Build a balancer with dopts.balancerBuilder. - cc.balancerWrapper = newCCBalancerWrapper(cc, cc.dopts.balancerBuilder, cc.balancerBuildOpts) - } - - cc.balancerWrapper.handleResolvedAddrs(addrs, nil) -} - -// switchBalancer starts the switching from current balancer to the balancer -// with the given name. -// -// It will NOT send the current address list to the new balancer. If needed, -// caller of this function should send address list to the new balancer after -// this function returns. -// -// Caller must hold cc.mu. -func (cc *ClientConn) switchBalancer(name string) { - if cc.conns == nil { - return - } - - if strings.ToLower(cc.curBalancerName) == strings.ToLower(name) { - return - } - - grpclog.Infof("ClientConn switching balancer to %q", name) - if cc.dopts.balancerBuilder != nil { - grpclog.Infoln("ignoring balancer switching: Balancer DialOption used instead") - return - } - // TODO(bar switching) change this to two steps: drain and close. - // Keep track of sc in wrapper. - if cc.balancerWrapper != nil { - cc.balancerWrapper.close() - } - // Clear all stickiness state. - cc.blockingpicker.clearStickinessState() - - builder := balancer.Get(name) - if builder == nil { - grpclog.Infof("failed to get balancer builder for: %v, using pick_first instead", name) - builder = newPickfirstBuilder() - } - cc.preBalancerName = cc.curBalancerName - cc.curBalancerName = builder.Name() - cc.balancerWrapper = newCCBalancerWrapper(cc, builder, cc.balancerBuildOpts) -} - -func (cc *ClientConn) handleSubConnStateChange(sc balancer.SubConn, s connectivity.State) { - cc.mu.Lock() - if cc.conns == nil { - cc.mu.Unlock() - return - } - // TODO(bar switching) send updates to all balancer wrappers when balancer - // gracefully switching is supported. - cc.balancerWrapper.handleSubConnStateChange(sc, s) - cc.mu.Unlock() -} - -// newAddrConn creates an addrConn for addrs and adds it to cc.conns. -// -// Caller needs to make sure len(addrs) > 0. -func (cc *ClientConn) newAddrConn(addrs []resolver.Address) (*addrConn, error) { - ac := &addrConn{ - cc: cc, - addrs: addrs, - dopts: cc.dopts, - } - ac.ctx, ac.cancel = context.WithCancel(cc.ctx) - // Track ac in cc. This needs to be done before any getTransport(...) is called. - cc.mu.Lock() - if cc.conns == nil { - cc.mu.Unlock() - return nil, ErrClientConnClosing - } - if channelz.IsOn() { - ac.channelzID = channelz.RegisterSubChannel(ac, cc.channelzID, "") - } - cc.conns[ac] = struct{}{} - cc.mu.Unlock() - return ac, nil -} - -// removeAddrConn removes the addrConn in the subConn from clientConn. -// It also tears down the ac with the given error. -func (cc *ClientConn) removeAddrConn(ac *addrConn, err error) { - cc.mu.Lock() - if cc.conns == nil { - cc.mu.Unlock() - return - } - delete(cc.conns, ac) - cc.mu.Unlock() - ac.tearDown(err) -} - -// ChannelzMetric returns ChannelInternalMetric of current ClientConn. -// This is an EXPERIMENTAL API. -func (cc *ClientConn) ChannelzMetric() *channelz.ChannelInternalMetric { - state := cc.GetState() - cc.czmu.RLock() - defer cc.czmu.RUnlock() - return &channelz.ChannelInternalMetric{ - State: state, - Target: cc.target, - CallsStarted: cc.callsStarted, - CallsSucceeded: cc.callsSucceeded, - CallsFailed: cc.callsFailed, - LastCallStartedTimestamp: cc.lastCallStartedTime, - } -} - -func (cc *ClientConn) incrCallsStarted() { - cc.czmu.Lock() - cc.callsStarted++ - // TODO(yuxuanli): will make this a time.Time pointer improve performance? - cc.lastCallStartedTime = time.Now() - cc.czmu.Unlock() -} - -func (cc *ClientConn) incrCallsSucceeded() { - cc.czmu.Lock() - cc.callsSucceeded++ - cc.czmu.Unlock() -} - -func (cc *ClientConn) incrCallsFailed() { - cc.czmu.Lock() - cc.callsFailed++ - cc.czmu.Unlock() -} - -// connect starts to creating transport and also starts the transport monitor -// goroutine for this ac. -// It does nothing if the ac is not IDLE. -// TODO(bar) Move this to the addrConn section. -// This was part of resetAddrConn, keep it here to make the diff look clean. -func (ac *addrConn) connect() error { - ac.mu.Lock() - if ac.state == connectivity.Shutdown { - ac.mu.Unlock() - return errConnClosing - } - if ac.state != connectivity.Idle { - ac.mu.Unlock() - return nil - } - ac.state = connectivity.Connecting - ac.cc.handleSubConnStateChange(ac.acbw, ac.state) - ac.mu.Unlock() - - // Start a goroutine connecting to the server asynchronously. - go func() { - if err := ac.resetTransport(); err != nil { - grpclog.Warningf("Failed to dial %s: %v; please retry.", ac.addrs[0].Addr, err) - if err != errConnClosing { - // Keep this ac in cc.conns, to get the reason it's torn down. - ac.tearDown(err) - } - return - } - ac.transportMonitor() - }() - return nil -} - -// tryUpdateAddrs tries to update ac.addrs with the new addresses list. -// -// It checks whether current connected address of ac is in the new addrs list. -// - If true, it updates ac.addrs and returns true. The ac will keep using -// the existing connection. -// - If false, it does nothing and returns false. -func (ac *addrConn) tryUpdateAddrs(addrs []resolver.Address) bool { - ac.mu.Lock() - defer ac.mu.Unlock() - grpclog.Infof("addrConn: tryUpdateAddrs curAddr: %v, addrs: %v", ac.curAddr, addrs) - if ac.state == connectivity.Shutdown { - ac.addrs = addrs - return true - } - - var curAddrFound bool - for _, a := range addrs { - if reflect.DeepEqual(ac.curAddr, a) { - curAddrFound = true - break - } - } - grpclog.Infof("addrConn: tryUpdateAddrs curAddrFound: %v", curAddrFound) - if curAddrFound { - ac.addrs = addrs - ac.reconnectIdx = 0 // Start reconnecting from beginning in the new list. - } - - return curAddrFound -} - -// GetMethodConfig gets the method config of the input method. -// If there's an exact match for input method (i.e. /service/method), we return -// the corresponding MethodConfig. -// If there isn't an exact match for the input method, we look for the default config -// under the service (i.e /service/). If there is a default MethodConfig for -// the service, we return it. -// Otherwise, we return an empty MethodConfig. -func (cc *ClientConn) GetMethodConfig(method string) MethodConfig { - // TODO: Avoid the locking here. - cc.mu.RLock() - defer cc.mu.RUnlock() - m, ok := cc.sc.Methods[method] - if !ok { - i := strings.LastIndex(method, "/") - m = cc.sc.Methods[method[:i+1]] - } - return m -} - -func (cc *ClientConn) getTransport(ctx context.Context, failfast bool) (transport.ClientTransport, func(balancer.DoneInfo), error) { - t, done, err := cc.blockingpicker.pick(ctx, failfast, balancer.PickOptions{}) - if err != nil { - return nil, nil, toRPCErr(err) - } - return t, done, nil -} - -// handleServiceConfig parses the service config string in JSON format to Go native -// struct ServiceConfig, and store both the struct and the JSON string in ClientConn. -func (cc *ClientConn) handleServiceConfig(js string) error { - if cc.dopts.disableServiceConfig { - return nil - } - sc, err := parseServiceConfig(js) - if err != nil { - return err - } - cc.mu.Lock() - cc.scRaw = js - cc.sc = sc - if sc.LB != nil && *sc.LB != grpclbName { // "grpclb" is not a valid balancer option in service config. - if cc.curBalancerName == grpclbName { - // If current balancer is grpclb, there's at least one grpclb - // balancer address in the resolved list. Don't switch the balancer, - // but change the previous balancer name, so if a new resolved - // address list doesn't contain grpclb address, balancer will be - // switched to *sc.LB. - cc.preBalancerName = *sc.LB - } else { - cc.switchBalancer(*sc.LB) - cc.balancerWrapper.handleResolvedAddrs(cc.curAddresses, nil) - } - } - - if envConfigStickinessOn { - var newStickinessMDKey string - if sc.stickinessMetadataKey != nil && *sc.stickinessMetadataKey != "" { - newStickinessMDKey = *sc.stickinessMetadataKey - } - // newStickinessMDKey is "" if one of the following happens: - // - stickinessMetadataKey is set to "" - // - stickinessMetadataKey field doesn't exist in service config - cc.blockingpicker.updateStickinessMDKey(strings.ToLower(newStickinessMDKey)) - } - - cc.mu.Unlock() - return nil -} - -func (cc *ClientConn) resolveNow(o resolver.ResolveNowOption) { - cc.mu.Lock() - r := cc.resolverWrapper - cc.mu.Unlock() - if r == nil { - return - } - go r.resolveNow(o) -} - -// Close tears down the ClientConn and all underlying connections. -func (cc *ClientConn) Close() error { - defer cc.cancel() - - cc.mu.Lock() - if cc.conns == nil { - cc.mu.Unlock() - return ErrClientConnClosing - } - conns := cc.conns - cc.conns = nil - cc.csMgr.updateState(connectivity.Shutdown) - - rWrapper := cc.resolverWrapper - cc.resolverWrapper = nil - bWrapper := cc.balancerWrapper - cc.balancerWrapper = nil - cc.mu.Unlock() - - cc.blockingpicker.close() - - if rWrapper != nil { - rWrapper.close() - } - if bWrapper != nil { - bWrapper.close() - } - - for ac := range conns { - ac.tearDown(ErrClientConnClosing) - } - if channelz.IsOn() { - channelz.RemoveEntry(cc.channelzID) - } - return nil -} - -// addrConn is a network connection to a given address. -type addrConn struct { - ctx context.Context - cancel context.CancelFunc - - cc *ClientConn - addrs []resolver.Address - dopts dialOptions - events trace.EventLog - acbw balancer.SubConn - - mu sync.Mutex - curAddr resolver.Address - reconnectIdx int // The index in addrs list to start reconnecting from. - state connectivity.State - // ready is closed and becomes nil when a new transport is up or failed - // due to timeout. - ready chan struct{} - transport transport.ClientTransport - - // The reason this addrConn is torn down. - tearDownErr error - - connectRetryNum int - // backoffDeadline is the time until which resetTransport needs to - // wait before increasing connectRetryNum count. - backoffDeadline time.Time - // connectDeadline is the time by which all connection - // negotiations must complete. - connectDeadline time.Time - - channelzID int64 // channelz unique identification number - czmu sync.RWMutex - callsStarted int64 - callsSucceeded int64 - callsFailed int64 - lastCallStartedTime time.Time -} - -// adjustParams updates parameters used to create transports upon -// receiving a GoAway. -func (ac *addrConn) adjustParams(r transport.GoAwayReason) { - switch r { - case transport.GoAwayTooManyPings: - v := 2 * ac.dopts.copts.KeepaliveParams.Time - ac.cc.mu.Lock() - if v > ac.cc.mkp.Time { - ac.cc.mkp.Time = v - } - ac.cc.mu.Unlock() - } -} - -// printf records an event in ac's event log, unless ac has been closed. -// REQUIRES ac.mu is held. -func (ac *addrConn) printf(format string, a ...interface{}) { - if ac.events != nil { - ac.events.Printf(format, a...) - } -} - -// errorf records an error in ac's event log, unless ac has been closed. -// REQUIRES ac.mu is held. -func (ac *addrConn) errorf(format string, a ...interface{}) { - if ac.events != nil { - ac.events.Errorf(format, a...) - } -} - -// resetTransport recreates a transport to the address for ac. The old -// transport will close itself on error or when the clientconn is closed. -// The created transport must receive initial settings frame from the server. -// In case that doesn't happen, transportMonitor will kill the newly created -// transport after connectDeadline has expired. -// In case there was an error on the transport before the settings frame was -// received, resetTransport resumes connecting to backends after the one that -// was previously connected to. In case end of the list is reached, resetTransport -// backs off until the original deadline. -// If the DialOption WithWaitForHandshake was set, resetTrasport returns -// successfully only after server settings are received. -// -// TODO(bar) make sure all state transitions are valid. -func (ac *addrConn) resetTransport() error { - ac.mu.Lock() - if ac.state == connectivity.Shutdown { - ac.mu.Unlock() - return errConnClosing - } - if ac.ready != nil { - close(ac.ready) - ac.ready = nil - } - ac.transport = nil - ridx := ac.reconnectIdx - ac.mu.Unlock() - ac.cc.mu.RLock() - ac.dopts.copts.KeepaliveParams = ac.cc.mkp - ac.cc.mu.RUnlock() - var backoffDeadline, connectDeadline time.Time - for connectRetryNum := 0; ; connectRetryNum++ { - ac.mu.Lock() - if ac.backoffDeadline.IsZero() { - // This means either a successful HTTP2 connection was established - // or this is the first time this addrConn is trying to establish a - // connection. - backoffFor := ac.dopts.bs.backoff(connectRetryNum) // time.Duration. - // This will be the duration that dial gets to finish. - dialDuration := getMinConnectTimeout() - if backoffFor > dialDuration { - // Give dial more time as we keep failing to connect. - dialDuration = backoffFor - } - start := time.Now() - backoffDeadline = start.Add(backoffFor) - connectDeadline = start.Add(dialDuration) - ridx = 0 // Start connecting from the beginning. - } else { - // Continue trying to connect with the same deadlines. - connectRetryNum = ac.connectRetryNum - backoffDeadline = ac.backoffDeadline - connectDeadline = ac.connectDeadline - ac.backoffDeadline = time.Time{} - ac.connectDeadline = time.Time{} - ac.connectRetryNum = 0 - } - if ac.state == connectivity.Shutdown { - ac.mu.Unlock() - return errConnClosing - } - ac.printf("connecting") - if ac.state != connectivity.Connecting { - ac.state = connectivity.Connecting - ac.cc.handleSubConnStateChange(ac.acbw, ac.state) - } - // copy ac.addrs in case of race - addrsIter := make([]resolver.Address, len(ac.addrs)) - copy(addrsIter, ac.addrs) - copts := ac.dopts.copts - ac.mu.Unlock() - connected, err := ac.createTransport(connectRetryNum, ridx, backoffDeadline, connectDeadline, addrsIter, copts) - if err != nil { - return err - } - if connected { - return nil - } - } -} - -// createTransport creates a connection to one of the backends in addrs. -// It returns true if a connection was established. -func (ac *addrConn) createTransport(connectRetryNum, ridx int, backoffDeadline, connectDeadline time.Time, addrs []resolver.Address, copts transport.ConnectOptions) (bool, error) { - for i := ridx; i < len(addrs); i++ { - addr := addrs[i] - target := transport.TargetInfo{ - Addr: addr.Addr, - Metadata: addr.Metadata, - Authority: ac.cc.authority, - } - done := make(chan struct{}) - onPrefaceReceipt := func() { - ac.mu.Lock() - close(done) - if !ac.backoffDeadline.IsZero() { - // If we haven't already started reconnecting to - // other backends. - // Note, this can happen when writer notices an error - // and triggers resetTransport while at the same time - // reader receives the preface and invokes this closure. - ac.backoffDeadline = time.Time{} - ac.connectDeadline = time.Time{} - ac.connectRetryNum = 0 - } - ac.mu.Unlock() - } - // Do not cancel in the success path because of - // this issue in Go1.6: https://github.com/golang/go/issues/15078. - connectCtx, cancel := context.WithDeadline(ac.ctx, connectDeadline) - if channelz.IsOn() { - copts.ChannelzParentID = ac.channelzID - } - newTr, err := transport.NewClientTransport(connectCtx, ac.cc.ctx, target, copts, onPrefaceReceipt) - if err != nil { - cancel() - ac.cc.blockingpicker.updateConnectionError(err) - ac.mu.Lock() - if ac.state == connectivity.Shutdown { - // ac.tearDown(...) has been invoked. - ac.mu.Unlock() - return false, errConnClosing - } - ac.mu.Unlock() - grpclog.Warningf("grpc: addrConn.createTransport failed to connect to %v. Err :%v. Reconnecting...", addr, err) - continue - } - if ac.dopts.waitForHandshake { - select { - case <-done: - case <-connectCtx.Done(): - // Didn't receive server preface, must kill this new transport now. - grpclog.Warningf("grpc: addrConn.createTransport failed to receive server preface before deadline.") - newTr.Close() - break - case <-ac.ctx.Done(): - } - } - ac.mu.Lock() - if ac.state == connectivity.Shutdown { - ac.mu.Unlock() - // ac.tearDonn(...) has been invoked. - newTr.Close() - return false, errConnClosing - } - ac.printf("ready") - ac.state = connectivity.Ready - ac.cc.handleSubConnStateChange(ac.acbw, ac.state) - ac.transport = newTr - ac.curAddr = addr - if ac.ready != nil { - close(ac.ready) - ac.ready = nil - } - select { - case <-done: - // If the server has responded back with preface already, - // don't set the reconnect parameters. - default: - ac.connectRetryNum = connectRetryNum - ac.backoffDeadline = backoffDeadline - ac.connectDeadline = connectDeadline - ac.reconnectIdx = i + 1 // Start reconnecting from the next backend in the list. - } - ac.mu.Unlock() - return true, nil - } - ac.mu.Lock() - if ac.state == connectivity.Shutdown { - ac.mu.Unlock() - return false, errConnClosing - } - ac.state = connectivity.TransientFailure - ac.cc.handleSubConnStateChange(ac.acbw, ac.state) - ac.cc.resolveNow(resolver.ResolveNowOption{}) - if ac.ready != nil { - close(ac.ready) - ac.ready = nil - } - ac.mu.Unlock() - timer := time.NewTimer(backoffDeadline.Sub(time.Now())) - select { - case <-timer.C: - case <-ac.ctx.Done(): - timer.Stop() - return false, ac.ctx.Err() - } - return false, nil -} - -// Run in a goroutine to track the error in transport and create the -// new transport if an error happens. It returns when the channel is closing. -func (ac *addrConn) transportMonitor() { - for { - var timer *time.Timer - var cdeadline <-chan time.Time - ac.mu.Lock() - t := ac.transport - if !ac.connectDeadline.IsZero() { - timer = time.NewTimer(ac.connectDeadline.Sub(time.Now())) - cdeadline = timer.C - } - ac.mu.Unlock() - // Block until we receive a goaway or an error occurs. - select { - case <-t.GoAway(): - done := t.Error() - cleanup := t.Close - // Since this transport will be orphaned (won't have a transportMonitor) - // we need to launch a goroutine to keep track of clientConn.Close() - // happening since it might not be noticed by any other goroutine for a while. - go func() { - <-done - cleanup() - }() - case <-t.Error(): - // In case this is triggered because clientConn.Close() - // was called, we want to immeditately close the transport - // since no other goroutine might notice it for a while. - t.Close() - case <-cdeadline: - ac.mu.Lock() - // This implies that client received server preface. - if ac.backoffDeadline.IsZero() { - ac.mu.Unlock() - continue - } - ac.mu.Unlock() - timer = nil - // No server preface received until deadline. - // Kill the connection. - grpclog.Warningf("grpc: addrConn.transportMonitor didn't get server preface after waiting. Closing the new transport now.") - t.Close() - } - if timer != nil { - timer.Stop() - } - // If a GoAway happened, regardless of error, adjust our keepalive - // parameters as appropriate. - select { - case <-t.GoAway(): - ac.adjustParams(t.GetGoAwayReason()) - default: - } - ac.mu.Lock() - if ac.state == connectivity.Shutdown { - ac.mu.Unlock() - return - } - // Set connectivity state to TransientFailure before calling - // resetTransport. Transition READY->CONNECTING is not valid. - ac.state = connectivity.TransientFailure - ac.cc.handleSubConnStateChange(ac.acbw, ac.state) - ac.cc.resolveNow(resolver.ResolveNowOption{}) - ac.curAddr = resolver.Address{} - ac.mu.Unlock() - if err := ac.resetTransport(); err != nil { - ac.mu.Lock() - ac.printf("transport exiting: %v", err) - ac.mu.Unlock() - grpclog.Warningf("grpc: addrConn.transportMonitor exits due to: %v", err) - if err != errConnClosing { - // Keep this ac in cc.conns, to get the reason it's torn down. - ac.tearDown(err) - } - return - } - } -} - -// wait blocks until i) the new transport is up or ii) ctx is done or iii) ac is closed or -// iv) transport is in connectivity.TransientFailure and there is a balancer/failfast is true. -func (ac *addrConn) wait(ctx context.Context, hasBalancer, failfast bool) (transport.ClientTransport, error) { - for { - ac.mu.Lock() - switch { - case ac.state == connectivity.Shutdown: - if failfast || !hasBalancer { - // RPC is failfast or balancer is nil. This RPC should fail with ac.tearDownErr. - err := ac.tearDownErr - ac.mu.Unlock() - return nil, err - } - ac.mu.Unlock() - return nil, errConnClosing - case ac.state == connectivity.Ready: - ct := ac.transport - ac.mu.Unlock() - return ct, nil - case ac.state == connectivity.TransientFailure: - if failfast || hasBalancer { - ac.mu.Unlock() - return nil, errConnUnavailable - } - } - ready := ac.ready - if ready == nil { - ready = make(chan struct{}) - ac.ready = ready - } - ac.mu.Unlock() - select { - case <-ctx.Done(): - return nil, toRPCErr(ctx.Err()) - // Wait until the new transport is ready or failed. - case <-ready: - } - } -} - -// getReadyTransport returns the transport if ac's state is READY. -// Otherwise it returns nil, false. -// If ac's state is IDLE, it will trigger ac to connect. -func (ac *addrConn) getReadyTransport() (transport.ClientTransport, bool) { - ac.mu.Lock() - if ac.state == connectivity.Ready { - t := ac.transport - ac.mu.Unlock() - return t, true - } - var idle bool - if ac.state == connectivity.Idle { - idle = true - } - ac.mu.Unlock() - // Trigger idle ac to connect. - if idle { - ac.connect() - } - return nil, false -} - -// tearDown starts to tear down the addrConn. -// TODO(zhaoq): Make this synchronous to avoid unbounded memory consumption in -// some edge cases (e.g., the caller opens and closes many addrConn's in a -// tight loop. -// tearDown doesn't remove ac from ac.cc.conns. -func (ac *addrConn) tearDown(err error) { - ac.cancel() - ac.mu.Lock() - defer ac.mu.Unlock() - if ac.state == connectivity.Shutdown { - return - } - ac.curAddr = resolver.Address{} - if err == errConnDrain && ac.transport != nil { - // GracefulClose(...) may be executed multiple times when - // i) receiving multiple GoAway frames from the server; or - // ii) there are concurrent name resolver/Balancer triggered - // address removal and GoAway. - ac.transport.GracefulClose() - } - ac.state = connectivity.Shutdown - ac.tearDownErr = err - ac.cc.handleSubConnStateChange(ac.acbw, ac.state) - if ac.events != nil { - ac.events.Finish() - ac.events = nil - } - if ac.ready != nil { - close(ac.ready) - ac.ready = nil - } - if channelz.IsOn() { - channelz.RemoveEntry(ac.channelzID) - } -} - -func (ac *addrConn) getState() connectivity.State { - ac.mu.Lock() - defer ac.mu.Unlock() - return ac.state -} - -func (ac *addrConn) getCurAddr() (ret resolver.Address) { - ac.mu.Lock() - ret = ac.curAddr - ac.mu.Unlock() - return -} - -func (ac *addrConn) ChannelzMetric() *channelz.ChannelInternalMetric { - ac.mu.Lock() - addr := ac.curAddr.Addr - ac.mu.Unlock() - state := ac.getState() - ac.czmu.RLock() - defer ac.czmu.RUnlock() - return &channelz.ChannelInternalMetric{ - State: state, - Target: addr, - CallsStarted: ac.callsStarted, - CallsSucceeded: ac.callsSucceeded, - CallsFailed: ac.callsFailed, - LastCallStartedTimestamp: ac.lastCallStartedTime, - } -} - -func (ac *addrConn) incrCallsStarted() { - ac.czmu.Lock() - ac.callsStarted++ - ac.lastCallStartedTime = time.Now() - ac.czmu.Unlock() -} - -func (ac *addrConn) incrCallsSucceeded() { - ac.czmu.Lock() - ac.callsSucceeded++ - ac.czmu.Unlock() -} - -func (ac *addrConn) incrCallsFailed() { - ac.czmu.Lock() - ac.callsFailed++ - ac.czmu.Unlock() -} - -// ErrClientConnTimeout indicates that the ClientConn cannot establish the -// underlying connections within the specified timeout. -// -// Deprecated: This error is never returned by grpc and should not be -// referenced by users. -var ErrClientConnTimeout = errors.New("grpc: timed out when dialing") diff --git a/vendor/google.golang.org/grpc/codec.go b/vendor/google.golang.org/grpc/codec.go deleted file mode 100644 index 12977654781..00000000000 --- a/vendor/google.golang.org/grpc/codec.go +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "google.golang.org/grpc/encoding" - _ "google.golang.org/grpc/encoding/proto" // to register the Codec for "proto" -) - -// baseCodec contains the functionality of both Codec and encoding.Codec, but -// omits the name/string, which vary between the two and are not needed for -// anything besides the registry in the encoding package. -type baseCodec interface { - Marshal(v interface{}) ([]byte, error) - Unmarshal(data []byte, v interface{}) error -} - -var _ baseCodec = Codec(nil) -var _ baseCodec = encoding.Codec(nil) - -// Codec defines the interface gRPC uses to encode and decode messages. -// Note that implementations of this interface must be thread safe; -// a Codec's methods can be called from concurrent goroutines. -// -// Deprecated: use encoding.Codec instead. -type Codec interface { - // Marshal returns the wire format of v. - Marshal(v interface{}) ([]byte, error) - // Unmarshal parses the wire format into v. - Unmarshal(data []byte, v interface{}) error - // String returns the name of the Codec implementation. This is unused by - // gRPC. - String() string -} diff --git a/vendor/google.golang.org/grpc/codes/code_string.go b/vendor/google.golang.org/grpc/codes/code_string.go deleted file mode 100644 index 0b206a57822..00000000000 --- a/vendor/google.golang.org/grpc/codes/code_string.go +++ /dev/null @@ -1,62 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package codes - -import "strconv" - -func (c Code) String() string { - switch c { - case OK: - return "OK" - case Canceled: - return "Canceled" - case Unknown: - return "Unknown" - case InvalidArgument: - return "InvalidArgument" - case DeadlineExceeded: - return "DeadlineExceeded" - case NotFound: - return "NotFound" - case AlreadyExists: - return "AlreadyExists" - case PermissionDenied: - return "PermissionDenied" - case ResourceExhausted: - return "ResourceExhausted" - case FailedPrecondition: - return "FailedPrecondition" - case Aborted: - return "Aborted" - case OutOfRange: - return "OutOfRange" - case Unimplemented: - return "Unimplemented" - case Internal: - return "Internal" - case Unavailable: - return "Unavailable" - case DataLoss: - return "DataLoss" - case Unauthenticated: - return "Unauthenticated" - default: - return "Code(" + strconv.FormatInt(int64(c), 10) + ")" - } -} diff --git a/vendor/google.golang.org/grpc/codes/codes.go b/vendor/google.golang.org/grpc/codes/codes.go deleted file mode 100644 index a8280ae660d..00000000000 --- a/vendor/google.golang.org/grpc/codes/codes.go +++ /dev/null @@ -1,184 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package codes defines the canonical error codes used by gRPC. It is -// consistent across various languages. -package codes // import "google.golang.org/grpc/codes" - -import ( - "fmt" -) - -// A Code is an unsigned 32-bit error code as defined in the gRPC spec. -type Code uint32 - -const ( - // OK is returned on success. - OK Code = 0 - - // Canceled indicates the operation was canceled (typically by the caller). - Canceled Code = 1 - - // Unknown error. An example of where this error may be returned is - // if a Status value received from another address space belongs to - // an error-space that is not known in this address space. Also - // errors raised by APIs that do not return enough error information - // may be converted to this error. - Unknown Code = 2 - - // InvalidArgument indicates client specified an invalid argument. - // Note that this differs from FailedPrecondition. It indicates arguments - // that are problematic regardless of the state of the system - // (e.g., a malformed file name). - InvalidArgument Code = 3 - - // DeadlineExceeded means operation expired before completion. - // For operations that change the state of the system, this error may be - // returned even if the operation has completed successfully. For - // example, a successful response from a server could have been delayed - // long enough for the deadline to expire. - DeadlineExceeded Code = 4 - - // NotFound means some requested entity (e.g., file or directory) was - // not found. - NotFound Code = 5 - - // AlreadyExists means an attempt to create an entity failed because one - // already exists. - AlreadyExists Code = 6 - - // PermissionDenied indicates the caller does not have permission to - // execute the specified operation. It must not be used for rejections - // caused by exhausting some resource (use ResourceExhausted - // instead for those errors). It must not be - // used if the caller cannot be identified (use Unauthenticated - // instead for those errors). - PermissionDenied Code = 7 - - // ResourceExhausted indicates some resource has been exhausted, perhaps - // a per-user quota, or perhaps the entire file system is out of space. - ResourceExhausted Code = 8 - - // FailedPrecondition indicates operation was rejected because the - // system is not in a state required for the operation's execution. - // For example, directory to be deleted may be non-empty, an rmdir - // operation is applied to a non-directory, etc. - // - // A litmus test that may help a service implementor in deciding - // between FailedPrecondition, Aborted, and Unavailable: - // (a) Use Unavailable if the client can retry just the failing call. - // (b) Use Aborted if the client should retry at a higher-level - // (e.g., restarting a read-modify-write sequence). - // (c) Use FailedPrecondition if the client should not retry until - // the system state has been explicitly fixed. E.g., if an "rmdir" - // fails because the directory is non-empty, FailedPrecondition - // should be returned since the client should not retry unless - // they have first fixed up the directory by deleting files from it. - // (d) Use FailedPrecondition if the client performs conditional - // REST Get/Update/Delete on a resource and the resource on the - // server does not match the condition. E.g., conflicting - // read-modify-write on the same resource. - FailedPrecondition Code = 9 - - // Aborted indicates the operation was aborted, typically due to a - // concurrency issue like sequencer check failures, transaction aborts, - // etc. - // - // See litmus test above for deciding between FailedPrecondition, - // Aborted, and Unavailable. - Aborted Code = 10 - - // OutOfRange means operation was attempted past the valid range. - // E.g., seeking or reading past end of file. - // - // Unlike InvalidArgument, this error indicates a problem that may - // be fixed if the system state changes. For example, a 32-bit file - // system will generate InvalidArgument if asked to read at an - // offset that is not in the range [0,2^32-1], but it will generate - // OutOfRange if asked to read from an offset past the current - // file size. - // - // There is a fair bit of overlap between FailedPrecondition and - // OutOfRange. We recommend using OutOfRange (the more specific - // error) when it applies so that callers who are iterating through - // a space can easily look for an OutOfRange error to detect when - // they are done. - OutOfRange Code = 11 - - // Unimplemented indicates operation is not implemented or not - // supported/enabled in this service. - Unimplemented Code = 12 - - // Internal errors. Means some invariants expected by underlying - // system has been broken. If you see one of these errors, - // something is very broken. - Internal Code = 13 - - // Unavailable indicates the service is currently unavailable. - // This is a most likely a transient condition and may be corrected - // by retrying with a backoff. - // - // See litmus test above for deciding between FailedPrecondition, - // Aborted, and Unavailable. - Unavailable Code = 14 - - // DataLoss indicates unrecoverable data loss or corruption. - DataLoss Code = 15 - - // Unauthenticated indicates the request does not have valid - // authentication credentials for the operation. - Unauthenticated Code = 16 -) - -var strToCode = map[string]Code{ - `"OK"`: OK, - `"CANCELLED"`:/* [sic] */ Canceled, - `"UNKNOWN"`: Unknown, - `"INVALID_ARGUMENT"`: InvalidArgument, - `"DEADLINE_EXCEEDED"`: DeadlineExceeded, - `"NOT_FOUND"`: NotFound, - `"ALREADY_EXISTS"`: AlreadyExists, - `"PERMISSION_DENIED"`: PermissionDenied, - `"RESOURCE_EXHAUSTED"`: ResourceExhausted, - `"FAILED_PRECONDITION"`: FailedPrecondition, - `"ABORTED"`: Aborted, - `"OUT_OF_RANGE"`: OutOfRange, - `"UNIMPLEMENTED"`: Unimplemented, - `"INTERNAL"`: Internal, - `"UNAVAILABLE"`: Unavailable, - `"DATA_LOSS"`: DataLoss, - `"UNAUTHENTICATED"`: Unauthenticated, -} - -// UnmarshalJSON unmarshals b into the Code. -func (c *Code) UnmarshalJSON(b []byte) error { - // From json.Unmarshaler: By convention, to approximate the behavior of - // Unmarshal itself, Unmarshalers implement UnmarshalJSON([]byte("null")) as - // a no-op. - if string(b) == "null" { - return nil - } - if c == nil { - return fmt.Errorf("nil receiver passed to UnmarshalJSON") - } - if jc, ok := strToCode[string(b)]; ok { - *c = jc - return nil - } - return fmt.Errorf("invalid code: %q", string(b)) -} diff --git a/vendor/google.golang.org/grpc/connectivity/connectivity.go b/vendor/google.golang.org/grpc/connectivity/connectivity.go deleted file mode 100644 index 568ef5dc68b..00000000000 --- a/vendor/google.golang.org/grpc/connectivity/connectivity.go +++ /dev/null @@ -1,72 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package connectivity defines connectivity semantics. -// For details, see https://github.com/grpc/grpc/blob/master/doc/connectivity-semantics-and-api.md. -// All APIs in this package are experimental. -package connectivity - -import ( - "golang.org/x/net/context" - "google.golang.org/grpc/grpclog" -) - -// State indicates the state of connectivity. -// It can be the state of a ClientConn or SubConn. -type State int - -func (s State) String() string { - switch s { - case Idle: - return "IDLE" - case Connecting: - return "CONNECTING" - case Ready: - return "READY" - case TransientFailure: - return "TRANSIENT_FAILURE" - case Shutdown: - return "SHUTDOWN" - default: - grpclog.Errorf("unknown connectivity state: %d", s) - return "Invalid-State" - } -} - -const ( - // Idle indicates the ClientConn is idle. - Idle State = iota - // Connecting indicates the ClienConn is connecting. - Connecting - // Ready indicates the ClientConn is ready for work. - Ready - // TransientFailure indicates the ClientConn has seen a failure but expects to recover. - TransientFailure - // Shutdown indicates the ClientConn has started shutting down. - Shutdown -) - -// Reporter reports the connectivity states. -type Reporter interface { - // CurrentState returns the current state of the reporter. - CurrentState() State - // WaitForStateChange blocks until the reporter's state is different from the given state, - // and returns true. - // It returns false if <-ctx.Done() can proceed (ctx got timeout or got canceled). - WaitForStateChange(context.Context, State) bool -} diff --git a/vendor/google.golang.org/grpc/credentials/credentials.go b/vendor/google.golang.org/grpc/credentials/credentials.go deleted file mode 100644 index 3351bf0ee5f..00000000000 --- a/vendor/google.golang.org/grpc/credentials/credentials.go +++ /dev/null @@ -1,220 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package credentials implements various credentials supported by gRPC library, -// which encapsulate all the state needed by a client to authenticate with a -// server and make various assertions, e.g., about the client's identity, role, -// or whether it is authorized to make a particular call. -package credentials // import "google.golang.org/grpc/credentials" - -import ( - "crypto/tls" - "crypto/x509" - "errors" - "fmt" - "io/ioutil" - "net" - "strings" - - "golang.org/x/net/context" -) - -// alpnProtoStr are the specified application level protocols for gRPC. -var alpnProtoStr = []string{"h2"} - -// PerRPCCredentials defines the common interface for the credentials which need to -// attach security information to every RPC (e.g., oauth2). -type PerRPCCredentials interface { - // GetRequestMetadata gets the current request metadata, refreshing - // tokens if required. This should be called by the transport layer on - // each request, and the data should be populated in headers or other - // context. If a status code is returned, it will be used as the status - // for the RPC. uri is the URI of the entry point for the request. - // When supported by the underlying implementation, ctx can be used for - // timeout and cancellation. - // TODO(zhaoq): Define the set of the qualified keys instead of leaving - // it as an arbitrary string. - GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) - // RequireTransportSecurity indicates whether the credentials requires - // transport security. - RequireTransportSecurity() bool -} - -// ProtocolInfo provides information regarding the gRPC wire protocol version, -// security protocol, security protocol version in use, server name, etc. -type ProtocolInfo struct { - // ProtocolVersion is the gRPC wire protocol version. - ProtocolVersion string - // SecurityProtocol is the security protocol in use. - SecurityProtocol string - // SecurityVersion is the security protocol version. - SecurityVersion string - // ServerName is the user-configured server name. - ServerName string -} - -// AuthInfo defines the common interface for the auth information the users are interested in. -type AuthInfo interface { - AuthType() string -} - -// ErrConnDispatched indicates that rawConn has been dispatched out of gRPC -// and the caller should not close rawConn. -var ErrConnDispatched = errors.New("credentials: rawConn is dispatched out of gRPC") - -// TransportCredentials defines the common interface for all the live gRPC wire -// protocols and supported transport security protocols (e.g., TLS, SSL). -type TransportCredentials interface { - // ClientHandshake does the authentication handshake specified by the corresponding - // authentication protocol on rawConn for clients. It returns the authenticated - // connection and the corresponding auth information about the connection. - // Implementations must use the provided context to implement timely cancellation. - // gRPC will try to reconnect if the error returned is a temporary error - // (io.EOF, context.DeadlineExceeded or err.Temporary() == true). - // If the returned error is a wrapper error, implementations should make sure that - // the error implements Temporary() to have the correct retry behaviors. - // - // If the returned net.Conn is closed, it MUST close the net.Conn provided. - ClientHandshake(context.Context, string, net.Conn) (net.Conn, AuthInfo, error) - // ServerHandshake does the authentication handshake for servers. It returns - // the authenticated connection and the corresponding auth information about - // the connection. - // - // If the returned net.Conn is closed, it MUST close the net.Conn provided. - ServerHandshake(net.Conn) (net.Conn, AuthInfo, error) - // Info provides the ProtocolInfo of this TransportCredentials. - Info() ProtocolInfo - // Clone makes a copy of this TransportCredentials. - Clone() TransportCredentials - // OverrideServerName overrides the server name used to verify the hostname on the returned certificates from the server. - // gRPC internals also use it to override the virtual hosting name if it is set. - // It must be called before dialing. Currently, this is only used by grpclb. - OverrideServerName(string) error -} - -// TLSInfo contains the auth information for a TLS authenticated connection. -// It implements the AuthInfo interface. -type TLSInfo struct { - State tls.ConnectionState -} - -// AuthType returns the type of TLSInfo as a string. -func (t TLSInfo) AuthType() string { - return "tls" -} - -// tlsCreds is the credentials required for authenticating a connection using TLS. -type tlsCreds struct { - // TLS configuration - config *tls.Config -} - -func (c tlsCreds) Info() ProtocolInfo { - return ProtocolInfo{ - SecurityProtocol: "tls", - SecurityVersion: "1.2", - ServerName: c.config.ServerName, - } -} - -func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ AuthInfo, err error) { - // use local cfg to avoid clobbering ServerName if using multiple endpoints - cfg := cloneTLSConfig(c.config) - if cfg.ServerName == "" { - colonPos := strings.LastIndex(authority, ":") - if colonPos == -1 { - colonPos = len(authority) - } - cfg.ServerName = authority[:colonPos] - } - conn := tls.Client(rawConn, cfg) - errChannel := make(chan error, 1) - go func() { - errChannel <- conn.Handshake() - }() - select { - case err := <-errChannel: - if err != nil { - return nil, nil, err - } - case <-ctx.Done(): - return nil, nil, ctx.Err() - } - return conn, TLSInfo{conn.ConnectionState()}, nil -} - -func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) { - conn := tls.Server(rawConn, c.config) - if err := conn.Handshake(); err != nil { - return nil, nil, err - } - return conn, TLSInfo{conn.ConnectionState()}, nil -} - -func (c *tlsCreds) Clone() TransportCredentials { - return NewTLS(c.config) -} - -func (c *tlsCreds) OverrideServerName(serverNameOverride string) error { - c.config.ServerName = serverNameOverride - return nil -} - -// NewTLS uses c to construct a TransportCredentials based on TLS. -func NewTLS(c *tls.Config) TransportCredentials { - tc := &tlsCreds{cloneTLSConfig(c)} - tc.config.NextProtos = alpnProtoStr - return tc -} - -// NewClientTLSFromCert constructs TLS credentials from the input certificate for client. -// serverNameOverride is for testing only. If set to a non empty string, -// it will override the virtual host name of authority (e.g. :authority header field) in requests. -func NewClientTLSFromCert(cp *x509.CertPool, serverNameOverride string) TransportCredentials { - return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}) -} - -// NewClientTLSFromFile constructs TLS credentials from the input certificate file for client. -// serverNameOverride is for testing only. If set to a non empty string, -// it will override the virtual host name of authority (e.g. :authority header field) in requests. -func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) { - b, err := ioutil.ReadFile(certFile) - if err != nil { - return nil, err - } - cp := x509.NewCertPool() - if !cp.AppendCertsFromPEM(b) { - return nil, fmt.Errorf("credentials: failed to append certificates") - } - return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil -} - -// NewServerTLSFromCert constructs TLS credentials from the input certificate for server. -func NewServerTLSFromCert(cert *tls.Certificate) TransportCredentials { - return NewTLS(&tls.Config{Certificates: []tls.Certificate{*cert}}) -} - -// NewServerTLSFromFile constructs TLS credentials from the input certificate file and key -// file for server. -func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error) { - cert, err := tls.LoadX509KeyPair(certFile, keyFile) - if err != nil { - return nil, err - } - return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil -} diff --git a/vendor/google.golang.org/grpc/credentials/credentials_util_go17.go b/vendor/google.golang.org/grpc/credentials/credentials_util_go17.go deleted file mode 100644 index 60409aac0fb..00000000000 --- a/vendor/google.golang.org/grpc/credentials/credentials_util_go17.go +++ /dev/null @@ -1,60 +0,0 @@ -// +build go1.7 -// +build !go1.8 - -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package credentials - -import ( - "crypto/tls" -) - -// cloneTLSConfig returns a shallow clone of the exported -// fields of cfg, ignoring the unexported sync.Once, which -// contains a mutex and must not be copied. -// -// If cfg is nil, a new zero tls.Config is returned. -func cloneTLSConfig(cfg *tls.Config) *tls.Config { - if cfg == nil { - return &tls.Config{} - } - return &tls.Config{ - Rand: cfg.Rand, - Time: cfg.Time, - Certificates: cfg.Certificates, - NameToCertificate: cfg.NameToCertificate, - GetCertificate: cfg.GetCertificate, - RootCAs: cfg.RootCAs, - NextProtos: cfg.NextProtos, - ServerName: cfg.ServerName, - ClientAuth: cfg.ClientAuth, - ClientCAs: cfg.ClientCAs, - InsecureSkipVerify: cfg.InsecureSkipVerify, - CipherSuites: cfg.CipherSuites, - PreferServerCipherSuites: cfg.PreferServerCipherSuites, - SessionTicketsDisabled: cfg.SessionTicketsDisabled, - SessionTicketKey: cfg.SessionTicketKey, - ClientSessionCache: cfg.ClientSessionCache, - MinVersion: cfg.MinVersion, - MaxVersion: cfg.MaxVersion, - CurvePreferences: cfg.CurvePreferences, - DynamicRecordSizingDisabled: cfg.DynamicRecordSizingDisabled, - Renegotiation: cfg.Renegotiation, - } -} diff --git a/vendor/google.golang.org/grpc/credentials/credentials_util_go18.go b/vendor/google.golang.org/grpc/credentials/credentials_util_go18.go deleted file mode 100644 index 93f0e1d8de2..00000000000 --- a/vendor/google.golang.org/grpc/credentials/credentials_util_go18.go +++ /dev/null @@ -1,38 +0,0 @@ -// +build go1.8 - -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package credentials - -import ( - "crypto/tls" -) - -// cloneTLSConfig returns a shallow clone of the exported -// fields of cfg, ignoring the unexported sync.Once, which -// contains a mutex and must not be copied. -// -// If cfg is nil, a new zero tls.Config is returned. -func cloneTLSConfig(cfg *tls.Config) *tls.Config { - if cfg == nil { - return &tls.Config{} - } - - return cfg.Clone() -} diff --git a/vendor/google.golang.org/grpc/credentials/credentials_util_pre_go17.go b/vendor/google.golang.org/grpc/credentials/credentials_util_pre_go17.go deleted file mode 100644 index d6bbcc9fdd9..00000000000 --- a/vendor/google.golang.org/grpc/credentials/credentials_util_pre_go17.go +++ /dev/null @@ -1,57 +0,0 @@ -// +build !go1.7 - -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package credentials - -import ( - "crypto/tls" -) - -// cloneTLSConfig returns a shallow clone of the exported -// fields of cfg, ignoring the unexported sync.Once, which -// contains a mutex and must not be copied. -// -// If cfg is nil, a new zero tls.Config is returned. -func cloneTLSConfig(cfg *tls.Config) *tls.Config { - if cfg == nil { - return &tls.Config{} - } - return &tls.Config{ - Rand: cfg.Rand, - Time: cfg.Time, - Certificates: cfg.Certificates, - NameToCertificate: cfg.NameToCertificate, - GetCertificate: cfg.GetCertificate, - RootCAs: cfg.RootCAs, - NextProtos: cfg.NextProtos, - ServerName: cfg.ServerName, - ClientAuth: cfg.ClientAuth, - ClientCAs: cfg.ClientCAs, - InsecureSkipVerify: cfg.InsecureSkipVerify, - CipherSuites: cfg.CipherSuites, - PreferServerCipherSuites: cfg.PreferServerCipherSuites, - SessionTicketsDisabled: cfg.SessionTicketsDisabled, - SessionTicketKey: cfg.SessionTicketKey, - ClientSessionCache: cfg.ClientSessionCache, - MinVersion: cfg.MinVersion, - MaxVersion: cfg.MaxVersion, - CurvePreferences: cfg.CurvePreferences, - } -} diff --git a/vendor/google.golang.org/grpc/credentials/oauth/oauth.go b/vendor/google.golang.org/grpc/credentials/oauth/oauth.go deleted file mode 100644 index f6d597a14fe..00000000000 --- a/vendor/google.golang.org/grpc/credentials/oauth/oauth.go +++ /dev/null @@ -1,173 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package oauth implements gRPC credentials using OAuth. -package oauth - -import ( - "fmt" - "io/ioutil" - "sync" - - "golang.org/x/net/context" - "golang.org/x/oauth2" - "golang.org/x/oauth2/google" - "golang.org/x/oauth2/jwt" - "google.golang.org/grpc/credentials" -) - -// TokenSource supplies PerRPCCredentials from an oauth2.TokenSource. -type TokenSource struct { - oauth2.TokenSource -} - -// GetRequestMetadata gets the request metadata as a map from a TokenSource. -func (ts TokenSource) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { - token, err := ts.Token() - if err != nil { - return nil, err - } - return map[string]string{ - "authorization": token.Type() + " " + token.AccessToken, - }, nil -} - -// RequireTransportSecurity indicates whether the credentials requires transport security. -func (ts TokenSource) RequireTransportSecurity() bool { - return true -} - -type jwtAccess struct { - jsonKey []byte -} - -// NewJWTAccessFromFile creates PerRPCCredentials from the given keyFile. -func NewJWTAccessFromFile(keyFile string) (credentials.PerRPCCredentials, error) { - jsonKey, err := ioutil.ReadFile(keyFile) - if err != nil { - return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err) - } - return NewJWTAccessFromKey(jsonKey) -} - -// NewJWTAccessFromKey creates PerRPCCredentials from the given jsonKey. -func NewJWTAccessFromKey(jsonKey []byte) (credentials.PerRPCCredentials, error) { - return jwtAccess{jsonKey}, nil -} - -func (j jwtAccess) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { - ts, err := google.JWTAccessTokenSourceFromJSON(j.jsonKey, uri[0]) - if err != nil { - return nil, err - } - token, err := ts.Token() - if err != nil { - return nil, err - } - return map[string]string{ - "authorization": token.Type() + " " + token.AccessToken, - }, nil -} - -func (j jwtAccess) RequireTransportSecurity() bool { - return true -} - -// oauthAccess supplies PerRPCCredentials from a given token. -type oauthAccess struct { - token oauth2.Token -} - -// NewOauthAccess constructs the PerRPCCredentials using a given token. -func NewOauthAccess(token *oauth2.Token) credentials.PerRPCCredentials { - return oauthAccess{token: *token} -} - -func (oa oauthAccess) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { - return map[string]string{ - "authorization": oa.token.Type() + " " + oa.token.AccessToken, - }, nil -} - -func (oa oauthAccess) RequireTransportSecurity() bool { - return true -} - -// NewComputeEngine constructs the PerRPCCredentials that fetches access tokens from -// Google Compute Engine (GCE)'s metadata server. It is only valid to use this -// if your program is running on a GCE instance. -// TODO(dsymonds): Deprecate and remove this. -func NewComputeEngine() credentials.PerRPCCredentials { - return TokenSource{google.ComputeTokenSource("")} -} - -// serviceAccount represents PerRPCCredentials via JWT signing key. -type serviceAccount struct { - mu sync.Mutex - config *jwt.Config - t *oauth2.Token -} - -func (s *serviceAccount) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) { - s.mu.Lock() - defer s.mu.Unlock() - if !s.t.Valid() { - var err error - s.t, err = s.config.TokenSource(ctx).Token() - if err != nil { - return nil, err - } - } - return map[string]string{ - "authorization": s.t.Type() + " " + s.t.AccessToken, - }, nil -} - -func (s *serviceAccount) RequireTransportSecurity() bool { - return true -} - -// NewServiceAccountFromKey constructs the PerRPCCredentials using the JSON key slice -// from a Google Developers service account. -func NewServiceAccountFromKey(jsonKey []byte, scope ...string) (credentials.PerRPCCredentials, error) { - config, err := google.JWTConfigFromJSON(jsonKey, scope...) - if err != nil { - return nil, err - } - return &serviceAccount{config: config}, nil -} - -// NewServiceAccountFromFile constructs the PerRPCCredentials using the JSON key file -// of a Google Developers service account. -func NewServiceAccountFromFile(keyFile string, scope ...string) (credentials.PerRPCCredentials, error) { - jsonKey, err := ioutil.ReadFile(keyFile) - if err != nil { - return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err) - } - return NewServiceAccountFromKey(jsonKey, scope...) -} - -// NewApplicationDefault returns "Application Default Credentials". For more -// detail, see https://developers.google.com/accounts/docs/application-default-credentials. -func NewApplicationDefault(ctx context.Context, scope ...string) (credentials.PerRPCCredentials, error) { - t, err := google.DefaultTokenSource(ctx, scope...) - if err != nil { - return nil, err - } - return TokenSource{t}, nil -} diff --git a/vendor/google.golang.org/grpc/doc.go b/vendor/google.golang.org/grpc/doc.go deleted file mode 100644 index 187adbb117f..00000000000 --- a/vendor/google.golang.org/grpc/doc.go +++ /dev/null @@ -1,24 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -/* -Package grpc implements an RPC system called gRPC. - -See grpc.io for more information about gRPC. -*/ -package grpc // import "google.golang.org/grpc" diff --git a/vendor/google.golang.org/grpc/encoding/encoding.go b/vendor/google.golang.org/grpc/encoding/encoding.go deleted file mode 100644 index ade8b7cec73..00000000000 --- a/vendor/google.golang.org/grpc/encoding/encoding.go +++ /dev/null @@ -1,118 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package encoding defines the interface for the compressor and codec, and -// functions to register and retrieve compressors and codecs. -// -// This package is EXPERIMENTAL. -package encoding - -import ( - "io" - "strings" -) - -// Identity specifies the optional encoding for uncompressed streams. -// It is intended for grpc internal use only. -const Identity = "identity" - -// Compressor is used for compressing and decompressing when sending or -// receiving messages. -type Compressor interface { - // Compress writes the data written to wc to w after compressing it. If an - // error occurs while initializing the compressor, that error is returned - // instead. - Compress(w io.Writer) (io.WriteCloser, error) - // Decompress reads data from r, decompresses it, and provides the - // uncompressed data via the returned io.Reader. If an error occurs while - // initializing the decompressor, that error is returned instead. - Decompress(r io.Reader) (io.Reader, error) - // Name is the name of the compression codec and is used to set the content - // coding header. The result must be static; the result cannot change - // between calls. - Name() string -} - -var registeredCompressor = make(map[string]Compressor) - -// RegisterCompressor registers the compressor with gRPC by its name. It can -// be activated when sending an RPC via grpc.UseCompressor(). It will be -// automatically accessed when receiving a message based on the content coding -// header. Servers also use it to send a response with the same encoding as -// the request. -// -// NOTE: this function must only be called during initialization time (i.e. in -// an init() function), and is not thread-safe. If multiple Compressors are -// registered with the same name, the one registered last will take effect. -func RegisterCompressor(c Compressor) { - registeredCompressor[c.Name()] = c -} - -// GetCompressor returns Compressor for the given compressor name. -func GetCompressor(name string) Compressor { - return registeredCompressor[name] -} - -// Codec defines the interface gRPC uses to encode and decode messages. Note -// that implementations of this interface must be thread safe; a Codec's -// methods can be called from concurrent goroutines. -type Codec interface { - // Marshal returns the wire format of v. - Marshal(v interface{}) ([]byte, error) - // Unmarshal parses the wire format into v. - Unmarshal(data []byte, v interface{}) error - // Name returns the name of the Codec implementation. The returned string - // will be used as part of content type in transmission. The result must be - // static; the result cannot change between calls. - Name() string -} - -var registeredCodecs = make(map[string]Codec) - -// RegisterCodec registers the provided Codec for use with all gRPC clients and -// servers. -// -// The Codec will be stored and looked up by result of its Name() method, which -// should match the content-subtype of the encoding handled by the Codec. This -// is case-insensitive, and is stored and looked up as lowercase. If the -// result of calling Name() is an empty string, RegisterCodec will panic. See -// Content-Type on -// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for -// more details. -// -// NOTE: this function must only be called during initialization time (i.e. in -// an init() function), and is not thread-safe. If multiple Compressors are -// registered with the same name, the one registered last will take effect. -func RegisterCodec(codec Codec) { - if codec == nil { - panic("cannot register a nil Codec") - } - contentSubtype := strings.ToLower(codec.Name()) - if contentSubtype == "" { - panic("cannot register Codec with empty string result for String()") - } - registeredCodecs[contentSubtype] = codec -} - -// GetCodec gets a registered Codec by content-subtype, or nil if no Codec is -// registered for the content-subtype. -// -// The content-subtype is expected to be lowercase. -func GetCodec(contentSubtype string) Codec { - return registeredCodecs[contentSubtype] -} diff --git a/vendor/google.golang.org/grpc/encoding/proto/proto.go b/vendor/google.golang.org/grpc/encoding/proto/proto.go deleted file mode 100644 index 66b97a6f692..00000000000 --- a/vendor/google.golang.org/grpc/encoding/proto/proto.go +++ /dev/null @@ -1,110 +0,0 @@ -/* - * - * Copyright 2018 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package proto defines the protobuf codec. Importing this package will -// register the codec. -package proto - -import ( - "math" - "sync" - - "github.com/golang/protobuf/proto" - "google.golang.org/grpc/encoding" -) - -// Name is the name registered for the proto compressor. -const Name = "proto" - -func init() { - encoding.RegisterCodec(codec{}) -} - -// codec is a Codec implementation with protobuf. It is the default codec for gRPC. -type codec struct{} - -type cachedProtoBuffer struct { - lastMarshaledSize uint32 - proto.Buffer -} - -func capToMaxInt32(val int) uint32 { - if val > math.MaxInt32 { - return uint32(math.MaxInt32) - } - return uint32(val) -} - -func marshal(v interface{}, cb *cachedProtoBuffer) ([]byte, error) { - protoMsg := v.(proto.Message) - newSlice := make([]byte, 0, cb.lastMarshaledSize) - - cb.SetBuf(newSlice) - cb.Reset() - if err := cb.Marshal(protoMsg); err != nil { - return nil, err - } - out := cb.Bytes() - cb.lastMarshaledSize = capToMaxInt32(len(out)) - return out, nil -} - -func (codec) Marshal(v interface{}) ([]byte, error) { - if pm, ok := v.(proto.Marshaler); ok { - // object can marshal itself, no need for buffer - return pm.Marshal() - } - - cb := protoBufferPool.Get().(*cachedProtoBuffer) - out, err := marshal(v, cb) - - // put back buffer and lose the ref to the slice - cb.SetBuf(nil) - protoBufferPool.Put(cb) - return out, err -} - -func (codec) Unmarshal(data []byte, v interface{}) error { - protoMsg := v.(proto.Message) - protoMsg.Reset() - - if pu, ok := protoMsg.(proto.Unmarshaler); ok { - // object can unmarshal itself, no need for buffer - return pu.Unmarshal(data) - } - - cb := protoBufferPool.Get().(*cachedProtoBuffer) - cb.SetBuf(data) - err := cb.Unmarshal(protoMsg) - cb.SetBuf(nil) - protoBufferPool.Put(cb) - return err -} - -func (codec) Name() string { - return Name -} - -var protoBufferPool = &sync.Pool{ - New: func() interface{} { - return &cachedProtoBuffer{ - Buffer: proto.Buffer{}, - lastMarshaledSize: 16, - } - }, -} diff --git a/vendor/google.golang.org/grpc/envconfig.go b/vendor/google.golang.org/grpc/envconfig.go deleted file mode 100644 index d50178e5171..00000000000 --- a/vendor/google.golang.org/grpc/envconfig.go +++ /dev/null @@ -1,37 +0,0 @@ -/* - * - * Copyright 2018 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "os" - "strings" -) - -const ( - envConfigPrefix = "GRPC_GO_" - envConfigStickinessStr = envConfigPrefix + "STICKINESS" -) - -var ( - envConfigStickinessOn bool -) - -func init() { - envConfigStickinessOn = strings.EqualFold(os.Getenv(envConfigStickinessStr), "on") -} diff --git a/vendor/google.golang.org/grpc/go16.go b/vendor/google.golang.org/grpc/go16.go deleted file mode 100644 index 535ee9356f3..00000000000 --- a/vendor/google.golang.org/grpc/go16.go +++ /dev/null @@ -1,70 +0,0 @@ -// +build go1.6,!go1.7 - -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "fmt" - "io" - "net" - "net/http" - - "golang.org/x/net/context" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/grpc/transport" -) - -// dialContext connects to the address on the named network. -func dialContext(ctx context.Context, network, address string) (net.Conn, error) { - return (&net.Dialer{Cancel: ctx.Done()}).Dial(network, address) -} - -func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error { - req.Cancel = ctx.Done() - if err := req.Write(conn); err != nil { - return fmt.Errorf("failed to write the HTTP request: %v", err) - } - return nil -} - -// toRPCErr converts an error into an error from the status package. -func toRPCErr(err error) error { - if err == nil || err == io.EOF { - return err - } - if _, ok := status.FromError(err); ok { - return err - } - switch e := err.(type) { - case transport.StreamError: - return status.Error(e.Code, e.Desc) - case transport.ConnectionError: - return status.Error(codes.Unavailable, e.Desc) - default: - switch err { - case context.DeadlineExceeded: - return status.Error(codes.DeadlineExceeded, err.Error()) - case context.Canceled: - return status.Error(codes.Canceled, err.Error()) - } - } - return status.Error(codes.Unknown, err.Error()) -} diff --git a/vendor/google.golang.org/grpc/go17.go b/vendor/google.golang.org/grpc/go17.go deleted file mode 100644 index ec676a93c39..00000000000 --- a/vendor/google.golang.org/grpc/go17.go +++ /dev/null @@ -1,71 +0,0 @@ -// +build go1.7 - -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "context" - "fmt" - "io" - "net" - "net/http" - - netctx "golang.org/x/net/context" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - "google.golang.org/grpc/transport" -) - -// dialContext connects to the address on the named network. -func dialContext(ctx context.Context, network, address string) (net.Conn, error) { - return (&net.Dialer{}).DialContext(ctx, network, address) -} - -func sendHTTPRequest(ctx context.Context, req *http.Request, conn net.Conn) error { - req = req.WithContext(ctx) - if err := req.Write(conn); err != nil { - return fmt.Errorf("failed to write the HTTP request: %v", err) - } - return nil -} - -// toRPCErr converts an error into an error from the status package. -func toRPCErr(err error) error { - if err == nil || err == io.EOF { - return err - } - if _, ok := status.FromError(err); ok { - return err - } - switch e := err.(type) { - case transport.StreamError: - return status.Error(e.Code, e.Desc) - case transport.ConnectionError: - return status.Error(codes.Unavailable, e.Desc) - default: - switch err { - case context.DeadlineExceeded, netctx.DeadlineExceeded: - return status.Error(codes.DeadlineExceeded, err.Error()) - case context.Canceled, netctx.Canceled: - return status.Error(codes.Canceled, err.Error()) - } - } - return status.Error(codes.Unknown, err.Error()) -} diff --git a/vendor/google.golang.org/grpc/grpclb.go b/vendor/google.golang.org/grpc/grpclb.go deleted file mode 100644 index bc2b4452558..00000000000 --- a/vendor/google.golang.org/grpc/grpclb.go +++ /dev/null @@ -1,341 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "strconv" - "strings" - "sync" - "time" - - "golang.org/x/net/context" - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/connectivity" - lbpb "google.golang.org/grpc/grpclb/grpc_lb_v1/messages" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/resolver" -) - -const ( - lbTokeyKey = "lb-token" - defaultFallbackTimeout = 10 * time.Second - grpclbName = "grpclb" -) - -func convertDuration(d *lbpb.Duration) time.Duration { - if d == nil { - return 0 - } - return time.Duration(d.Seconds)*time.Second + time.Duration(d.Nanos)*time.Nanosecond -} - -// Client API for LoadBalancer service. -// Mostly copied from generated pb.go file. -// To avoid circular dependency. -type loadBalancerClient struct { - cc *ClientConn -} - -func (c *loadBalancerClient) BalanceLoad(ctx context.Context, opts ...CallOption) (*balanceLoadClientStream, error) { - desc := &StreamDesc{ - StreamName: "BalanceLoad", - ServerStreams: true, - ClientStreams: true, - } - stream, err := c.cc.NewStream(ctx, desc, "/grpc.lb.v1.LoadBalancer/BalanceLoad", opts...) - if err != nil { - return nil, err - } - x := &balanceLoadClientStream{stream} - return x, nil -} - -type balanceLoadClientStream struct { - ClientStream -} - -func (x *balanceLoadClientStream) Send(m *lbpb.LoadBalanceRequest) error { - return x.ClientStream.SendMsg(m) -} - -func (x *balanceLoadClientStream) Recv() (*lbpb.LoadBalanceResponse, error) { - m := new(lbpb.LoadBalanceResponse) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -func init() { - balancer.Register(newLBBuilder()) -} - -// newLBBuilder creates a builder for grpclb. -func newLBBuilder() balancer.Builder { - return NewLBBuilderWithFallbackTimeout(defaultFallbackTimeout) -} - -// NewLBBuilderWithFallbackTimeout creates a grpclb builder with the given -// fallbackTimeout. If no response is received from the remote balancer within -// fallbackTimeout, the backend addresses from the resolved address list will be -// used. -// -// Only call this function when a non-default fallback timeout is needed. -func NewLBBuilderWithFallbackTimeout(fallbackTimeout time.Duration) balancer.Builder { - return &lbBuilder{ - fallbackTimeout: fallbackTimeout, - } -} - -type lbBuilder struct { - fallbackTimeout time.Duration -} - -func (b *lbBuilder) Name() string { - return grpclbName -} - -func (b *lbBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer { - // This generates a manual resolver builder with a random scheme. This - // scheme will be used to dial to remote LB, so we can send filtered address - // updates to remote LB ClientConn using this manual resolver. - scheme := "grpclb_internal_" + strconv.FormatInt(time.Now().UnixNano(), 36) - r := &lbManualResolver{scheme: scheme, ccb: cc} - - var target string - targetSplitted := strings.Split(cc.Target(), ":///") - if len(targetSplitted) < 2 { - target = cc.Target() - } else { - target = targetSplitted[1] - } - - lb := &lbBalancer{ - cc: newLBCacheClientConn(cc), - target: target, - opt: opt, - fallbackTimeout: b.fallbackTimeout, - doneCh: make(chan struct{}), - - manualResolver: r, - csEvltr: &connectivityStateEvaluator{}, - subConns: make(map[resolver.Address]balancer.SubConn), - scStates: make(map[balancer.SubConn]connectivity.State), - picker: &errPicker{err: balancer.ErrNoSubConnAvailable}, - clientStats: &rpcStats{}, - } - - return lb -} - -type lbBalancer struct { - cc *lbCacheClientConn - target string - opt balancer.BuildOptions - fallbackTimeout time.Duration - doneCh chan struct{} - - // manualResolver is used in the remote LB ClientConn inside grpclb. When - // resolved address updates are received by grpclb, filtered updates will be - // send to remote LB ClientConn through this resolver. - manualResolver *lbManualResolver - // The ClientConn to talk to the remote balancer. - ccRemoteLB *ClientConn - - // Support client side load reporting. Each picker gets a reference to this, - // and will update its content. - clientStats *rpcStats - - mu sync.Mutex // guards everything following. - // The full server list including drops, used to check if the newly received - // serverList contains anything new. Each generate picker will also have - // reference to this list to do the first layer pick. - fullServerList []*lbpb.Server - // All backends addresses, with metadata set to nil. This list contains all - // backend addresses in the same order and with the same duplicates as in - // serverlist. When generating picker, a SubConn slice with the same order - // but with only READY SCs will be gerenated. - backendAddrs []resolver.Address - // Roundrobin functionalities. - csEvltr *connectivityStateEvaluator - state connectivity.State - subConns map[resolver.Address]balancer.SubConn // Used to new/remove SubConn. - scStates map[balancer.SubConn]connectivity.State // Used to filter READY SubConns. - picker balancer.Picker - // Support fallback to resolved backend addresses if there's no response - // from remote balancer within fallbackTimeout. - fallbackTimerExpired bool - serverListReceived bool - // resolvedBackendAddrs is resolvedAddrs minus remote balancers. It's set - // when resolved address updates are received, and read in the goroutine - // handling fallback. - resolvedBackendAddrs []resolver.Address -} - -// regeneratePicker takes a snapshot of the balancer, and generates a picker from -// it. The picker -// - always returns ErrTransientFailure if the balancer is in TransientFailure, -// - does two layer roundrobin pick otherwise. -// Caller must hold lb.mu. -func (lb *lbBalancer) regeneratePicker() { - if lb.state == connectivity.TransientFailure { - lb.picker = &errPicker{err: balancer.ErrTransientFailure} - return - } - var readySCs []balancer.SubConn - for _, a := range lb.backendAddrs { - if sc, ok := lb.subConns[a]; ok { - if st, ok := lb.scStates[sc]; ok && st == connectivity.Ready { - readySCs = append(readySCs, sc) - } - } - } - - if len(lb.fullServerList) <= 0 { - if len(readySCs) <= 0 { - lb.picker = &errPicker{err: balancer.ErrNoSubConnAvailable} - return - } - lb.picker = &rrPicker{subConns: readySCs} - return - } - lb.picker = &lbPicker{ - serverList: lb.fullServerList, - subConns: readySCs, - stats: lb.clientStats, - } -} - -func (lb *lbBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) { - grpclog.Infof("lbBalancer: handle SubConn state change: %p, %v", sc, s) - lb.mu.Lock() - defer lb.mu.Unlock() - - oldS, ok := lb.scStates[sc] - if !ok { - grpclog.Infof("lbBalancer: got state changes for an unknown SubConn: %p, %v", sc, s) - return - } - lb.scStates[sc] = s - switch s { - case connectivity.Idle: - sc.Connect() - case connectivity.Shutdown: - // When an address was removed by resolver, b called RemoveSubConn but - // kept the sc's state in scStates. Remove state for this sc here. - delete(lb.scStates, sc) - } - - oldAggrState := lb.state - lb.state = lb.csEvltr.recordTransition(oldS, s) - - // Regenerate picker when one of the following happens: - // - this sc became ready from not-ready - // - this sc became not-ready from ready - // - the aggregated state of balancer became TransientFailure from non-TransientFailure - // - the aggregated state of balancer became non-TransientFailure from TransientFailure - if (oldS == connectivity.Ready) != (s == connectivity.Ready) || - (lb.state == connectivity.TransientFailure) != (oldAggrState == connectivity.TransientFailure) { - lb.regeneratePicker() - } - - lb.cc.UpdateBalancerState(lb.state, lb.picker) -} - -// fallbackToBackendsAfter blocks for fallbackTimeout and falls back to use -// resolved backends (backends received from resolver, not from remote balancer) -// if no connection to remote balancers was successful. -func (lb *lbBalancer) fallbackToBackendsAfter(fallbackTimeout time.Duration) { - timer := time.NewTimer(fallbackTimeout) - defer timer.Stop() - select { - case <-timer.C: - case <-lb.doneCh: - return - } - lb.mu.Lock() - if lb.serverListReceived { - lb.mu.Unlock() - return - } - lb.fallbackTimerExpired = true - lb.refreshSubConns(lb.resolvedBackendAddrs) - lb.mu.Unlock() -} - -// HandleResolvedAddrs sends the updated remoteLB addresses to remoteLB -// clientConn. The remoteLB clientConn will handle creating/removing remoteLB -// connections. -func (lb *lbBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) { - grpclog.Infof("lbBalancer: handleResolvedResult: %+v", addrs) - if len(addrs) <= 0 { - return - } - - var remoteBalancerAddrs, backendAddrs []resolver.Address - for _, a := range addrs { - if a.Type == resolver.GRPCLB { - remoteBalancerAddrs = append(remoteBalancerAddrs, a) - } else { - backendAddrs = append(backendAddrs, a) - } - } - - if lb.ccRemoteLB == nil { - if len(remoteBalancerAddrs) <= 0 { - grpclog.Errorf("grpclb: no remote balancer address is available, should never happen") - return - } - // First time receiving resolved addresses, create a cc to remote - // balancers. - lb.dialRemoteLB(remoteBalancerAddrs[0].ServerName) - // Start the fallback goroutine. - go lb.fallbackToBackendsAfter(lb.fallbackTimeout) - } - - // cc to remote balancers uses lb.manualResolver. Send the updated remote - // balancer addresses to it through manualResolver. - lb.manualResolver.NewAddress(remoteBalancerAddrs) - - lb.mu.Lock() - lb.resolvedBackendAddrs = backendAddrs - // If serverListReceived is true, connection to remote balancer was - // successful and there's no need to do fallback anymore. - // If fallbackTimerExpired is false, fallback hasn't happened yet. - if !lb.serverListReceived && lb.fallbackTimerExpired { - // This means we received a new list of resolved backends, and we are - // still in fallback mode. Need to update the list of backends we are - // using to the new list of backends. - lb.refreshSubConns(lb.resolvedBackendAddrs) - } - lb.mu.Unlock() -} - -func (lb *lbBalancer) Close() { - select { - case <-lb.doneCh: - return - default: - } - close(lb.doneCh) - if lb.ccRemoteLB != nil { - lb.ccRemoteLB.Close() - } - lb.cc.close() -} diff --git a/vendor/google.golang.org/grpc/grpclb/grpc_lb_v1/messages/messages.pb.go b/vendor/google.golang.org/grpc/grpclb/grpc_lb_v1/messages/messages.pb.go deleted file mode 100644 index b3b32b48e86..00000000000 --- a/vendor/google.golang.org/grpc/grpclb/grpc_lb_v1/messages/messages.pb.go +++ /dev/null @@ -1,799 +0,0 @@ -// Code generated by protoc-gen-go. DO NOT EDIT. -// source: grpc_lb_v1/messages/messages.proto - -package messages // import "google.golang.org/grpc/grpclb/grpc_lb_v1/messages" - -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" - -// Reference imports to suppress errors if they are not otherwise used. -var _ = proto.Marshal -var _ = fmt.Errorf -var _ = math.Inf - -// This is a compile-time assertion to ensure that this generated file -// is compatible with the proto package it is being compiled against. -// A compilation error at this line likely means your copy of the -// proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package - -type Duration struct { - // Signed seconds of the span of time. Must be from -315,576,000,000 - // to +315,576,000,000 inclusive. - Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"` - // Signed fractions of a second at nanosecond resolution of the span - // of time. Durations less than one second are represented with a 0 - // `seconds` field and a positive or negative `nanos` field. For durations - // of one second or more, a non-zero value for the `nanos` field must be - // of the same sign as the `seconds` field. Must be from -999,999,999 - // to +999,999,999 inclusive. - Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Duration) Reset() { *m = Duration{} } -func (m *Duration) String() string { return proto.CompactTextString(m) } -func (*Duration) ProtoMessage() {} -func (*Duration) Descriptor() ([]byte, []int) { - return fileDescriptor_messages_b81c731f0e83edbd, []int{0} -} -func (m *Duration) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Duration.Unmarshal(m, b) -} -func (m *Duration) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Duration.Marshal(b, m, deterministic) -} -func (dst *Duration) XXX_Merge(src proto.Message) { - xxx_messageInfo_Duration.Merge(dst, src) -} -func (m *Duration) XXX_Size() int { - return xxx_messageInfo_Duration.Size(m) -} -func (m *Duration) XXX_DiscardUnknown() { - xxx_messageInfo_Duration.DiscardUnknown(m) -} - -var xxx_messageInfo_Duration proto.InternalMessageInfo - -func (m *Duration) GetSeconds() int64 { - if m != nil { - return m.Seconds - } - return 0 -} - -func (m *Duration) GetNanos() int32 { - if m != nil { - return m.Nanos - } - return 0 -} - -type Timestamp struct { - // Represents seconds of UTC time since Unix epoch - // 1970-01-01T00:00:00Z. Must be from 0001-01-01T00:00:00Z to - // 9999-12-31T23:59:59Z inclusive. - Seconds int64 `protobuf:"varint,1,opt,name=seconds" json:"seconds,omitempty"` - // Non-negative fractions of a second at nanosecond resolution. Negative - // second values with fractions must still have non-negative nanos values - // that count forward in time. Must be from 0 to 999,999,999 - // inclusive. - Nanos int32 `protobuf:"varint,2,opt,name=nanos" json:"nanos,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Timestamp) Reset() { *m = Timestamp{} } -func (m *Timestamp) String() string { return proto.CompactTextString(m) } -func (*Timestamp) ProtoMessage() {} -func (*Timestamp) Descriptor() ([]byte, []int) { - return fileDescriptor_messages_b81c731f0e83edbd, []int{1} -} -func (m *Timestamp) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Timestamp.Unmarshal(m, b) -} -func (m *Timestamp) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Timestamp.Marshal(b, m, deterministic) -} -func (dst *Timestamp) XXX_Merge(src proto.Message) { - xxx_messageInfo_Timestamp.Merge(dst, src) -} -func (m *Timestamp) XXX_Size() int { - return xxx_messageInfo_Timestamp.Size(m) -} -func (m *Timestamp) XXX_DiscardUnknown() { - xxx_messageInfo_Timestamp.DiscardUnknown(m) -} - -var xxx_messageInfo_Timestamp proto.InternalMessageInfo - -func (m *Timestamp) GetSeconds() int64 { - if m != nil { - return m.Seconds - } - return 0 -} - -func (m *Timestamp) GetNanos() int32 { - if m != nil { - return m.Nanos - } - return 0 -} - -type LoadBalanceRequest struct { - // Types that are valid to be assigned to LoadBalanceRequestType: - // *LoadBalanceRequest_InitialRequest - // *LoadBalanceRequest_ClientStats - LoadBalanceRequestType isLoadBalanceRequest_LoadBalanceRequestType `protobuf_oneof:"load_balance_request_type"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *LoadBalanceRequest) Reset() { *m = LoadBalanceRequest{} } -func (m *LoadBalanceRequest) String() string { return proto.CompactTextString(m) } -func (*LoadBalanceRequest) ProtoMessage() {} -func (*LoadBalanceRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_messages_b81c731f0e83edbd, []int{2} -} -func (m *LoadBalanceRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_LoadBalanceRequest.Unmarshal(m, b) -} -func (m *LoadBalanceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_LoadBalanceRequest.Marshal(b, m, deterministic) -} -func (dst *LoadBalanceRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_LoadBalanceRequest.Merge(dst, src) -} -func (m *LoadBalanceRequest) XXX_Size() int { - return xxx_messageInfo_LoadBalanceRequest.Size(m) -} -func (m *LoadBalanceRequest) XXX_DiscardUnknown() { - xxx_messageInfo_LoadBalanceRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_LoadBalanceRequest proto.InternalMessageInfo - -type isLoadBalanceRequest_LoadBalanceRequestType interface { - isLoadBalanceRequest_LoadBalanceRequestType() -} - -type LoadBalanceRequest_InitialRequest struct { - InitialRequest *InitialLoadBalanceRequest `protobuf:"bytes,1,opt,name=initial_request,json=initialRequest,oneof"` -} -type LoadBalanceRequest_ClientStats struct { - ClientStats *ClientStats `protobuf:"bytes,2,opt,name=client_stats,json=clientStats,oneof"` -} - -func (*LoadBalanceRequest_InitialRequest) isLoadBalanceRequest_LoadBalanceRequestType() {} -func (*LoadBalanceRequest_ClientStats) isLoadBalanceRequest_LoadBalanceRequestType() {} - -func (m *LoadBalanceRequest) GetLoadBalanceRequestType() isLoadBalanceRequest_LoadBalanceRequestType { - if m != nil { - return m.LoadBalanceRequestType - } - return nil -} - -func (m *LoadBalanceRequest) GetInitialRequest() *InitialLoadBalanceRequest { - if x, ok := m.GetLoadBalanceRequestType().(*LoadBalanceRequest_InitialRequest); ok { - return x.InitialRequest - } - return nil -} - -func (m *LoadBalanceRequest) GetClientStats() *ClientStats { - if x, ok := m.GetLoadBalanceRequestType().(*LoadBalanceRequest_ClientStats); ok { - return x.ClientStats - } - return nil -} - -// XXX_OneofFuncs is for the internal use of the proto package. -func (*LoadBalanceRequest) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _LoadBalanceRequest_OneofMarshaler, _LoadBalanceRequest_OneofUnmarshaler, _LoadBalanceRequest_OneofSizer, []interface{}{ - (*LoadBalanceRequest_InitialRequest)(nil), - (*LoadBalanceRequest_ClientStats)(nil), - } -} - -func _LoadBalanceRequest_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*LoadBalanceRequest) - // load_balance_request_type - switch x := m.LoadBalanceRequestType.(type) { - case *LoadBalanceRequest_InitialRequest: - b.EncodeVarint(1<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.InitialRequest); err != nil { - return err - } - case *LoadBalanceRequest_ClientStats: - b.EncodeVarint(2<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.ClientStats); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("LoadBalanceRequest.LoadBalanceRequestType has unexpected type %T", x) - } - return nil -} - -func _LoadBalanceRequest_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*LoadBalanceRequest) - switch tag { - case 1: // load_balance_request_type.initial_request - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(InitialLoadBalanceRequest) - err := b.DecodeMessage(msg) - m.LoadBalanceRequestType = &LoadBalanceRequest_InitialRequest{msg} - return true, err - case 2: // load_balance_request_type.client_stats - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(ClientStats) - err := b.DecodeMessage(msg) - m.LoadBalanceRequestType = &LoadBalanceRequest_ClientStats{msg} - return true, err - default: - return false, nil - } -} - -func _LoadBalanceRequest_OneofSizer(msg proto.Message) (n int) { - m := msg.(*LoadBalanceRequest) - // load_balance_request_type - switch x := m.LoadBalanceRequestType.(type) { - case *LoadBalanceRequest_InitialRequest: - s := proto.Size(x.InitialRequest) - n += 1 // tag and wire - n += proto.SizeVarint(uint64(s)) - n += s - case *LoadBalanceRequest_ClientStats: - s := proto.Size(x.ClientStats) - n += 1 // tag and wire - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - -type InitialLoadBalanceRequest struct { - // Name of load balanced service (IE, balancer.service.com) - // length should be less than 256 bytes. - Name string `protobuf:"bytes,1,opt,name=name" json:"name,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *InitialLoadBalanceRequest) Reset() { *m = InitialLoadBalanceRequest{} } -func (m *InitialLoadBalanceRequest) String() string { return proto.CompactTextString(m) } -func (*InitialLoadBalanceRequest) ProtoMessage() {} -func (*InitialLoadBalanceRequest) Descriptor() ([]byte, []int) { - return fileDescriptor_messages_b81c731f0e83edbd, []int{3} -} -func (m *InitialLoadBalanceRequest) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_InitialLoadBalanceRequest.Unmarshal(m, b) -} -func (m *InitialLoadBalanceRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_InitialLoadBalanceRequest.Marshal(b, m, deterministic) -} -func (dst *InitialLoadBalanceRequest) XXX_Merge(src proto.Message) { - xxx_messageInfo_InitialLoadBalanceRequest.Merge(dst, src) -} -func (m *InitialLoadBalanceRequest) XXX_Size() int { - return xxx_messageInfo_InitialLoadBalanceRequest.Size(m) -} -func (m *InitialLoadBalanceRequest) XXX_DiscardUnknown() { - xxx_messageInfo_InitialLoadBalanceRequest.DiscardUnknown(m) -} - -var xxx_messageInfo_InitialLoadBalanceRequest proto.InternalMessageInfo - -func (m *InitialLoadBalanceRequest) GetName() string { - if m != nil { - return m.Name - } - return "" -} - -// Contains client level statistics that are useful to load balancing. Each -// count except the timestamp should be reset to zero after reporting the stats. -type ClientStats struct { - // The timestamp of generating the report. - Timestamp *Timestamp `protobuf:"bytes,1,opt,name=timestamp" json:"timestamp,omitempty"` - // The total number of RPCs that started. - NumCallsStarted int64 `protobuf:"varint,2,opt,name=num_calls_started,json=numCallsStarted" json:"num_calls_started,omitempty"` - // The total number of RPCs that finished. - NumCallsFinished int64 `protobuf:"varint,3,opt,name=num_calls_finished,json=numCallsFinished" json:"num_calls_finished,omitempty"` - // The total number of RPCs that were dropped by the client because of rate - // limiting. - NumCallsFinishedWithDropForRateLimiting int64 `protobuf:"varint,4,opt,name=num_calls_finished_with_drop_for_rate_limiting,json=numCallsFinishedWithDropForRateLimiting" json:"num_calls_finished_with_drop_for_rate_limiting,omitempty"` - // The total number of RPCs that were dropped by the client because of load - // balancing. - NumCallsFinishedWithDropForLoadBalancing int64 `protobuf:"varint,5,opt,name=num_calls_finished_with_drop_for_load_balancing,json=numCallsFinishedWithDropForLoadBalancing" json:"num_calls_finished_with_drop_for_load_balancing,omitempty"` - // The total number of RPCs that failed to reach a server except dropped RPCs. - NumCallsFinishedWithClientFailedToSend int64 `protobuf:"varint,6,opt,name=num_calls_finished_with_client_failed_to_send,json=numCallsFinishedWithClientFailedToSend" json:"num_calls_finished_with_client_failed_to_send,omitempty"` - // The total number of RPCs that finished and are known to have been received - // by a server. - NumCallsFinishedKnownReceived int64 `protobuf:"varint,7,opt,name=num_calls_finished_known_received,json=numCallsFinishedKnownReceived" json:"num_calls_finished_known_received,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ClientStats) Reset() { *m = ClientStats{} } -func (m *ClientStats) String() string { return proto.CompactTextString(m) } -func (*ClientStats) ProtoMessage() {} -func (*ClientStats) Descriptor() ([]byte, []int) { - return fileDescriptor_messages_b81c731f0e83edbd, []int{4} -} -func (m *ClientStats) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ClientStats.Unmarshal(m, b) -} -func (m *ClientStats) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ClientStats.Marshal(b, m, deterministic) -} -func (dst *ClientStats) XXX_Merge(src proto.Message) { - xxx_messageInfo_ClientStats.Merge(dst, src) -} -func (m *ClientStats) XXX_Size() int { - return xxx_messageInfo_ClientStats.Size(m) -} -func (m *ClientStats) XXX_DiscardUnknown() { - xxx_messageInfo_ClientStats.DiscardUnknown(m) -} - -var xxx_messageInfo_ClientStats proto.InternalMessageInfo - -func (m *ClientStats) GetTimestamp() *Timestamp { - if m != nil { - return m.Timestamp - } - return nil -} - -func (m *ClientStats) GetNumCallsStarted() int64 { - if m != nil { - return m.NumCallsStarted - } - return 0 -} - -func (m *ClientStats) GetNumCallsFinished() int64 { - if m != nil { - return m.NumCallsFinished - } - return 0 -} - -func (m *ClientStats) GetNumCallsFinishedWithDropForRateLimiting() int64 { - if m != nil { - return m.NumCallsFinishedWithDropForRateLimiting - } - return 0 -} - -func (m *ClientStats) GetNumCallsFinishedWithDropForLoadBalancing() int64 { - if m != nil { - return m.NumCallsFinishedWithDropForLoadBalancing - } - return 0 -} - -func (m *ClientStats) GetNumCallsFinishedWithClientFailedToSend() int64 { - if m != nil { - return m.NumCallsFinishedWithClientFailedToSend - } - return 0 -} - -func (m *ClientStats) GetNumCallsFinishedKnownReceived() int64 { - if m != nil { - return m.NumCallsFinishedKnownReceived - } - return 0 -} - -type LoadBalanceResponse struct { - // Types that are valid to be assigned to LoadBalanceResponseType: - // *LoadBalanceResponse_InitialResponse - // *LoadBalanceResponse_ServerList - LoadBalanceResponseType isLoadBalanceResponse_LoadBalanceResponseType `protobuf_oneof:"load_balance_response_type"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *LoadBalanceResponse) Reset() { *m = LoadBalanceResponse{} } -func (m *LoadBalanceResponse) String() string { return proto.CompactTextString(m) } -func (*LoadBalanceResponse) ProtoMessage() {} -func (*LoadBalanceResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_messages_b81c731f0e83edbd, []int{5} -} -func (m *LoadBalanceResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_LoadBalanceResponse.Unmarshal(m, b) -} -func (m *LoadBalanceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_LoadBalanceResponse.Marshal(b, m, deterministic) -} -func (dst *LoadBalanceResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_LoadBalanceResponse.Merge(dst, src) -} -func (m *LoadBalanceResponse) XXX_Size() int { - return xxx_messageInfo_LoadBalanceResponse.Size(m) -} -func (m *LoadBalanceResponse) XXX_DiscardUnknown() { - xxx_messageInfo_LoadBalanceResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_LoadBalanceResponse proto.InternalMessageInfo - -type isLoadBalanceResponse_LoadBalanceResponseType interface { - isLoadBalanceResponse_LoadBalanceResponseType() -} - -type LoadBalanceResponse_InitialResponse struct { - InitialResponse *InitialLoadBalanceResponse `protobuf:"bytes,1,opt,name=initial_response,json=initialResponse,oneof"` -} -type LoadBalanceResponse_ServerList struct { - ServerList *ServerList `protobuf:"bytes,2,opt,name=server_list,json=serverList,oneof"` -} - -func (*LoadBalanceResponse_InitialResponse) isLoadBalanceResponse_LoadBalanceResponseType() {} -func (*LoadBalanceResponse_ServerList) isLoadBalanceResponse_LoadBalanceResponseType() {} - -func (m *LoadBalanceResponse) GetLoadBalanceResponseType() isLoadBalanceResponse_LoadBalanceResponseType { - if m != nil { - return m.LoadBalanceResponseType - } - return nil -} - -func (m *LoadBalanceResponse) GetInitialResponse() *InitialLoadBalanceResponse { - if x, ok := m.GetLoadBalanceResponseType().(*LoadBalanceResponse_InitialResponse); ok { - return x.InitialResponse - } - return nil -} - -func (m *LoadBalanceResponse) GetServerList() *ServerList { - if x, ok := m.GetLoadBalanceResponseType().(*LoadBalanceResponse_ServerList); ok { - return x.ServerList - } - return nil -} - -// XXX_OneofFuncs is for the internal use of the proto package. -func (*LoadBalanceResponse) XXX_OneofFuncs() (func(msg proto.Message, b *proto.Buffer) error, func(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error), func(msg proto.Message) (n int), []interface{}) { - return _LoadBalanceResponse_OneofMarshaler, _LoadBalanceResponse_OneofUnmarshaler, _LoadBalanceResponse_OneofSizer, []interface{}{ - (*LoadBalanceResponse_InitialResponse)(nil), - (*LoadBalanceResponse_ServerList)(nil), - } -} - -func _LoadBalanceResponse_OneofMarshaler(msg proto.Message, b *proto.Buffer) error { - m := msg.(*LoadBalanceResponse) - // load_balance_response_type - switch x := m.LoadBalanceResponseType.(type) { - case *LoadBalanceResponse_InitialResponse: - b.EncodeVarint(1<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.InitialResponse); err != nil { - return err - } - case *LoadBalanceResponse_ServerList: - b.EncodeVarint(2<<3 | proto.WireBytes) - if err := b.EncodeMessage(x.ServerList); err != nil { - return err - } - case nil: - default: - return fmt.Errorf("LoadBalanceResponse.LoadBalanceResponseType has unexpected type %T", x) - } - return nil -} - -func _LoadBalanceResponse_OneofUnmarshaler(msg proto.Message, tag, wire int, b *proto.Buffer) (bool, error) { - m := msg.(*LoadBalanceResponse) - switch tag { - case 1: // load_balance_response_type.initial_response - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(InitialLoadBalanceResponse) - err := b.DecodeMessage(msg) - m.LoadBalanceResponseType = &LoadBalanceResponse_InitialResponse{msg} - return true, err - case 2: // load_balance_response_type.server_list - if wire != proto.WireBytes { - return true, proto.ErrInternalBadWireType - } - msg := new(ServerList) - err := b.DecodeMessage(msg) - m.LoadBalanceResponseType = &LoadBalanceResponse_ServerList{msg} - return true, err - default: - return false, nil - } -} - -func _LoadBalanceResponse_OneofSizer(msg proto.Message) (n int) { - m := msg.(*LoadBalanceResponse) - // load_balance_response_type - switch x := m.LoadBalanceResponseType.(type) { - case *LoadBalanceResponse_InitialResponse: - s := proto.Size(x.InitialResponse) - n += 1 // tag and wire - n += proto.SizeVarint(uint64(s)) - n += s - case *LoadBalanceResponse_ServerList: - s := proto.Size(x.ServerList) - n += 1 // tag and wire - n += proto.SizeVarint(uint64(s)) - n += s - case nil: - default: - panic(fmt.Sprintf("proto: unexpected type %T in oneof", x)) - } - return n -} - -type InitialLoadBalanceResponse struct { - // This is an application layer redirect that indicates the client should use - // the specified server for load balancing. When this field is non-empty in - // the response, the client should open a separate connection to the - // load_balancer_delegate and call the BalanceLoad method. Its length should - // be less than 64 bytes. - LoadBalancerDelegate string `protobuf:"bytes,1,opt,name=load_balancer_delegate,json=loadBalancerDelegate" json:"load_balancer_delegate,omitempty"` - // This interval defines how often the client should send the client stats - // to the load balancer. Stats should only be reported when the duration is - // positive. - ClientStatsReportInterval *Duration `protobuf:"bytes,2,opt,name=client_stats_report_interval,json=clientStatsReportInterval" json:"client_stats_report_interval,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *InitialLoadBalanceResponse) Reset() { *m = InitialLoadBalanceResponse{} } -func (m *InitialLoadBalanceResponse) String() string { return proto.CompactTextString(m) } -func (*InitialLoadBalanceResponse) ProtoMessage() {} -func (*InitialLoadBalanceResponse) Descriptor() ([]byte, []int) { - return fileDescriptor_messages_b81c731f0e83edbd, []int{6} -} -func (m *InitialLoadBalanceResponse) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_InitialLoadBalanceResponse.Unmarshal(m, b) -} -func (m *InitialLoadBalanceResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_InitialLoadBalanceResponse.Marshal(b, m, deterministic) -} -func (dst *InitialLoadBalanceResponse) XXX_Merge(src proto.Message) { - xxx_messageInfo_InitialLoadBalanceResponse.Merge(dst, src) -} -func (m *InitialLoadBalanceResponse) XXX_Size() int { - return xxx_messageInfo_InitialLoadBalanceResponse.Size(m) -} -func (m *InitialLoadBalanceResponse) XXX_DiscardUnknown() { - xxx_messageInfo_InitialLoadBalanceResponse.DiscardUnknown(m) -} - -var xxx_messageInfo_InitialLoadBalanceResponse proto.InternalMessageInfo - -func (m *InitialLoadBalanceResponse) GetLoadBalancerDelegate() string { - if m != nil { - return m.LoadBalancerDelegate - } - return "" -} - -func (m *InitialLoadBalanceResponse) GetClientStatsReportInterval() *Duration { - if m != nil { - return m.ClientStatsReportInterval - } - return nil -} - -type ServerList struct { - // Contains a list of servers selected by the load balancer. The list will - // be updated when server resolutions change or as needed to balance load - // across more servers. The client should consume the server list in order - // unless instructed otherwise via the client_config. - Servers []*Server `protobuf:"bytes,1,rep,name=servers" json:"servers,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *ServerList) Reset() { *m = ServerList{} } -func (m *ServerList) String() string { return proto.CompactTextString(m) } -func (*ServerList) ProtoMessage() {} -func (*ServerList) Descriptor() ([]byte, []int) { - return fileDescriptor_messages_b81c731f0e83edbd, []int{7} -} -func (m *ServerList) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_ServerList.Unmarshal(m, b) -} -func (m *ServerList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_ServerList.Marshal(b, m, deterministic) -} -func (dst *ServerList) XXX_Merge(src proto.Message) { - xxx_messageInfo_ServerList.Merge(dst, src) -} -func (m *ServerList) XXX_Size() int { - return xxx_messageInfo_ServerList.Size(m) -} -func (m *ServerList) XXX_DiscardUnknown() { - xxx_messageInfo_ServerList.DiscardUnknown(m) -} - -var xxx_messageInfo_ServerList proto.InternalMessageInfo - -func (m *ServerList) GetServers() []*Server { - if m != nil { - return m.Servers - } - return nil -} - -// Contains server information. When none of the [drop_for_*] fields are true, -// use the other fields. When drop_for_rate_limiting is true, ignore all other -// fields. Use drop_for_load_balancing only when it is true and -// drop_for_rate_limiting is false. -type Server struct { - // A resolved address for the server, serialized in network-byte-order. It may - // either be an IPv4 or IPv6 address. - IpAddress []byte `protobuf:"bytes,1,opt,name=ip_address,json=ipAddress,proto3" json:"ip_address,omitempty"` - // A resolved port number for the server. - Port int32 `protobuf:"varint,2,opt,name=port" json:"port,omitempty"` - // An opaque but printable token given to the frontend for each pick. All - // frontend requests for that pick must include the token in its initial - // metadata. The token is used by the backend to verify the request and to - // allow the backend to report load to the gRPC LB system. - // - // Its length is variable but less than 50 bytes. - LoadBalanceToken string `protobuf:"bytes,3,opt,name=load_balance_token,json=loadBalanceToken" json:"load_balance_token,omitempty"` - // Indicates whether this particular request should be dropped by the client - // for rate limiting. - DropForRateLimiting bool `protobuf:"varint,4,opt,name=drop_for_rate_limiting,json=dropForRateLimiting" json:"drop_for_rate_limiting,omitempty"` - // Indicates whether this particular request should be dropped by the client - // for load balancing. - DropForLoadBalancing bool `protobuf:"varint,5,opt,name=drop_for_load_balancing,json=dropForLoadBalancing" json:"drop_for_load_balancing,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` -} - -func (m *Server) Reset() { *m = Server{} } -func (m *Server) String() string { return proto.CompactTextString(m) } -func (*Server) ProtoMessage() {} -func (*Server) Descriptor() ([]byte, []int) { - return fileDescriptor_messages_b81c731f0e83edbd, []int{8} -} -func (m *Server) XXX_Unmarshal(b []byte) error { - return xxx_messageInfo_Server.Unmarshal(m, b) -} -func (m *Server) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { - return xxx_messageInfo_Server.Marshal(b, m, deterministic) -} -func (dst *Server) XXX_Merge(src proto.Message) { - xxx_messageInfo_Server.Merge(dst, src) -} -func (m *Server) XXX_Size() int { - return xxx_messageInfo_Server.Size(m) -} -func (m *Server) XXX_DiscardUnknown() { - xxx_messageInfo_Server.DiscardUnknown(m) -} - -var xxx_messageInfo_Server proto.InternalMessageInfo - -func (m *Server) GetIpAddress() []byte { - if m != nil { - return m.IpAddress - } - return nil -} - -func (m *Server) GetPort() int32 { - if m != nil { - return m.Port - } - return 0 -} - -func (m *Server) GetLoadBalanceToken() string { - if m != nil { - return m.LoadBalanceToken - } - return "" -} - -func (m *Server) GetDropForRateLimiting() bool { - if m != nil { - return m.DropForRateLimiting - } - return false -} - -func (m *Server) GetDropForLoadBalancing() bool { - if m != nil { - return m.DropForLoadBalancing - } - return false -} - -func init() { - proto.RegisterType((*Duration)(nil), "grpc.lb.v1.Duration") - proto.RegisterType((*Timestamp)(nil), "grpc.lb.v1.Timestamp") - proto.RegisterType((*LoadBalanceRequest)(nil), "grpc.lb.v1.LoadBalanceRequest") - proto.RegisterType((*InitialLoadBalanceRequest)(nil), "grpc.lb.v1.InitialLoadBalanceRequest") - proto.RegisterType((*ClientStats)(nil), "grpc.lb.v1.ClientStats") - proto.RegisterType((*LoadBalanceResponse)(nil), "grpc.lb.v1.LoadBalanceResponse") - proto.RegisterType((*InitialLoadBalanceResponse)(nil), "grpc.lb.v1.InitialLoadBalanceResponse") - proto.RegisterType((*ServerList)(nil), "grpc.lb.v1.ServerList") - proto.RegisterType((*Server)(nil), "grpc.lb.v1.Server") -} - -func init() { - proto.RegisterFile("grpc_lb_v1/messages/messages.proto", fileDescriptor_messages_b81c731f0e83edbd) -} - -var fileDescriptor_messages_b81c731f0e83edbd = []byte{ - // 731 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x55, 0xdd, 0x4e, 0x1b, 0x39, - 0x14, 0x26, 0x9b, 0x00, 0xc9, 0x09, 0x5a, 0xb2, 0x26, 0x0b, 0x81, 0x05, 0x89, 0x1d, 0x69, 0xd9, - 0x68, 0xc5, 0x4e, 0x04, 0xd9, 0xbd, 0xe8, 0xcf, 0x45, 0x1b, 0x10, 0x0a, 0x2d, 0x17, 0x95, 0x43, - 0x55, 0xa9, 0x52, 0x65, 0x39, 0x19, 0x33, 0x58, 0x38, 0xf6, 0xd4, 0x76, 0x82, 0xfa, 0x08, 0x7d, - 0x94, 0x3e, 0x46, 0xd5, 0x67, 0xe8, 0xfb, 0x54, 0xe3, 0x99, 0xc9, 0x0c, 0x10, 0x40, 0xbd, 0x89, - 0xec, 0xe3, 0xef, 0x7c, 0xdf, 0xf1, 0x89, 0xbf, 0x33, 0xe0, 0x85, 0x3a, 0x1a, 0x11, 0x31, 0x24, - 0xd3, 0x83, 0xce, 0x98, 0x19, 0x43, 0x43, 0x66, 0x66, 0x0b, 0x3f, 0xd2, 0xca, 0x2a, 0x04, 0x31, - 0xc6, 0x17, 0x43, 0x7f, 0x7a, 0xe0, 0x3d, 0x85, 0xea, 0xf1, 0x44, 0x53, 0xcb, 0x95, 0x44, 0x2d, - 0x58, 0x36, 0x6c, 0xa4, 0x64, 0x60, 0x5a, 0xa5, 0xdd, 0x52, 0xbb, 0x8c, 0xb3, 0x2d, 0x6a, 0xc2, - 0xa2, 0xa4, 0x52, 0x99, 0xd6, 0x2f, 0xbb, 0xa5, 0xf6, 0x22, 0x4e, 0x36, 0xde, 0x33, 0xa8, 0x9d, - 0xf3, 0x31, 0x33, 0x96, 0x8e, 0xa3, 0x9f, 0x4e, 0xfe, 0x5a, 0x02, 0x74, 0xa6, 0x68, 0xd0, 0xa3, - 0x82, 0xca, 0x11, 0xc3, 0xec, 0xe3, 0x84, 0x19, 0x8b, 0xde, 0xc0, 0x2a, 0x97, 0xdc, 0x72, 0x2a, - 0x88, 0x4e, 0x42, 0x8e, 0xae, 0x7e, 0xf8, 0x97, 0x9f, 0x57, 0xed, 0x9f, 0x26, 0x90, 0xbb, 0xf9, - 0xfd, 0x05, 0xfc, 0x6b, 0x9a, 0x9f, 0x31, 0x3e, 0x87, 0x95, 0x91, 0xe0, 0x4c, 0x5a, 0x62, 0x2c, - 0xb5, 0x49, 0x15, 0xf5, 0xc3, 0x8d, 0x22, 0xdd, 0x91, 0x3b, 0x1f, 0xc4, 0xc7, 0xfd, 0x05, 0x5c, - 0x1f, 0xe5, 0xdb, 0xde, 0x1f, 0xb0, 0x29, 0x14, 0x0d, 0xc8, 0x30, 0x91, 0xc9, 0x8a, 0x22, 0xf6, - 0x53, 0xc4, 0xbc, 0x0e, 0x6c, 0xde, 0x5b, 0x09, 0x42, 0x50, 0x91, 0x74, 0xcc, 0x5c, 0xf9, 0x35, - 0xec, 0xd6, 0xde, 0xe7, 0x0a, 0xd4, 0x0b, 0x62, 0xa8, 0x0b, 0x35, 0x9b, 0x75, 0x30, 0xbd, 0xe7, - 0xef, 0xc5, 0xc2, 0x66, 0xed, 0xc5, 0x39, 0x0e, 0xfd, 0x03, 0xbf, 0xc9, 0xc9, 0x98, 0x8c, 0xa8, - 0x10, 0x26, 0xbe, 0x93, 0xb6, 0x2c, 0x70, 0xb7, 0x2a, 0xe3, 0x55, 0x39, 0x19, 0x1f, 0xc5, 0xf1, - 0x41, 0x12, 0x46, 0xfb, 0x80, 0x72, 0xec, 0x05, 0x97, 0xdc, 0x5c, 0xb2, 0xa0, 0x55, 0x76, 0xe0, - 0x46, 0x06, 0x3e, 0x49, 0xe3, 0x88, 0x80, 0x7f, 0x17, 0x4d, 0xae, 0xb9, 0xbd, 0x24, 0x81, 0x56, - 0x11, 0xb9, 0x50, 0x9a, 0x68, 0x6a, 0x19, 0x11, 0x7c, 0xcc, 0x2d, 0x97, 0x61, 0xab, 0xe2, 0x98, - 0xfe, 0xbe, 0xcd, 0xf4, 0x8e, 0xdb, 0xcb, 0x63, 0xad, 0xa2, 0x13, 0xa5, 0x31, 0xb5, 0xec, 0x2c, - 0x85, 0x23, 0x0a, 0x9d, 0x47, 0x05, 0x0a, 0xed, 0x8e, 0x15, 0x16, 0x9d, 0x42, 0xfb, 0x01, 0x85, - 0xbc, 0xf7, 0xb1, 0xc4, 0x07, 0xf8, 0xf7, 0x3e, 0x89, 0xf4, 0x19, 0x5c, 0x50, 0x2e, 0x58, 0x40, - 0xac, 0x22, 0x86, 0xc9, 0xa0, 0xb5, 0xe4, 0x04, 0xf6, 0xe6, 0x09, 0x24, 0x7f, 0xd5, 0x89, 0xc3, - 0x9f, 0xab, 0x01, 0x93, 0x01, 0xea, 0xc3, 0x9f, 0x73, 0xe8, 0xaf, 0xa4, 0xba, 0x96, 0x44, 0xb3, - 0x11, 0xe3, 0x53, 0x16, 0xb4, 0x96, 0x1d, 0xe5, 0xce, 0x6d, 0xca, 0xd7, 0x31, 0x0a, 0xa7, 0x20, - 0xef, 0x5b, 0x09, 0xd6, 0x6e, 0x3c, 0x1b, 0x13, 0x29, 0x69, 0x18, 0x1a, 0x40, 0x23, 0x77, 0x40, - 0x12, 0x4b, 0x9f, 0xc6, 0xde, 0x63, 0x16, 0x48, 0xd0, 0xfd, 0x05, 0xbc, 0x3a, 0xf3, 0x40, 0x4a, - 0xfa, 0x04, 0xea, 0x86, 0xe9, 0x29, 0xd3, 0x44, 0x70, 0x63, 0x53, 0x0f, 0xac, 0x17, 0xf9, 0x06, - 0xee, 0xf8, 0x8c, 0x3b, 0x0f, 0x81, 0x99, 0xed, 0x7a, 0xdb, 0xb0, 0x75, 0xcb, 0x01, 0x09, 0x67, - 0x62, 0x81, 0x2f, 0x25, 0xd8, 0xba, 0xbf, 0x14, 0xf4, 0x1f, 0xac, 0x17, 0x93, 0x35, 0x09, 0x98, - 0x60, 0x21, 0xb5, 0x99, 0x2d, 0x9a, 0x22, 0x4f, 0xd2, 0xc7, 0xe9, 0x19, 0x7a, 0x0b, 0xdb, 0x45, - 0xcb, 0x12, 0xcd, 0x22, 0xa5, 0x2d, 0xe1, 0xd2, 0x32, 0x3d, 0xa5, 0x22, 0x2d, 0xbf, 0x59, 0x2c, - 0x3f, 0x1b, 0x62, 0x78, 0xb3, 0xe0, 0x5e, 0xec, 0xf2, 0x4e, 0xd3, 0x34, 0xef, 0x05, 0x40, 0x7e, - 0x4b, 0xb4, 0x1f, 0x0f, 0xac, 0x78, 0x17, 0x0f, 0xac, 0x72, 0xbb, 0x7e, 0x88, 0xee, 0xb6, 0x03, - 0x67, 0x90, 0x57, 0x95, 0x6a, 0xb9, 0x51, 0xf1, 0xbe, 0x97, 0x60, 0x29, 0x39, 0x41, 0x3b, 0x00, - 0x3c, 0x22, 0x34, 0x08, 0x34, 0x33, 0xc9, 0xc8, 0x5b, 0xc1, 0x35, 0x1e, 0xbd, 0x4c, 0x02, 0xb1, - 0xfb, 0x63, 0xed, 0x74, 0xe6, 0xb9, 0x75, 0x6c, 0xc6, 0x1b, 0x9d, 0xb4, 0xea, 0x8a, 0x49, 0x67, - 0xc6, 0x1a, 0x6e, 0x14, 0x1a, 0x71, 0x1e, 0xc7, 0x51, 0x17, 0xd6, 0x1f, 0x30, 0x5d, 0x15, 0xaf, - 0x05, 0x73, 0x0c, 0xf6, 0x3f, 0x6c, 0x3c, 0x64, 0xa4, 0x2a, 0x6e, 0x06, 0x73, 0x4c, 0xd3, 0xeb, - 0xbe, 0x3f, 0x08, 0x95, 0x0a, 0x05, 0xf3, 0x43, 0x25, 0xa8, 0x0c, 0x7d, 0xa5, 0xc3, 0x4e, 0xdc, - 0x0d, 0xf7, 0x23, 0x86, 0x9d, 0x39, 0x5f, 0x95, 0xe1, 0x92, 0xfb, 0x9a, 0x74, 0x7f, 0x04, 0x00, - 0x00, 0xff, 0xff, 0x8e, 0xd0, 0x70, 0xb7, 0x73, 0x06, 0x00, 0x00, -} diff --git a/vendor/google.golang.org/grpc/grpclb_picker.go b/vendor/google.golang.org/grpc/grpclb_picker.go deleted file mode 100644 index 872c7ccea0e..00000000000 --- a/vendor/google.golang.org/grpc/grpclb_picker.go +++ /dev/null @@ -1,159 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "sync" - "sync/atomic" - - "golang.org/x/net/context" - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/codes" - lbpb "google.golang.org/grpc/grpclb/grpc_lb_v1/messages" - "google.golang.org/grpc/status" -) - -type rpcStats struct { - NumCallsStarted int64 - NumCallsFinished int64 - NumCallsFinishedWithDropForRateLimiting int64 - NumCallsFinishedWithDropForLoadBalancing int64 - NumCallsFinishedWithClientFailedToSend int64 - NumCallsFinishedKnownReceived int64 -} - -// toClientStats converts rpcStats to lbpb.ClientStats, and clears rpcStats. -func (s *rpcStats) toClientStats() *lbpb.ClientStats { - stats := &lbpb.ClientStats{ - NumCallsStarted: atomic.SwapInt64(&s.NumCallsStarted, 0), - NumCallsFinished: atomic.SwapInt64(&s.NumCallsFinished, 0), - NumCallsFinishedWithDropForRateLimiting: atomic.SwapInt64(&s.NumCallsFinishedWithDropForRateLimiting, 0), - NumCallsFinishedWithDropForLoadBalancing: atomic.SwapInt64(&s.NumCallsFinishedWithDropForLoadBalancing, 0), - NumCallsFinishedWithClientFailedToSend: atomic.SwapInt64(&s.NumCallsFinishedWithClientFailedToSend, 0), - NumCallsFinishedKnownReceived: atomic.SwapInt64(&s.NumCallsFinishedKnownReceived, 0), - } - return stats -} - -func (s *rpcStats) dropForRateLimiting() { - atomic.AddInt64(&s.NumCallsStarted, 1) - atomic.AddInt64(&s.NumCallsFinishedWithDropForRateLimiting, 1) - atomic.AddInt64(&s.NumCallsFinished, 1) -} - -func (s *rpcStats) dropForLoadBalancing() { - atomic.AddInt64(&s.NumCallsStarted, 1) - atomic.AddInt64(&s.NumCallsFinishedWithDropForLoadBalancing, 1) - atomic.AddInt64(&s.NumCallsFinished, 1) -} - -func (s *rpcStats) failedToSend() { - atomic.AddInt64(&s.NumCallsStarted, 1) - atomic.AddInt64(&s.NumCallsFinishedWithClientFailedToSend, 1) - atomic.AddInt64(&s.NumCallsFinished, 1) -} - -func (s *rpcStats) knownReceived() { - atomic.AddInt64(&s.NumCallsStarted, 1) - atomic.AddInt64(&s.NumCallsFinishedKnownReceived, 1) - atomic.AddInt64(&s.NumCallsFinished, 1) -} - -type errPicker struct { - // Pick always returns this err. - err error -} - -func (p *errPicker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) { - return nil, nil, p.err -} - -// rrPicker does roundrobin on subConns. It's typically used when there's no -// response from remote balancer, and grpclb falls back to the resolved -// backends. -// -// It guaranteed that len(subConns) > 0. -type rrPicker struct { - mu sync.Mutex - subConns []balancer.SubConn // The subConns that were READY when taking the snapshot. - subConnsNext int -} - -func (p *rrPicker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) { - p.mu.Lock() - defer p.mu.Unlock() - sc := p.subConns[p.subConnsNext] - p.subConnsNext = (p.subConnsNext + 1) % len(p.subConns) - return sc, nil, nil -} - -// lbPicker does two layers of picks: -// -// First layer: roundrobin on all servers in serverList, including drops and backends. -// - If it picks a drop, the RPC will fail as being dropped. -// - If it picks a backend, do a second layer pick to pick the real backend. -// -// Second layer: roundrobin on all READY backends. -// -// It's guaranteed that len(serverList) > 0. -type lbPicker struct { - mu sync.Mutex - serverList []*lbpb.Server - serverListNext int - subConns []balancer.SubConn // The subConns that were READY when taking the snapshot. - subConnsNext int - - stats *rpcStats -} - -func (p *lbPicker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) { - p.mu.Lock() - defer p.mu.Unlock() - - // Layer one roundrobin on serverList. - s := p.serverList[p.serverListNext] - p.serverListNext = (p.serverListNext + 1) % len(p.serverList) - - // If it's a drop, return an error and fail the RPC. - if s.DropForRateLimiting { - p.stats.dropForRateLimiting() - return nil, nil, status.Errorf(codes.Unavailable, "request dropped by grpclb") - } - if s.DropForLoadBalancing { - p.stats.dropForLoadBalancing() - return nil, nil, status.Errorf(codes.Unavailable, "request dropped by grpclb") - } - - // If not a drop but there's no ready subConns. - if len(p.subConns) <= 0 { - return nil, nil, balancer.ErrNoSubConnAvailable - } - - // Return the next ready subConn in the list, also collect rpc stats. - sc := p.subConns[p.subConnsNext] - p.subConnsNext = (p.subConnsNext + 1) % len(p.subConns) - done := func(info balancer.DoneInfo) { - if !info.BytesSent { - p.stats.failedToSend() - } else if info.BytesReceived { - p.stats.knownReceived() - } - } - return sc, done, nil -} diff --git a/vendor/google.golang.org/grpc/grpclb_remote_balancer.go b/vendor/google.golang.org/grpc/grpclb_remote_balancer.go deleted file mode 100644 index b8dd4f18ce5..00000000000 --- a/vendor/google.golang.org/grpc/grpclb_remote_balancer.go +++ /dev/null @@ -1,266 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "fmt" - "net" - "reflect" - "time" - - "golang.org/x/net/context" - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/channelz" - - "google.golang.org/grpc/connectivity" - lbpb "google.golang.org/grpc/grpclb/grpc_lb_v1/messages" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/resolver" -) - -// processServerList updates balaner's internal state, create/remove SubConns -// and regenerates picker using the received serverList. -func (lb *lbBalancer) processServerList(l *lbpb.ServerList) { - grpclog.Infof("lbBalancer: processing server list: %+v", l) - lb.mu.Lock() - defer lb.mu.Unlock() - - // Set serverListReceived to true so fallback will not take effect if it has - // not hit timeout. - lb.serverListReceived = true - - // If the new server list == old server list, do nothing. - if reflect.DeepEqual(lb.fullServerList, l.Servers) { - grpclog.Infof("lbBalancer: new serverlist same as the previous one, ignoring") - return - } - lb.fullServerList = l.Servers - - var backendAddrs []resolver.Address - for _, s := range l.Servers { - if s.DropForLoadBalancing || s.DropForRateLimiting { - continue - } - - md := metadata.Pairs(lbTokeyKey, s.LoadBalanceToken) - ip := net.IP(s.IpAddress) - ipStr := ip.String() - if ip.To4() == nil { - // Add square brackets to ipv6 addresses, otherwise net.Dial() and - // net.SplitHostPort() will return too many colons error. - ipStr = fmt.Sprintf("[%s]", ipStr) - } - addr := resolver.Address{ - Addr: fmt.Sprintf("%s:%d", ipStr, s.Port), - Metadata: &md, - } - - backendAddrs = append(backendAddrs, addr) - } - - // Call refreshSubConns to create/remove SubConns. - lb.refreshSubConns(backendAddrs) - // Regenerate and update picker no matter if there's update on backends (if - // any SubConn will be newed/removed). Because since the full serverList was - // different, there might be updates in drops or pick weights(different - // number of duplicates). We need to update picker with the fulllist. - // - // Now with cache, even if SubConn was newed/removed, there might be no - // state changes. - lb.regeneratePicker() - lb.cc.UpdateBalancerState(lb.state, lb.picker) -} - -// refreshSubConns creates/removes SubConns with backendAddrs. It returns a bool -// indicating whether the backendAddrs are different from the cached -// backendAddrs (whether any SubConn was newed/removed). -// Caller must hold lb.mu. -func (lb *lbBalancer) refreshSubConns(backendAddrs []resolver.Address) bool { - lb.backendAddrs = nil - var backendsUpdated bool - // addrsSet is the set converted from backendAddrs, it's used to quick - // lookup for an address. - addrsSet := make(map[resolver.Address]struct{}) - // Create new SubConns. - for _, addr := range backendAddrs { - addrWithoutMD := addr - addrWithoutMD.Metadata = nil - addrsSet[addrWithoutMD] = struct{}{} - lb.backendAddrs = append(lb.backendAddrs, addrWithoutMD) - - if _, ok := lb.subConns[addrWithoutMD]; !ok { - backendsUpdated = true - - // Use addrWithMD to create the SubConn. - sc, err := lb.cc.NewSubConn([]resolver.Address{addr}, balancer.NewSubConnOptions{}) - if err != nil { - grpclog.Warningf("roundrobinBalancer: failed to create new SubConn: %v", err) - continue - } - lb.subConns[addrWithoutMD] = sc // Use the addr without MD as key for the map. - if _, ok := lb.scStates[sc]; !ok { - // Only set state of new sc to IDLE. The state could already be - // READY for cached SubConns. - lb.scStates[sc] = connectivity.Idle - } - sc.Connect() - } - } - - for a, sc := range lb.subConns { - // a was removed by resolver. - if _, ok := addrsSet[a]; !ok { - backendsUpdated = true - - lb.cc.RemoveSubConn(sc) - delete(lb.subConns, a) - // Keep the state of this sc in b.scStates until sc's state becomes Shutdown. - // The entry will be deleted in HandleSubConnStateChange. - } - } - - return backendsUpdated -} - -func (lb *lbBalancer) readServerList(s *balanceLoadClientStream) error { - for { - reply, err := s.Recv() - if err != nil { - return fmt.Errorf("grpclb: failed to recv server list: %v", err) - } - if serverList := reply.GetServerList(); serverList != nil { - lb.processServerList(serverList) - } - } -} - -func (lb *lbBalancer) sendLoadReport(s *balanceLoadClientStream, interval time.Duration) { - ticker := time.NewTicker(interval) - defer ticker.Stop() - for { - select { - case <-ticker.C: - case <-s.Context().Done(): - return - } - stats := lb.clientStats.toClientStats() - t := time.Now() - stats.Timestamp = &lbpb.Timestamp{ - Seconds: t.Unix(), - Nanos: int32(t.Nanosecond()), - } - if err := s.Send(&lbpb.LoadBalanceRequest{ - LoadBalanceRequestType: &lbpb.LoadBalanceRequest_ClientStats{ - ClientStats: stats, - }, - }); err != nil { - return - } - } -} - -func (lb *lbBalancer) callRemoteBalancer() error { - lbClient := &loadBalancerClient{cc: lb.ccRemoteLB} - ctx, cancel := context.WithCancel(context.Background()) - defer cancel() - stream, err := lbClient.BalanceLoad(ctx, FailFast(false)) - if err != nil { - return fmt.Errorf("grpclb: failed to perform RPC to the remote balancer %v", err) - } - - // grpclb handshake on the stream. - initReq := &lbpb.LoadBalanceRequest{ - LoadBalanceRequestType: &lbpb.LoadBalanceRequest_InitialRequest{ - InitialRequest: &lbpb.InitialLoadBalanceRequest{ - Name: lb.target, - }, - }, - } - if err := stream.Send(initReq); err != nil { - return fmt.Errorf("grpclb: failed to send init request: %v", err) - } - reply, err := stream.Recv() - if err != nil { - return fmt.Errorf("grpclb: failed to recv init response: %v", err) - } - initResp := reply.GetInitialResponse() - if initResp == nil { - return fmt.Errorf("grpclb: reply from remote balancer did not include initial response") - } - if initResp.LoadBalancerDelegate != "" { - return fmt.Errorf("grpclb: Delegation is not supported") - } - - go func() { - if d := convertDuration(initResp.ClientStatsReportInterval); d > 0 { - lb.sendLoadReport(stream, d) - } - }() - return lb.readServerList(stream) -} - -func (lb *lbBalancer) watchRemoteBalancer() { - for { - err := lb.callRemoteBalancer() - select { - case <-lb.doneCh: - return - default: - if err != nil { - grpclog.Error(err) - } - } - - } -} - -func (lb *lbBalancer) dialRemoteLB(remoteLBName string) { - var dopts []DialOption - if creds := lb.opt.DialCreds; creds != nil { - if err := creds.OverrideServerName(remoteLBName); err == nil { - dopts = append(dopts, WithTransportCredentials(creds)) - } else { - grpclog.Warningf("grpclb: failed to override the server name in the credentials: %v, using Insecure", err) - dopts = append(dopts, WithInsecure()) - } - } else { - dopts = append(dopts, WithInsecure()) - } - if lb.opt.Dialer != nil { - // WithDialer takes a different type of function, so we instead use a - // special DialOption here. - dopts = append(dopts, withContextDialer(lb.opt.Dialer)) - } - // Explicitly set pickfirst as the balancer. - dopts = append(dopts, WithBalancerName(PickFirstBalancerName)) - dopts = append(dopts, withResolverBuilder(lb.manualResolver)) - if channelz.IsOn() { - dopts = append(dopts, WithChannelzParentID(lb.opt.ChannelzParentID)) - } - - // DialContext using manualResolver.Scheme, which is a random scheme generated - // when init grpclb. The target name is not important. - cc, err := DialContext(context.Background(), "grpclb:///grpclb.server", dopts...) - if err != nil { - grpclog.Fatalf("failed to dial: %v", err) - } - lb.ccRemoteLB = cc - go lb.watchRemoteBalancer() -} diff --git a/vendor/google.golang.org/grpc/grpclb_util.go b/vendor/google.golang.org/grpc/grpclb_util.go deleted file mode 100644 index 063ba9d8590..00000000000 --- a/vendor/google.golang.org/grpc/grpclb_util.go +++ /dev/null @@ -1,214 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "fmt" - "sync" - "time" - - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/resolver" -) - -// The parent ClientConn should re-resolve when grpclb loses connection to the -// remote balancer. When the ClientConn inside grpclb gets a TransientFailure, -// it calls lbManualResolver.ResolveNow(), which calls parent ClientConn's -// ResolveNow, and eventually results in re-resolve happening in parent -// ClientConn's resolver (DNS for example). -// -// parent -// ClientConn -// +-----------------------------------------------------------------+ -// | parent +---------------------------------+ | -// | DNS ClientConn | grpclb | | -// | resolver balancerWrapper | | | -// | + + | grpclb grpclb | | -// | | | | ManualResolver ClientConn | | -// | | | | + + | | -// | | | | | | Transient | | -// | | | | | | Failure | | -// | | | | | <--------- | | | -// | | | <--------------- | ResolveNow | | | -// | | <--------- | ResolveNow | | | | | -// | | ResolveNow | | | | | | -// | | | | | | | | -// | + + | + + | | -// | +---------------------------------+ | -// +-----------------------------------------------------------------+ - -// lbManualResolver is used by the ClientConn inside grpclb. It's a manual -// resolver with a special ResolveNow() function. -// -// When ResolveNow() is called, it calls ResolveNow() on the parent ClientConn, -// so when grpclb client lose contact with remote balancers, the parent -// ClientConn's resolver will re-resolve. -type lbManualResolver struct { - scheme string - ccr resolver.ClientConn - - ccb balancer.ClientConn -} - -func (r *lbManualResolver) Build(_ resolver.Target, cc resolver.ClientConn, _ resolver.BuildOption) (resolver.Resolver, error) { - r.ccr = cc - return r, nil -} - -func (r *lbManualResolver) Scheme() string { - return r.scheme -} - -// ResolveNow calls resolveNow on the parent ClientConn. -func (r *lbManualResolver) ResolveNow(o resolver.ResolveNowOption) { - r.ccb.ResolveNow(o) -} - -// Close is a noop for Resolver. -func (*lbManualResolver) Close() {} - -// NewAddress calls cc.NewAddress. -func (r *lbManualResolver) NewAddress(addrs []resolver.Address) { - r.ccr.NewAddress(addrs) -} - -// NewServiceConfig calls cc.NewServiceConfig. -func (r *lbManualResolver) NewServiceConfig(sc string) { - r.ccr.NewServiceConfig(sc) -} - -const subConnCacheTime = time.Second * 10 - -// lbCacheClientConn is a wrapper balancer.ClientConn with a SubConn cache. -// SubConns will be kept in cache for subConnCacheTime before being removed. -// -// Its new and remove methods are updated to do cache first. -type lbCacheClientConn struct { - cc balancer.ClientConn - timeout time.Duration - - mu sync.Mutex - // subConnCache only keeps subConns that are being deleted. - subConnCache map[resolver.Address]*subConnCacheEntry - subConnToAddr map[balancer.SubConn]resolver.Address -} - -type subConnCacheEntry struct { - sc balancer.SubConn - - cancel func() - abortDeleting bool -} - -func newLBCacheClientConn(cc balancer.ClientConn) *lbCacheClientConn { - return &lbCacheClientConn{ - cc: cc, - timeout: subConnCacheTime, - subConnCache: make(map[resolver.Address]*subConnCacheEntry), - subConnToAddr: make(map[balancer.SubConn]resolver.Address), - } -} - -func (ccc *lbCacheClientConn) NewSubConn(addrs []resolver.Address, opts balancer.NewSubConnOptions) (balancer.SubConn, error) { - if len(addrs) != 1 { - return nil, fmt.Errorf("grpclb calling NewSubConn with addrs of length %v", len(addrs)) - } - addrWithoutMD := addrs[0] - addrWithoutMD.Metadata = nil - - ccc.mu.Lock() - defer ccc.mu.Unlock() - if entry, ok := ccc.subConnCache[addrWithoutMD]; ok { - // If entry is in subConnCache, the SubConn was being deleted. - // cancel function will never be nil. - entry.cancel() - delete(ccc.subConnCache, addrWithoutMD) - return entry.sc, nil - } - - scNew, err := ccc.cc.NewSubConn(addrs, opts) - if err != nil { - return nil, err - } - - ccc.subConnToAddr[scNew] = addrWithoutMD - return scNew, nil -} - -func (ccc *lbCacheClientConn) RemoveSubConn(sc balancer.SubConn) { - ccc.mu.Lock() - defer ccc.mu.Unlock() - addr, ok := ccc.subConnToAddr[sc] - if !ok { - return - } - - if entry, ok := ccc.subConnCache[addr]; ok { - if entry.sc != sc { - // This could happen if NewSubConn was called multiple times for the - // same address, and those SubConns are all removed. We remove sc - // immediately here. - delete(ccc.subConnToAddr, sc) - ccc.cc.RemoveSubConn(sc) - } - return - } - - entry := &subConnCacheEntry{ - sc: sc, - } - ccc.subConnCache[addr] = entry - - timer := time.AfterFunc(ccc.timeout, func() { - ccc.mu.Lock() - if entry.abortDeleting { - return - } - ccc.cc.RemoveSubConn(sc) - delete(ccc.subConnToAddr, sc) - delete(ccc.subConnCache, addr) - ccc.mu.Unlock() - }) - entry.cancel = func() { - if !timer.Stop() { - // If stop was not successful, the timer has fired (this can only - // happen in a race). But the deleting function is blocked on ccc.mu - // because the mutex was held by the caller of this function. - // - // Set abortDeleting to true to abort the deleting function. When - // the lock is released, the deleting function will acquire the - // lock, check the value of abortDeleting and return. - entry.abortDeleting = true - } - } -} - -func (ccc *lbCacheClientConn) UpdateBalancerState(s connectivity.State, p balancer.Picker) { - ccc.cc.UpdateBalancerState(s, p) -} - -func (ccc *lbCacheClientConn) close() { - ccc.mu.Lock() - // Only cancel all existing timers. There's no need to remove SubConns. - for _, entry := range ccc.subConnCache { - entry.cancel() - } - ccc.mu.Unlock() -} diff --git a/vendor/google.golang.org/grpc/grpclog/grpclog.go b/vendor/google.golang.org/grpc/grpclog/grpclog.go deleted file mode 100644 index 1fabb11e1ba..00000000000 --- a/vendor/google.golang.org/grpc/grpclog/grpclog.go +++ /dev/null @@ -1,126 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package grpclog defines logging for grpc. -// -// All logs in transport package only go to verbose level 2. -// All logs in other packages in grpc are logged in spite of the verbosity level. -// -// In the default logger, -// severity level can be set by environment variable GRPC_GO_LOG_SEVERITY_LEVEL, -// verbosity level can be set by GRPC_GO_LOG_VERBOSITY_LEVEL. -package grpclog // import "google.golang.org/grpc/grpclog" - -import "os" - -var logger = newLoggerV2() - -// V reports whether verbosity level l is at least the requested verbose level. -func V(l int) bool { - return logger.V(l) -} - -// Info logs to the INFO log. -func Info(args ...interface{}) { - logger.Info(args...) -} - -// Infof logs to the INFO log. Arguments are handled in the manner of fmt.Printf. -func Infof(format string, args ...interface{}) { - logger.Infof(format, args...) -} - -// Infoln logs to the INFO log. Arguments are handled in the manner of fmt.Println. -func Infoln(args ...interface{}) { - logger.Infoln(args...) -} - -// Warning logs to the WARNING log. -func Warning(args ...interface{}) { - logger.Warning(args...) -} - -// Warningf logs to the WARNING log. Arguments are handled in the manner of fmt.Printf. -func Warningf(format string, args ...interface{}) { - logger.Warningf(format, args...) -} - -// Warningln logs to the WARNING log. Arguments are handled in the manner of fmt.Println. -func Warningln(args ...interface{}) { - logger.Warningln(args...) -} - -// Error logs to the ERROR log. -func Error(args ...interface{}) { - logger.Error(args...) -} - -// Errorf logs to the ERROR log. Arguments are handled in the manner of fmt.Printf. -func Errorf(format string, args ...interface{}) { - logger.Errorf(format, args...) -} - -// Errorln logs to the ERROR log. Arguments are handled in the manner of fmt.Println. -func Errorln(args ...interface{}) { - logger.Errorln(args...) -} - -// Fatal logs to the FATAL log. Arguments are handled in the manner of fmt.Print. -// It calls os.Exit() with exit code 1. -func Fatal(args ...interface{}) { - logger.Fatal(args...) - // Make sure fatal logs will exit. - os.Exit(1) -} - -// Fatalf logs to the FATAL log. Arguments are handled in the manner of fmt.Printf. -// It calles os.Exit() with exit code 1. -func Fatalf(format string, args ...interface{}) { - logger.Fatalf(format, args...) - // Make sure fatal logs will exit. - os.Exit(1) -} - -// Fatalln logs to the FATAL log. Arguments are handled in the manner of fmt.Println. -// It calle os.Exit()) with exit code 1. -func Fatalln(args ...interface{}) { - logger.Fatalln(args...) - // Make sure fatal logs will exit. - os.Exit(1) -} - -// Print prints to the logger. Arguments are handled in the manner of fmt.Print. -// -// Deprecated: use Info. -func Print(args ...interface{}) { - logger.Info(args...) -} - -// Printf prints to the logger. Arguments are handled in the manner of fmt.Printf. -// -// Deprecated: use Infof. -func Printf(format string, args ...interface{}) { - logger.Infof(format, args...) -} - -// Println prints to the logger. Arguments are handled in the manner of fmt.Println. -// -// Deprecated: use Infoln. -func Println(args ...interface{}) { - logger.Infoln(args...) -} diff --git a/vendor/google.golang.org/grpc/grpclog/logger.go b/vendor/google.golang.org/grpc/grpclog/logger.go deleted file mode 100644 index 097494f710f..00000000000 --- a/vendor/google.golang.org/grpc/grpclog/logger.go +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpclog - -// Logger mimics golang's standard Logger as an interface. -// -// Deprecated: use LoggerV2. -type Logger interface { - Fatal(args ...interface{}) - Fatalf(format string, args ...interface{}) - Fatalln(args ...interface{}) - Print(args ...interface{}) - Printf(format string, args ...interface{}) - Println(args ...interface{}) -} - -// SetLogger sets the logger that is used in grpc. Call only from -// init() functions. -// -// Deprecated: use SetLoggerV2. -func SetLogger(l Logger) { - logger = &loggerWrapper{Logger: l} -} - -// loggerWrapper wraps Logger into a LoggerV2. -type loggerWrapper struct { - Logger -} - -func (g *loggerWrapper) Info(args ...interface{}) { - g.Logger.Print(args...) -} - -func (g *loggerWrapper) Infoln(args ...interface{}) { - g.Logger.Println(args...) -} - -func (g *loggerWrapper) Infof(format string, args ...interface{}) { - g.Logger.Printf(format, args...) -} - -func (g *loggerWrapper) Warning(args ...interface{}) { - g.Logger.Print(args...) -} - -func (g *loggerWrapper) Warningln(args ...interface{}) { - g.Logger.Println(args...) -} - -func (g *loggerWrapper) Warningf(format string, args ...interface{}) { - g.Logger.Printf(format, args...) -} - -func (g *loggerWrapper) Error(args ...interface{}) { - g.Logger.Print(args...) -} - -func (g *loggerWrapper) Errorln(args ...interface{}) { - g.Logger.Println(args...) -} - -func (g *loggerWrapper) Errorf(format string, args ...interface{}) { - g.Logger.Printf(format, args...) -} - -func (g *loggerWrapper) V(l int) bool { - // Returns true for all verbose level. - return true -} diff --git a/vendor/google.golang.org/grpc/grpclog/loggerv2.go b/vendor/google.golang.org/grpc/grpclog/loggerv2.go deleted file mode 100644 index d4932577695..00000000000 --- a/vendor/google.golang.org/grpc/grpclog/loggerv2.go +++ /dev/null @@ -1,195 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpclog - -import ( - "io" - "io/ioutil" - "log" - "os" - "strconv" -) - -// LoggerV2 does underlying logging work for grpclog. -type LoggerV2 interface { - // Info logs to INFO log. Arguments are handled in the manner of fmt.Print. - Info(args ...interface{}) - // Infoln logs to INFO log. Arguments are handled in the manner of fmt.Println. - Infoln(args ...interface{}) - // Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf. - Infof(format string, args ...interface{}) - // Warning logs to WARNING log. Arguments are handled in the manner of fmt.Print. - Warning(args ...interface{}) - // Warningln logs to WARNING log. Arguments are handled in the manner of fmt.Println. - Warningln(args ...interface{}) - // Warningf logs to WARNING log. Arguments are handled in the manner of fmt.Printf. - Warningf(format string, args ...interface{}) - // Error logs to ERROR log. Arguments are handled in the manner of fmt.Print. - Error(args ...interface{}) - // Errorln logs to ERROR log. Arguments are handled in the manner of fmt.Println. - Errorln(args ...interface{}) - // Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf. - Errorf(format string, args ...interface{}) - // Fatal logs to ERROR log. Arguments are handled in the manner of fmt.Print. - // gRPC ensures that all Fatal logs will exit with os.Exit(1). - // Implementations may also call os.Exit() with a non-zero exit code. - Fatal(args ...interface{}) - // Fatalln logs to ERROR log. Arguments are handled in the manner of fmt.Println. - // gRPC ensures that all Fatal logs will exit with os.Exit(1). - // Implementations may also call os.Exit() with a non-zero exit code. - Fatalln(args ...interface{}) - // Fatalf logs to ERROR log. Arguments are handled in the manner of fmt.Printf. - // gRPC ensures that all Fatal logs will exit with os.Exit(1). - // Implementations may also call os.Exit() with a non-zero exit code. - Fatalf(format string, args ...interface{}) - // V reports whether verbosity level l is at least the requested verbose level. - V(l int) bool -} - -// SetLoggerV2 sets logger that is used in grpc to a V2 logger. -// Not mutex-protected, should be called before any gRPC functions. -func SetLoggerV2(l LoggerV2) { - logger = l -} - -const ( - // infoLog indicates Info severity. - infoLog int = iota - // warningLog indicates Warning severity. - warningLog - // errorLog indicates Error severity. - errorLog - // fatalLog indicates Fatal severity. - fatalLog -) - -// severityName contains the string representation of each severity. -var severityName = []string{ - infoLog: "INFO", - warningLog: "WARNING", - errorLog: "ERROR", - fatalLog: "FATAL", -} - -// loggerT is the default logger used by grpclog. -type loggerT struct { - m []*log.Logger - v int -} - -// NewLoggerV2 creates a loggerV2 with the provided writers. -// Fatal logs will be written to errorW, warningW, infoW, followed by exit(1). -// Error logs will be written to errorW, warningW and infoW. -// Warning logs will be written to warningW and infoW. -// Info logs will be written to infoW. -func NewLoggerV2(infoW, warningW, errorW io.Writer) LoggerV2 { - return NewLoggerV2WithVerbosity(infoW, warningW, errorW, 0) -} - -// NewLoggerV2WithVerbosity creates a loggerV2 with the provided writers and -// verbosity level. -func NewLoggerV2WithVerbosity(infoW, warningW, errorW io.Writer, v int) LoggerV2 { - var m []*log.Logger - m = append(m, log.New(infoW, severityName[infoLog]+": ", log.LstdFlags)) - m = append(m, log.New(io.MultiWriter(infoW, warningW), severityName[warningLog]+": ", log.LstdFlags)) - ew := io.MultiWriter(infoW, warningW, errorW) // ew will be used for error and fatal. - m = append(m, log.New(ew, severityName[errorLog]+": ", log.LstdFlags)) - m = append(m, log.New(ew, severityName[fatalLog]+": ", log.LstdFlags)) - return &loggerT{m: m, v: v} -} - -// newLoggerV2 creates a loggerV2 to be used as default logger. -// All logs are written to stderr. -func newLoggerV2() LoggerV2 { - errorW := ioutil.Discard - warningW := ioutil.Discard - infoW := ioutil.Discard - - logLevel := os.Getenv("GRPC_GO_LOG_SEVERITY_LEVEL") - switch logLevel { - case "", "ERROR", "error": // If env is unset, set level to ERROR. - errorW = os.Stderr - case "WARNING", "warning": - warningW = os.Stderr - case "INFO", "info": - infoW = os.Stderr - } - - var v int - vLevel := os.Getenv("GRPC_GO_LOG_VERBOSITY_LEVEL") - if vl, err := strconv.Atoi(vLevel); err == nil { - v = vl - } - return NewLoggerV2WithVerbosity(infoW, warningW, errorW, v) -} - -func (g *loggerT) Info(args ...interface{}) { - g.m[infoLog].Print(args...) -} - -func (g *loggerT) Infoln(args ...interface{}) { - g.m[infoLog].Println(args...) -} - -func (g *loggerT) Infof(format string, args ...interface{}) { - g.m[infoLog].Printf(format, args...) -} - -func (g *loggerT) Warning(args ...interface{}) { - g.m[warningLog].Print(args...) -} - -func (g *loggerT) Warningln(args ...interface{}) { - g.m[warningLog].Println(args...) -} - -func (g *loggerT) Warningf(format string, args ...interface{}) { - g.m[warningLog].Printf(format, args...) -} - -func (g *loggerT) Error(args ...interface{}) { - g.m[errorLog].Print(args...) -} - -func (g *loggerT) Errorln(args ...interface{}) { - g.m[errorLog].Println(args...) -} - -func (g *loggerT) Errorf(format string, args ...interface{}) { - g.m[errorLog].Printf(format, args...) -} - -func (g *loggerT) Fatal(args ...interface{}) { - g.m[fatalLog].Fatal(args...) - // No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit(). -} - -func (g *loggerT) Fatalln(args ...interface{}) { - g.m[fatalLog].Fatalln(args...) - // No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit(). -} - -func (g *loggerT) Fatalf(format string, args ...interface{}) { - g.m[fatalLog].Fatalf(format, args...) - // No need to call os.Exit() again because log.Logger.Fatal() calls os.Exit(). -} - -func (g *loggerT) V(l int) bool { - return l <= g.v -} diff --git a/vendor/google.golang.org/grpc/interceptor.go b/vendor/google.golang.org/grpc/interceptor.go deleted file mode 100644 index 1f6ef678035..00000000000 --- a/vendor/google.golang.org/grpc/interceptor.go +++ /dev/null @@ -1,77 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "golang.org/x/net/context" -) - -// UnaryInvoker is called by UnaryClientInterceptor to complete RPCs. -type UnaryInvoker func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, opts ...CallOption) error - -// UnaryClientInterceptor intercepts the execution of a unary RPC on the client. invoker is the handler to complete the RPC -// and it is the responsibility of the interceptor to call it. -// This is an EXPERIMENTAL API. -type UnaryClientInterceptor func(ctx context.Context, method string, req, reply interface{}, cc *ClientConn, invoker UnaryInvoker, opts ...CallOption) error - -// Streamer is called by StreamClientInterceptor to create a ClientStream. -type Streamer func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (ClientStream, error) - -// StreamClientInterceptor intercepts the creation of ClientStream. It may return a custom ClientStream to intercept all I/O -// operations. streamer is the handler to create a ClientStream and it is the responsibility of the interceptor to call it. -// This is an EXPERIMENTAL API. -type StreamClientInterceptor func(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, streamer Streamer, opts ...CallOption) (ClientStream, error) - -// UnaryServerInfo consists of various information about a unary RPC on -// server side. All per-rpc information may be mutated by the interceptor. -type UnaryServerInfo struct { - // Server is the service implementation the user provides. This is read-only. - Server interface{} - // FullMethod is the full RPC method string, i.e., /package.service/method. - FullMethod string -} - -// UnaryHandler defines the handler invoked by UnaryServerInterceptor to complete the normal -// execution of a unary RPC. If a UnaryHandler returns an error, it should be produced by the -// status package, or else gRPC will use codes.Unknown as the status code and err.Error() as -// the status message of the RPC. -type UnaryHandler func(ctx context.Context, req interface{}) (interface{}, error) - -// UnaryServerInterceptor provides a hook to intercept the execution of a unary RPC on the server. info -// contains all the information of this RPC the interceptor can operate on. And handler is the wrapper -// of the service method implementation. It is the responsibility of the interceptor to invoke handler -// to complete the RPC. -type UnaryServerInterceptor func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (resp interface{}, err error) - -// StreamServerInfo consists of various information about a streaming RPC on -// server side. All per-rpc information may be mutated by the interceptor. -type StreamServerInfo struct { - // FullMethod is the full RPC method string, i.e., /package.service/method. - FullMethod string - // IsClientStream indicates whether the RPC is a client streaming RPC. - IsClientStream bool - // IsServerStream indicates whether the RPC is a server streaming RPC. - IsServerStream bool -} - -// StreamServerInterceptor provides a hook to intercept the execution of a streaming RPC on the server. -// info contains all the information of this RPC the interceptor can operate on. And handler is the -// service method implementation. It is the responsibility of the interceptor to invoke handler to -// complete the RPC. -type StreamServerInterceptor func(srv interface{}, ss ServerStream, info *StreamServerInfo, handler StreamHandler) error diff --git a/vendor/google.golang.org/grpc/internal/internal.go b/vendor/google.golang.org/grpc/internal/internal.go deleted file mode 100644 index 53f1775201c..00000000000 --- a/vendor/google.golang.org/grpc/internal/internal.go +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package internal contains gRPC-internal code for testing, to avoid polluting -// the godoc of the top-level grpc package. -package internal - -// TestingUseHandlerImpl enables the http.Handler-based server implementation. -// It must be called before Serve and requires TLS credentials. -// -// The provided grpcServer must be of type *grpc.Server. It is untyped -// for circular dependency reasons. -var TestingUseHandlerImpl func(grpcServer interface{}) diff --git a/vendor/google.golang.org/grpc/keepalive/keepalive.go b/vendor/google.golang.org/grpc/keepalive/keepalive.go deleted file mode 100644 index f8adc7e6d4f..00000000000 --- a/vendor/google.golang.org/grpc/keepalive/keepalive.go +++ /dev/null @@ -1,65 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package keepalive defines configurable parameters for point-to-point healthcheck. -package keepalive - -import ( - "time" -) - -// ClientParameters is used to set keepalive parameters on the client-side. -// These configure how the client will actively probe to notice when a connection is broken -// and send pings so intermediaries will be aware of the liveness of the connection. -// Make sure these parameters are set in coordination with the keepalive policy on the server, -// as incompatible settings can result in closing of connection. -type ClientParameters struct { - // After a duration of this time if the client doesn't see any activity it pings the server to see if the transport is still alive. - Time time.Duration // The current default value is infinity. - // After having pinged for keepalive check, the client waits for a duration of Timeout and if no activity is seen even after that - // the connection is closed. - Timeout time.Duration // The current default value is 20 seconds. - // If true, client runs keepalive checks even with no active RPCs. - PermitWithoutStream bool // false by default. -} - -// ServerParameters is used to set keepalive and max-age parameters on the server-side. -type ServerParameters struct { - // MaxConnectionIdle is a duration for the amount of time after which an idle connection would be closed by sending a GoAway. - // Idleness duration is defined since the most recent time the number of outstanding RPCs became zero or the connection establishment. - MaxConnectionIdle time.Duration // The current default value is infinity. - // MaxConnectionAge is a duration for the maximum amount of time a connection may exist before it will be closed by sending a GoAway. - // A random jitter of +/-10% will be added to MaxConnectionAge to spread out connection storms. - MaxConnectionAge time.Duration // The current default value is infinity. - // MaxConnectinoAgeGrace is an additive period after MaxConnectionAge after which the connection will be forcibly closed. - MaxConnectionAgeGrace time.Duration // The current default value is infinity. - // After a duration of this time if the server doesn't see any activity it pings the client to see if the transport is still alive. - Time time.Duration // The current default value is 2 hours. - // After having pinged for keepalive check, the server waits for a duration of Timeout and if no activity is seen even after that - // the connection is closed. - Timeout time.Duration // The current default value is 20 seconds. -} - -// EnforcementPolicy is used to set keepalive enforcement policy on the server-side. -// Server will close connection with a client that violates this policy. -type EnforcementPolicy struct { - // MinTime is the minimum amount of time a client should wait before sending a keepalive ping. - MinTime time.Duration // The current default value is 5 minutes. - // If true, server expects keepalive pings even when there are no active streams(RPCs). - PermitWithoutStream bool // false by default. -} diff --git a/vendor/google.golang.org/grpc/metadata/metadata.go b/vendor/google.golang.org/grpc/metadata/metadata.go deleted file mode 100644 index bd2eaf40837..00000000000 --- a/vendor/google.golang.org/grpc/metadata/metadata.go +++ /dev/null @@ -1,210 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package metadata define the structure of the metadata supported by gRPC library. -// Please refer to https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md -// for more information about custom-metadata. -package metadata // import "google.golang.org/grpc/metadata" - -import ( - "fmt" - "strings" - - "golang.org/x/net/context" -) - -// DecodeKeyValue returns k, v, nil. -// -// Deprecated: use k and v directly instead. -func DecodeKeyValue(k, v string) (string, string, error) { - return k, v, nil -} - -// MD is a mapping from metadata keys to values. Users should use the following -// two convenience functions New and Pairs to generate MD. -type MD map[string][]string - -// New creates an MD from a given key-value map. -// -// Only the following ASCII characters are allowed in keys: -// - digits: 0-9 -// - uppercase letters: A-Z (normalized to lower) -// - lowercase letters: a-z -// - special characters: -_. -// Uppercase letters are automatically converted to lowercase. -// -// Keys beginning with "grpc-" are reserved for grpc-internal use only and may -// result in errors if set in metadata. -func New(m map[string]string) MD { - md := MD{} - for k, val := range m { - key := strings.ToLower(k) - md[key] = append(md[key], val) - } - return md -} - -// Pairs returns an MD formed by the mapping of key, value ... -// Pairs panics if len(kv) is odd. -// -// Only the following ASCII characters are allowed in keys: -// - digits: 0-9 -// - uppercase letters: A-Z (normalized to lower) -// - lowercase letters: a-z -// - special characters: -_. -// Uppercase letters are automatically converted to lowercase. -// -// Keys beginning with "grpc-" are reserved for grpc-internal use only and may -// result in errors if set in metadata. -func Pairs(kv ...string) MD { - if len(kv)%2 == 1 { - panic(fmt.Sprintf("metadata: Pairs got the odd number of input pairs for metadata: %d", len(kv))) - } - md := MD{} - var key string - for i, s := range kv { - if i%2 == 0 { - key = strings.ToLower(s) - continue - } - md[key] = append(md[key], s) - } - return md -} - -// Len returns the number of items in md. -func (md MD) Len() int { - return len(md) -} - -// Copy returns a copy of md. -func (md MD) Copy() MD { - return Join(md) -} - -// Get obtains the values for a given key. -func (md MD) Get(k string) []string { - k = strings.ToLower(k) - return md[k] -} - -// Set sets the value of a given key with a slice of values. -func (md MD) Set(k string, vals ...string) { - if len(vals) == 0 { - return - } - k = strings.ToLower(k) - md[k] = vals -} - -// Append adds the values to key k, not overwriting what was already stored at that key. -func (md MD) Append(k string, vals ...string) { - if len(vals) == 0 { - return - } - k = strings.ToLower(k) - md[k] = append(md[k], vals...) -} - -// Join joins any number of mds into a single MD. -// The order of values for each key is determined by the order in which -// the mds containing those values are presented to Join. -func Join(mds ...MD) MD { - out := MD{} - for _, md := range mds { - for k, v := range md { - out[k] = append(out[k], v...) - } - } - return out -} - -type mdIncomingKey struct{} -type mdOutgoingKey struct{} - -// NewIncomingContext creates a new context with incoming md attached. -func NewIncomingContext(ctx context.Context, md MD) context.Context { - return context.WithValue(ctx, mdIncomingKey{}, md) -} - -// NewOutgoingContext creates a new context with outgoing md attached. If used -// in conjunction with AppendToOutgoingContext, NewOutgoingContext will -// overwrite any previously-appended metadata. -func NewOutgoingContext(ctx context.Context, md MD) context.Context { - return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md}) -} - -// AppendToOutgoingContext returns a new context with the provided kv merged -// with any existing metadata in the context. Please refer to the -// documentation of Pairs for a description of kv. -func AppendToOutgoingContext(ctx context.Context, kv ...string) context.Context { - if len(kv)%2 == 1 { - panic(fmt.Sprintf("metadata: AppendToOutgoingContext got an odd number of input pairs for metadata: %d", len(kv))) - } - md, _ := ctx.Value(mdOutgoingKey{}).(rawMD) - added := make([][]string, len(md.added)+1) - copy(added, md.added) - added[len(added)-1] = make([]string, len(kv)) - copy(added[len(added)-1], kv) - return context.WithValue(ctx, mdOutgoingKey{}, rawMD{md: md.md, added: added}) -} - -// FromIncomingContext returns the incoming metadata in ctx if it exists. The -// returned MD should not be modified. Writing to it may cause races. -// Modification should be made to copies of the returned MD. -func FromIncomingContext(ctx context.Context) (md MD, ok bool) { - md, ok = ctx.Value(mdIncomingKey{}).(MD) - return -} - -// FromOutgoingContextRaw returns the un-merged, intermediary contents -// of rawMD. Remember to perform strings.ToLower on the keys. The returned -// MD should not be modified. Writing to it may cause races. Modification -// should be made to copies of the returned MD. -// -// This is intended for gRPC-internal use ONLY. -func FromOutgoingContextRaw(ctx context.Context) (MD, [][]string, bool) { - raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD) - if !ok { - return nil, nil, false - } - - return raw.md, raw.added, true -} - -// FromOutgoingContext returns the outgoing metadata in ctx if it exists. The -// returned MD should not be modified. Writing to it may cause races. -// Modification should be made to copies of the returned MD. -func FromOutgoingContext(ctx context.Context) (MD, bool) { - raw, ok := ctx.Value(mdOutgoingKey{}).(rawMD) - if !ok { - return nil, false - } - - mds := make([]MD, 0, len(raw.added)+1) - mds = append(mds, raw.md) - for _, vv := range raw.added { - mds = append(mds, Pairs(vv...)) - } - return Join(mds...), ok -} - -type rawMD struct { - md MD - added [][]string -} diff --git a/vendor/google.golang.org/grpc/naming/dns_resolver.go b/vendor/google.golang.org/grpc/naming/dns_resolver.go deleted file mode 100644 index 0f8a908ea9c..00000000000 --- a/vendor/google.golang.org/grpc/naming/dns_resolver.go +++ /dev/null @@ -1,290 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package naming - -import ( - "errors" - "fmt" - "net" - "strconv" - "time" - - "golang.org/x/net/context" - "google.golang.org/grpc/grpclog" -) - -const ( - defaultPort = "443" - defaultFreq = time.Minute * 30 -) - -var ( - errMissingAddr = errors.New("missing address") - errWatcherClose = errors.New("watcher has been closed") -) - -// NewDNSResolverWithFreq creates a DNS Resolver that can resolve DNS names, and -// create watchers that poll the DNS server using the frequency set by freq. -func NewDNSResolverWithFreq(freq time.Duration) (Resolver, error) { - return &dnsResolver{freq: freq}, nil -} - -// NewDNSResolver creates a DNS Resolver that can resolve DNS names, and create -// watchers that poll the DNS server using the default frequency defined by defaultFreq. -func NewDNSResolver() (Resolver, error) { - return NewDNSResolverWithFreq(defaultFreq) -} - -// dnsResolver handles name resolution for names following the DNS scheme -type dnsResolver struct { - // frequency of polling the DNS server that the watchers created by this resolver will use. - freq time.Duration -} - -// formatIP returns ok = false if addr is not a valid textual representation of an IP address. -// If addr is an IPv4 address, return the addr and ok = true. -// If addr is an IPv6 address, return the addr enclosed in square brackets and ok = true. -func formatIP(addr string) (addrIP string, ok bool) { - ip := net.ParseIP(addr) - if ip == nil { - return "", false - } - if ip.To4() != nil { - return addr, true - } - return "[" + addr + "]", true -} - -// parseTarget takes the user input target string, returns formatted host and port info. -// If target doesn't specify a port, set the port to be the defaultPort. -// If target is in IPv6 format and host-name is enclosed in sqarue brackets, brackets -// are strippd when setting the host. -// examples: -// target: "www.google.com" returns host: "www.google.com", port: "443" -// target: "ipv4-host:80" returns host: "ipv4-host", port: "80" -// target: "[ipv6-host]" returns host: "ipv6-host", port: "443" -// target: ":80" returns host: "localhost", port: "80" -// target: ":" returns host: "localhost", port: "443" -func parseTarget(target string) (host, port string, err error) { - if target == "" { - return "", "", errMissingAddr - } - - if ip := net.ParseIP(target); ip != nil { - // target is an IPv4 or IPv6(without brackets) address - return target, defaultPort, nil - } - if host, port, err := net.SplitHostPort(target); err == nil { - // target has port, i.e ipv4-host:port, [ipv6-host]:port, host-name:port - if host == "" { - // Keep consistent with net.Dial(): If the host is empty, as in ":80", the local system is assumed. - host = "localhost" - } - if port == "" { - // If the port field is empty(target ends with colon), e.g. "[::1]:", defaultPort is used. - port = defaultPort - } - return host, port, nil - } - if host, port, err := net.SplitHostPort(target + ":" + defaultPort); err == nil { - // target doesn't have port - return host, port, nil - } - return "", "", fmt.Errorf("invalid target address %v", target) -} - -// Resolve creates a watcher that watches the name resolution of the target. -func (r *dnsResolver) Resolve(target string) (Watcher, error) { - host, port, err := parseTarget(target) - if err != nil { - return nil, err - } - - if net.ParseIP(host) != nil { - ipWatcher := &ipWatcher{ - updateChan: make(chan *Update, 1), - } - host, _ = formatIP(host) - ipWatcher.updateChan <- &Update{Op: Add, Addr: host + ":" + port} - return ipWatcher, nil - } - - ctx, cancel := context.WithCancel(context.Background()) - return &dnsWatcher{ - r: r, - host: host, - port: port, - ctx: ctx, - cancel: cancel, - t: time.NewTimer(0), - }, nil -} - -// dnsWatcher watches for the name resolution update for a specific target -type dnsWatcher struct { - r *dnsResolver - host string - port string - // The latest resolved address set - curAddrs map[string]*Update - ctx context.Context - cancel context.CancelFunc - t *time.Timer -} - -// ipWatcher watches for the name resolution update for an IP address. -type ipWatcher struct { - updateChan chan *Update -} - -// Next returns the address resolution Update for the target. For IP address, -// the resolution is itself, thus polling name server is unnecessary. Therefore, -// Next() will return an Update the first time it is called, and will be blocked -// for all following calls as no Update exists until watcher is closed. -func (i *ipWatcher) Next() ([]*Update, error) { - u, ok := <-i.updateChan - if !ok { - return nil, errWatcherClose - } - return []*Update{u}, nil -} - -// Close closes the ipWatcher. -func (i *ipWatcher) Close() { - close(i.updateChan) -} - -// AddressType indicates the address type returned by name resolution. -type AddressType uint8 - -const ( - // Backend indicates the server is a backend server. - Backend AddressType = iota - // GRPCLB indicates the server is a grpclb load balancer. - GRPCLB -) - -// AddrMetadataGRPCLB contains the information the name resolver for grpclb should provide. The -// name resolver used by the grpclb balancer is required to provide this type of metadata in -// its address updates. -type AddrMetadataGRPCLB struct { - // AddrType is the type of server (grpc load balancer or backend). - AddrType AddressType - // ServerName is the name of the grpc load balancer. Used for authentication. - ServerName string -} - -// compileUpdate compares the old resolved addresses and newly resolved addresses, -// and generates an update list -func (w *dnsWatcher) compileUpdate(newAddrs map[string]*Update) []*Update { - var res []*Update - for a, u := range w.curAddrs { - if _, ok := newAddrs[a]; !ok { - u.Op = Delete - res = append(res, u) - } - } - for a, u := range newAddrs { - if _, ok := w.curAddrs[a]; !ok { - res = append(res, u) - } - } - return res -} - -func (w *dnsWatcher) lookupSRV() map[string]*Update { - newAddrs := make(map[string]*Update) - _, srvs, err := lookupSRV(w.ctx, "grpclb", "tcp", w.host) - if err != nil { - grpclog.Infof("grpc: failed dns SRV record lookup due to %v.\n", err) - return nil - } - for _, s := range srvs { - lbAddrs, err := lookupHost(w.ctx, s.Target) - if err != nil { - grpclog.Warningf("grpc: failed load banlacer address dns lookup due to %v.\n", err) - continue - } - for _, a := range lbAddrs { - a, ok := formatIP(a) - if !ok { - grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err) - continue - } - addr := a + ":" + strconv.Itoa(int(s.Port)) - newAddrs[addr] = &Update{Addr: addr, - Metadata: AddrMetadataGRPCLB{AddrType: GRPCLB, ServerName: s.Target}} - } - } - return newAddrs -} - -func (w *dnsWatcher) lookupHost() map[string]*Update { - newAddrs := make(map[string]*Update) - addrs, err := lookupHost(w.ctx, w.host) - if err != nil { - grpclog.Warningf("grpc: failed dns A record lookup due to %v.\n", err) - return nil - } - for _, a := range addrs { - a, ok := formatIP(a) - if !ok { - grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err) - continue - } - addr := a + ":" + w.port - newAddrs[addr] = &Update{Addr: addr} - } - return newAddrs -} - -func (w *dnsWatcher) lookup() []*Update { - newAddrs := w.lookupSRV() - if newAddrs == nil { - // If failed to get any balancer address (either no corresponding SRV for the - // target, or caused by failure during resolution/parsing of the balancer target), - // return any A record info available. - newAddrs = w.lookupHost() - } - result := w.compileUpdate(newAddrs) - w.curAddrs = newAddrs - return result -} - -// Next returns the resolved address update(delta) for the target. If there's no -// change, it will sleep for 30 mins and try to resolve again after that. -func (w *dnsWatcher) Next() ([]*Update, error) { - for { - select { - case <-w.ctx.Done(): - return nil, errWatcherClose - case <-w.t.C: - } - result := w.lookup() - // Next lookup should happen after an interval defined by w.r.freq. - w.t.Reset(w.r.freq) - if len(result) > 0 { - return result, nil - } - } -} - -func (w *dnsWatcher) Close() { - w.cancel() -} diff --git a/vendor/google.golang.org/grpc/naming/go17.go b/vendor/google.golang.org/grpc/naming/go17.go deleted file mode 100644 index 57b65d7b889..00000000000 --- a/vendor/google.golang.org/grpc/naming/go17.go +++ /dev/null @@ -1,34 +0,0 @@ -// +build go1.6,!go1.8 - -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package naming - -import ( - "net" - - "golang.org/x/net/context" -) - -var ( - lookupHost = func(ctx context.Context, host string) ([]string, error) { return net.LookupHost(host) } - lookupSRV = func(ctx context.Context, service, proto, name string) (string, []*net.SRV, error) { - return net.LookupSRV(service, proto, name) - } -) diff --git a/vendor/google.golang.org/grpc/naming/go18.go b/vendor/google.golang.org/grpc/naming/go18.go deleted file mode 100644 index b5a0f842748..00000000000 --- a/vendor/google.golang.org/grpc/naming/go18.go +++ /dev/null @@ -1,28 +0,0 @@ -// +build go1.8 - -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package naming - -import "net" - -var ( - lookupHost = net.DefaultResolver.LookupHost - lookupSRV = net.DefaultResolver.LookupSRV -) diff --git a/vendor/google.golang.org/grpc/naming/naming.go b/vendor/google.golang.org/grpc/naming/naming.go deleted file mode 100644 index 8cc39e93758..00000000000 --- a/vendor/google.golang.org/grpc/naming/naming.go +++ /dev/null @@ -1,69 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package naming defines the naming API and related data structures for gRPC. -// The interface is EXPERIMENTAL and may be suject to change. -// -// Deprecated: please use package resolver. -package naming - -// Operation defines the corresponding operations for a name resolution change. -// -// Deprecated: please use package resolver. -type Operation uint8 - -const ( - // Add indicates a new address is added. - Add Operation = iota - // Delete indicates an existing address is deleted. - Delete -) - -// Update defines a name resolution update. Notice that it is not valid having both -// empty string Addr and nil Metadata in an Update. -// -// Deprecated: please use package resolver. -type Update struct { - // Op indicates the operation of the update. - Op Operation - // Addr is the updated address. It is empty string if there is no address update. - Addr string - // Metadata is the updated metadata. It is nil if there is no metadata update. - // Metadata is not required for a custom naming implementation. - Metadata interface{} -} - -// Resolver creates a Watcher for a target to track its resolution changes. -// -// Deprecated: please use package resolver. -type Resolver interface { - // Resolve creates a Watcher for target. - Resolve(target string) (Watcher, error) -} - -// Watcher watches for the updates on the specified target. -// -// Deprecated: please use package resolver. -type Watcher interface { - // Next blocks until an update or error happens. It may return one or more - // updates. The first call should get the full set of the results. It should - // return an error if and only if Watcher cannot recover. - Next() ([]*Update, error) - // Close closes the Watcher. - Close() -} diff --git a/vendor/google.golang.org/grpc/peer/peer.go b/vendor/google.golang.org/grpc/peer/peer.go deleted file mode 100644 index 317b8b9d09a..00000000000 --- a/vendor/google.golang.org/grpc/peer/peer.go +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package peer defines various peer information associated with RPCs and -// corresponding utils. -package peer - -import ( - "net" - - "golang.org/x/net/context" - "google.golang.org/grpc/credentials" -) - -// Peer contains the information of the peer for an RPC, such as the address -// and authentication information. -type Peer struct { - // Addr is the peer address. - Addr net.Addr - // AuthInfo is the authentication information of the transport. - // It is nil if there is no transport security being used. - AuthInfo credentials.AuthInfo -} - -type peerKey struct{} - -// NewContext creates a new context with peer information attached. -func NewContext(ctx context.Context, p *Peer) context.Context { - return context.WithValue(ctx, peerKey{}, p) -} - -// FromContext returns the peer information in ctx if it exists. -func FromContext(ctx context.Context) (p *Peer, ok bool) { - p, ok = ctx.Value(peerKey{}).(*Peer) - return -} diff --git a/vendor/google.golang.org/grpc/picker_wrapper.go b/vendor/google.golang.org/grpc/picker_wrapper.go deleted file mode 100644 index 0a984e6c8af..00000000000 --- a/vendor/google.golang.org/grpc/picker_wrapper.go +++ /dev/null @@ -1,331 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "io" - "sync" - "sync/atomic" - - "golang.org/x/net/context" - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/channelz" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/resolver" - "google.golang.org/grpc/status" - "google.golang.org/grpc/transport" -) - -// pickerWrapper is a wrapper of balancer.Picker. It blocks on certain pick -// actions and unblock when there's a picker update. -type pickerWrapper struct { - mu sync.Mutex - done bool - blockingCh chan struct{} - picker balancer.Picker - - // The latest connection happened. - connErrMu sync.Mutex - connErr error - - stickinessMDKey atomic.Value - stickiness *stickyStore -} - -func newPickerWrapper() *pickerWrapper { - bp := &pickerWrapper{ - blockingCh: make(chan struct{}), - stickiness: newStickyStore(), - } - return bp -} - -func (bp *pickerWrapper) updateConnectionError(err error) { - bp.connErrMu.Lock() - bp.connErr = err - bp.connErrMu.Unlock() -} - -func (bp *pickerWrapper) connectionError() error { - bp.connErrMu.Lock() - err := bp.connErr - bp.connErrMu.Unlock() - return err -} - -func (bp *pickerWrapper) updateStickinessMDKey(newKey string) { - // No need to check ok because mdKey == "" if ok == false. - if oldKey, _ := bp.stickinessMDKey.Load().(string); oldKey != newKey { - bp.stickinessMDKey.Store(newKey) - bp.stickiness.reset(newKey) - } -} - -func (bp *pickerWrapper) getStickinessMDKey() string { - // No need to check ok because mdKey == "" if ok == false. - mdKey, _ := bp.stickinessMDKey.Load().(string) - return mdKey -} - -func (bp *pickerWrapper) clearStickinessState() { - if oldKey := bp.getStickinessMDKey(); oldKey != "" { - // There's no need to reset store if mdKey was "". - bp.stickiness.reset(oldKey) - } -} - -// updatePicker is called by UpdateBalancerState. It unblocks all blocked pick. -func (bp *pickerWrapper) updatePicker(p balancer.Picker) { - bp.mu.Lock() - if bp.done { - bp.mu.Unlock() - return - } - bp.picker = p - // bp.blockingCh should never be nil. - close(bp.blockingCh) - bp.blockingCh = make(chan struct{}) - bp.mu.Unlock() -} - -func doneChannelzWrapper(acw *acBalancerWrapper, done func(balancer.DoneInfo)) func(balancer.DoneInfo) { - acw.mu.Lock() - ac := acw.ac - acw.mu.Unlock() - ac.incrCallsStarted() - return func(b balancer.DoneInfo) { - if b.Err != nil && b.Err != io.EOF { - ac.incrCallsFailed() - } else { - ac.incrCallsSucceeded() - } - if done != nil { - done(b) - } - } -} - -// pick returns the transport that will be used for the RPC. -// It may block in the following cases: -// - there's no picker -// - the current picker returns ErrNoSubConnAvailable -// - the current picker returns other errors and failfast is false. -// - the subConn returned by the current picker is not READY -// When one of these situations happens, pick blocks until the picker gets updated. -func (bp *pickerWrapper) pick(ctx context.Context, failfast bool, opts balancer.PickOptions) (transport.ClientTransport, func(balancer.DoneInfo), error) { - - mdKey := bp.getStickinessMDKey() - stickyKey, isSticky := stickyKeyFromContext(ctx, mdKey) - - // Potential race here: if stickinessMDKey is updated after the above two - // lines, and this pick is a sticky pick, the following put could add an - // entry to sticky store with an outdated sticky key. - // - // The solution: keep the current md key in sticky store, and at the - // beginning of each get/put, check the mdkey against store.curMDKey. - // - Cons: one more string comparing for each get/put. - // - Pros: the string matching happens inside get/put, so the overhead for - // non-sticky RPCs will be minimal. - - if isSticky { - if t, ok := bp.stickiness.get(mdKey, stickyKey); ok { - // Done function returned is always nil. - return t, nil, nil - } - } - - var ( - p balancer.Picker - ch chan struct{} - ) - - for { - bp.mu.Lock() - if bp.done { - bp.mu.Unlock() - return nil, nil, ErrClientConnClosing - } - - if bp.picker == nil { - ch = bp.blockingCh - } - if ch == bp.blockingCh { - // This could happen when either: - // - bp.picker is nil (the previous if condition), or - // - has called pick on the current picker. - bp.mu.Unlock() - select { - case <-ctx.Done(): - return nil, nil, ctx.Err() - case <-ch: - } - continue - } - - ch = bp.blockingCh - p = bp.picker - bp.mu.Unlock() - - subConn, done, err := p.Pick(ctx, opts) - - if err != nil { - switch err { - case balancer.ErrNoSubConnAvailable: - continue - case balancer.ErrTransientFailure: - if !failfast { - continue - } - return nil, nil, status.Errorf(codes.Unavailable, "%v, latest connection error: %v", err, bp.connectionError()) - default: - // err is some other error. - return nil, nil, toRPCErr(err) - } - } - - acw, ok := subConn.(*acBalancerWrapper) - if !ok { - grpclog.Infof("subconn returned from pick is not *acBalancerWrapper") - continue - } - if t, ok := acw.getAddrConn().getReadyTransport(); ok { - if isSticky { - bp.stickiness.put(mdKey, stickyKey, acw) - } - if channelz.IsOn() { - return t, doneChannelzWrapper(acw, done), nil - } - return t, done, nil - } - grpclog.Infof("blockingPicker: the picked transport is not ready, loop back to repick") - // If ok == false, ac.state is not READY. - // A valid picker always returns READY subConn. This means the state of ac - // just changed, and picker will be updated shortly. - // continue back to the beginning of the for loop to repick. - } -} - -func (bp *pickerWrapper) close() { - bp.mu.Lock() - defer bp.mu.Unlock() - if bp.done { - return - } - bp.done = true - close(bp.blockingCh) -} - -type stickyStoreEntry struct { - acw *acBalancerWrapper - addr resolver.Address -} - -type stickyStore struct { - mu sync.Mutex - // curMDKey is check before every get/put to avoid races. The operation will - // abort immediately when the given mdKey is different from the curMDKey. - curMDKey string - store map[string]*stickyStoreEntry -} - -func newStickyStore() *stickyStore { - return &stickyStore{ - store: make(map[string]*stickyStoreEntry), - } -} - -// reset clears the map in stickyStore, and set the currentMDKey to newMDKey. -func (ss *stickyStore) reset(newMDKey string) { - ss.mu.Lock() - ss.curMDKey = newMDKey - ss.store = make(map[string]*stickyStoreEntry) - ss.mu.Unlock() -} - -// stickyKey is the key to look up in store. mdKey will be checked against -// curMDKey to avoid races. -func (ss *stickyStore) put(mdKey, stickyKey string, acw *acBalancerWrapper) { - ss.mu.Lock() - defer ss.mu.Unlock() - if mdKey != ss.curMDKey { - return - } - // TODO(stickiness): limit the total number of entries. - ss.store[stickyKey] = &stickyStoreEntry{ - acw: acw, - addr: acw.getAddrConn().getCurAddr(), - } -} - -// stickyKey is the key to look up in store. mdKey will be checked against -// curMDKey to avoid races. -func (ss *stickyStore) get(mdKey, stickyKey string) (transport.ClientTransport, bool) { - ss.mu.Lock() - defer ss.mu.Unlock() - if mdKey != ss.curMDKey { - return nil, false - } - entry, ok := ss.store[stickyKey] - if !ok { - return nil, false - } - ac := entry.acw.getAddrConn() - if ac.getCurAddr() != entry.addr { - delete(ss.store, stickyKey) - return nil, false - } - t, ok := ac.getReadyTransport() - if !ok { - delete(ss.store, stickyKey) - return nil, false - } - return t, true -} - -// Get one value from metadata in ctx with key stickinessMDKey. -// -// It returns "", false if stickinessMDKey is an empty string. -func stickyKeyFromContext(ctx context.Context, stickinessMDKey string) (string, bool) { - if stickinessMDKey == "" { - return "", false - } - - md, added, ok := metadata.FromOutgoingContextRaw(ctx) - if !ok { - return "", false - } - - if vv, ok := md[stickinessMDKey]; ok { - if len(vv) > 0 { - return vv[0], true - } - } - - for _, ss := range added { - for i := 0; i < len(ss)-1; i += 2 { - if ss[i] == stickinessMDKey { - return ss[i+1], true - } - } - } - - return "", false -} diff --git a/vendor/google.golang.org/grpc/pickfirst.go b/vendor/google.golang.org/grpc/pickfirst.go deleted file mode 100644 index bf659d49d2f..00000000000 --- a/vendor/google.golang.org/grpc/pickfirst.go +++ /dev/null @@ -1,108 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "golang.org/x/net/context" - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/connectivity" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/resolver" -) - -// PickFirstBalancerName is the name of the pick_first balancer. -const PickFirstBalancerName = "pick_first" - -func newPickfirstBuilder() balancer.Builder { - return &pickfirstBuilder{} -} - -type pickfirstBuilder struct{} - -func (*pickfirstBuilder) Build(cc balancer.ClientConn, opt balancer.BuildOptions) balancer.Balancer { - return &pickfirstBalancer{cc: cc} -} - -func (*pickfirstBuilder) Name() string { - return PickFirstBalancerName -} - -type pickfirstBalancer struct { - cc balancer.ClientConn - sc balancer.SubConn -} - -func (b *pickfirstBalancer) HandleResolvedAddrs(addrs []resolver.Address, err error) { - if err != nil { - grpclog.Infof("pickfirstBalancer: HandleResolvedAddrs called with error %v", err) - return - } - if b.sc == nil { - b.sc, err = b.cc.NewSubConn(addrs, balancer.NewSubConnOptions{}) - if err != nil { - grpclog.Errorf("pickfirstBalancer: failed to NewSubConn: %v", err) - return - } - b.cc.UpdateBalancerState(connectivity.Idle, &picker{sc: b.sc}) - b.sc.Connect() - } else { - b.sc.UpdateAddresses(addrs) - b.sc.Connect() - } -} - -func (b *pickfirstBalancer) HandleSubConnStateChange(sc balancer.SubConn, s connectivity.State) { - grpclog.Infof("pickfirstBalancer: HandleSubConnStateChange: %p, %v", sc, s) - if b.sc != sc { - grpclog.Infof("pickfirstBalancer: ignored state change because sc is not recognized") - return - } - if s == connectivity.Shutdown { - b.sc = nil - return - } - - switch s { - case connectivity.Ready, connectivity.Idle: - b.cc.UpdateBalancerState(s, &picker{sc: sc}) - case connectivity.Connecting: - b.cc.UpdateBalancerState(s, &picker{err: balancer.ErrNoSubConnAvailable}) - case connectivity.TransientFailure: - b.cc.UpdateBalancerState(s, &picker{err: balancer.ErrTransientFailure}) - } -} - -func (b *pickfirstBalancer) Close() { -} - -type picker struct { - err error - sc balancer.SubConn -} - -func (p *picker) Pick(ctx context.Context, opts balancer.PickOptions) (balancer.SubConn, func(balancer.DoneInfo), error) { - if p.err != nil { - return nil, nil, p.err - } - return p.sc, nil, nil -} - -func init() { - balancer.Register(newPickfirstBuilder()) -} diff --git a/vendor/google.golang.org/grpc/proxy.go b/vendor/google.golang.org/grpc/proxy.go deleted file mode 100644 index 2d40236e218..00000000000 --- a/vendor/google.golang.org/grpc/proxy.go +++ /dev/null @@ -1,130 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "bufio" - "errors" - "fmt" - "io" - "net" - "net/http" - "net/http/httputil" - "net/url" - - "golang.org/x/net/context" -) - -var ( - // errDisabled indicates that proxy is disabled for the address. - errDisabled = errors.New("proxy is disabled for the address") - // The following variable will be overwritten in the tests. - httpProxyFromEnvironment = http.ProxyFromEnvironment -) - -func mapAddress(ctx context.Context, address string) (string, error) { - req := &http.Request{ - URL: &url.URL{ - Scheme: "https", - Host: address, - }, - } - url, err := httpProxyFromEnvironment(req) - if err != nil { - return "", err - } - if url == nil { - return "", errDisabled - } - return url.Host, nil -} - -// To read a response from a net.Conn, http.ReadResponse() takes a bufio.Reader. -// It's possible that this reader reads more than what's need for the response and stores -// those bytes in the buffer. -// bufConn wraps the original net.Conn and the bufio.Reader to make sure we don't lose the -// bytes in the buffer. -type bufConn struct { - net.Conn - r io.Reader -} - -func (c *bufConn) Read(b []byte) (int, error) { - return c.r.Read(b) -} - -func doHTTPConnectHandshake(ctx context.Context, conn net.Conn, addr string) (_ net.Conn, err error) { - defer func() { - if err != nil { - conn.Close() - } - }() - - req := (&http.Request{ - Method: http.MethodConnect, - URL: &url.URL{Host: addr}, - Header: map[string][]string{"User-Agent": {grpcUA}}, - }) - - if err := sendHTTPRequest(ctx, req, conn); err != nil { - return nil, fmt.Errorf("failed to write the HTTP request: %v", err) - } - - r := bufio.NewReader(conn) - resp, err := http.ReadResponse(r, req) - if err != nil { - return nil, fmt.Errorf("reading server HTTP response: %v", err) - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - dump, err := httputil.DumpResponse(resp, true) - if err != nil { - return nil, fmt.Errorf("failed to do connect handshake, status code: %s", resp.Status) - } - return nil, fmt.Errorf("failed to do connect handshake, response: %q", dump) - } - - return &bufConn{Conn: conn, r: r}, nil -} - -// newProxyDialer returns a dialer that connects to proxy first if necessary. -// The returned dialer checks if a proxy is necessary, dial to the proxy with the -// provided dialer, does HTTP CONNECT handshake and returns the connection. -func newProxyDialer(dialer func(context.Context, string) (net.Conn, error)) func(context.Context, string) (net.Conn, error) { - return func(ctx context.Context, addr string) (conn net.Conn, err error) { - var skipHandshake bool - newAddr, err := mapAddress(ctx, addr) - if err != nil { - if err != errDisabled { - return nil, err - } - skipHandshake = true - newAddr = addr - } - - conn, err = dialer(ctx, newAddr) - if err != nil { - return - } - if !skipHandshake { - conn, err = doHTTPConnectHandshake(ctx, conn, addr) - } - return - } -} diff --git a/vendor/google.golang.org/grpc/resolver/dns/dns_resolver.go b/vendor/google.golang.org/grpc/resolver/dns/dns_resolver.go deleted file mode 100644 index c1cabfc995f..00000000000 --- a/vendor/google.golang.org/grpc/resolver/dns/dns_resolver.go +++ /dev/null @@ -1,379 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package dns implements a dns resolver to be installed as the default resolver -// in grpc. -package dns - -import ( - "encoding/json" - "errors" - "fmt" - "math/rand" - "net" - "os" - "strconv" - "strings" - "sync" - "time" - - "golang.org/x/net/context" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/resolver" -) - -func init() { - resolver.Register(NewBuilder()) -} - -const ( - defaultPort = "443" - defaultFreq = time.Minute * 30 - golang = "GO" - // In DNS, service config is encoded in a TXT record via the mechanism - // described in RFC-1464 using the attribute name grpc_config. - txtAttribute = "grpc_config=" -) - -var ( - errMissingAddr = errors.New("missing address") - randomGen = rand.New(rand.NewSource(time.Now().UnixNano())) -) - -// NewBuilder creates a dnsBuilder which is used to factory DNS resolvers. -func NewBuilder() resolver.Builder { - return &dnsBuilder{freq: defaultFreq} -} - -type dnsBuilder struct { - // frequency of polling the DNS server. - freq time.Duration -} - -// Build creates and starts a DNS resolver that watches the name resolution of the target. -func (b *dnsBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) { - host, port, err := parseTarget(target.Endpoint) - if err != nil { - return nil, err - } - - // IP address. - if net.ParseIP(host) != nil { - host, _ = formatIP(host) - addr := []resolver.Address{{Addr: host + ":" + port}} - i := &ipResolver{ - cc: cc, - ip: addr, - rn: make(chan struct{}, 1), - q: make(chan struct{}), - } - cc.NewAddress(addr) - go i.watcher() - return i, nil - } - - // DNS address (non-IP). - ctx, cancel := context.WithCancel(context.Background()) - d := &dnsResolver{ - freq: b.freq, - host: host, - port: port, - ctx: ctx, - cancel: cancel, - cc: cc, - t: time.NewTimer(0), - rn: make(chan struct{}, 1), - disableServiceConfig: opts.DisableServiceConfig, - } - - d.wg.Add(1) - go d.watcher() - return d, nil -} - -// Scheme returns the naming scheme of this resolver builder, which is "dns". -func (b *dnsBuilder) Scheme() string { - return "dns" -} - -// ipResolver watches for the name resolution update for an IP address. -type ipResolver struct { - cc resolver.ClientConn - ip []resolver.Address - // rn channel is used by ResolveNow() to force an immediate resolution of the target. - rn chan struct{} - q chan struct{} -} - -// ResolveNow resend the address it stores, no resolution is needed. -func (i *ipResolver) ResolveNow(opt resolver.ResolveNowOption) { - select { - case i.rn <- struct{}{}: - default: - } -} - -// Close closes the ipResolver. -func (i *ipResolver) Close() { - close(i.q) -} - -func (i *ipResolver) watcher() { - for { - select { - case <-i.rn: - i.cc.NewAddress(i.ip) - case <-i.q: - return - } - } -} - -// dnsResolver watches for the name resolution update for a non-IP target. -type dnsResolver struct { - freq time.Duration - host string - port string - ctx context.Context - cancel context.CancelFunc - cc resolver.ClientConn - // rn channel is used by ResolveNow() to force an immediate resolution of the target. - rn chan struct{} - t *time.Timer - // wg is used to enforce Close() to return after the watcher() goroutine has finished. - // Otherwise, data race will be possible. [Race Example] in dns_resolver_test we - // replace the real lookup functions with mocked ones to facilitate testing. - // If Close() doesn't wait for watcher() goroutine finishes, race detector sometimes - // will warns lookup (READ the lookup function pointers) inside watcher() goroutine - // has data race with replaceNetFunc (WRITE the lookup function pointers). - wg sync.WaitGroup - disableServiceConfig bool -} - -// ResolveNow invoke an immediate resolution of the target that this dnsResolver watches. -func (d *dnsResolver) ResolveNow(opt resolver.ResolveNowOption) { - select { - case d.rn <- struct{}{}: - default: - } -} - -// Close closes the dnsResolver. -func (d *dnsResolver) Close() { - d.cancel() - d.wg.Wait() - d.t.Stop() -} - -func (d *dnsResolver) watcher() { - defer d.wg.Done() - for { - select { - case <-d.ctx.Done(): - return - case <-d.t.C: - case <-d.rn: - } - result, sc := d.lookup() - // Next lookup should happen after an interval defined by d.freq. - d.t.Reset(d.freq) - d.cc.NewServiceConfig(sc) - d.cc.NewAddress(result) - } -} - -func (d *dnsResolver) lookupSRV() []resolver.Address { - var newAddrs []resolver.Address - _, srvs, err := lookupSRV(d.ctx, "grpclb", "tcp", d.host) - if err != nil { - grpclog.Infof("grpc: failed dns SRV record lookup due to %v.\n", err) - return nil - } - for _, s := range srvs { - lbAddrs, err := lookupHost(d.ctx, s.Target) - if err != nil { - grpclog.Infof("grpc: failed load balancer address dns lookup due to %v.\n", err) - continue - } - for _, a := range lbAddrs { - a, ok := formatIP(a) - if !ok { - grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err) - continue - } - addr := a + ":" + strconv.Itoa(int(s.Port)) - newAddrs = append(newAddrs, resolver.Address{Addr: addr, Type: resolver.GRPCLB, ServerName: s.Target}) - } - } - return newAddrs -} - -func (d *dnsResolver) lookupTXT() string { - ss, err := lookupTXT(d.ctx, d.host) - if err != nil { - grpclog.Infof("grpc: failed dns TXT record lookup due to %v.\n", err) - return "" - } - var res string - for _, s := range ss { - res += s - } - - // TXT record must have "grpc_config=" attribute in order to be used as service config. - if !strings.HasPrefix(res, txtAttribute) { - grpclog.Warningf("grpc: TXT record %v missing %v attribute", res, txtAttribute) - return "" - } - return strings.TrimPrefix(res, txtAttribute) -} - -func (d *dnsResolver) lookupHost() []resolver.Address { - var newAddrs []resolver.Address - addrs, err := lookupHost(d.ctx, d.host) - if err != nil { - grpclog.Warningf("grpc: failed dns A record lookup due to %v.\n", err) - return nil - } - for _, a := range addrs { - a, ok := formatIP(a) - if !ok { - grpclog.Errorf("grpc: failed IP parsing due to %v.\n", err) - continue - } - addr := a + ":" + d.port - newAddrs = append(newAddrs, resolver.Address{Addr: addr}) - } - return newAddrs -} - -func (d *dnsResolver) lookup() ([]resolver.Address, string) { - newAddrs := d.lookupSRV() - // Support fallback to non-balancer address. - newAddrs = append(newAddrs, d.lookupHost()...) - if d.disableServiceConfig { - return newAddrs, "" - } - sc := d.lookupTXT() - return newAddrs, canaryingSC(sc) -} - -// formatIP returns ok = false if addr is not a valid textual representation of an IP address. -// If addr is an IPv4 address, return the addr and ok = true. -// If addr is an IPv6 address, return the addr enclosed in square brackets and ok = true. -func formatIP(addr string) (addrIP string, ok bool) { - ip := net.ParseIP(addr) - if ip == nil { - return "", false - } - if ip.To4() != nil { - return addr, true - } - return "[" + addr + "]", true -} - -// parseTarget takes the user input target string, returns formatted host and port info. -// If target doesn't specify a port, set the port to be the defaultPort. -// If target is in IPv6 format and host-name is enclosed in sqarue brackets, brackets -// are strippd when setting the host. -// examples: -// target: "www.google.com" returns host: "www.google.com", port: "443" -// target: "ipv4-host:80" returns host: "ipv4-host", port: "80" -// target: "[ipv6-host]" returns host: "ipv6-host", port: "443" -// target: ":80" returns host: "localhost", port: "80" -// target: ":" returns host: "localhost", port: "443" -func parseTarget(target string) (host, port string, err error) { - if target == "" { - return "", "", errMissingAddr - } - if ip := net.ParseIP(target); ip != nil { - // target is an IPv4 or IPv6(without brackets) address - return target, defaultPort, nil - } - if host, port, err = net.SplitHostPort(target); err == nil { - // target has port, i.e ipv4-host:port, [ipv6-host]:port, host-name:port - if host == "" { - // Keep consistent with net.Dial(): If the host is empty, as in ":80", the local system is assumed. - host = "localhost" - } - if port == "" { - // If the port field is empty(target ends with colon), e.g. "[::1]:", defaultPort is used. - port = defaultPort - } - return host, port, nil - } - if host, port, err = net.SplitHostPort(target + ":" + defaultPort); err == nil { - // target doesn't have port - return host, port, nil - } - return "", "", fmt.Errorf("invalid target address %v, error info: %v", target, err) -} - -type rawChoice struct { - ClientLanguage *[]string `json:"clientLanguage,omitempty"` - Percentage *int `json:"percentage,omitempty"` - ClientHostName *[]string `json:"clientHostName,omitempty"` - ServiceConfig *json.RawMessage `json:"serviceConfig,omitempty"` -} - -func containsString(a *[]string, b string) bool { - if a == nil { - return true - } - for _, c := range *a { - if c == b { - return true - } - } - return false -} - -func chosenByPercentage(a *int) bool { - if a == nil { - return true - } - return randomGen.Intn(100)+1 <= *a -} - -func canaryingSC(js string) string { - if js == "" { - return "" - } - var rcs []rawChoice - err := json.Unmarshal([]byte(js), &rcs) - if err != nil { - grpclog.Warningf("grpc: failed to parse service config json string due to %v.\n", err) - return "" - } - cliHostname, err := os.Hostname() - if err != nil { - grpclog.Warningf("grpc: failed to get client hostname due to %v.\n", err) - return "" - } - var sc string - for _, c := range rcs { - if !containsString(c.ClientLanguage, golang) || - !chosenByPercentage(c.Percentage) || - !containsString(c.ClientHostName, cliHostname) || - c.ServiceConfig == nil { - continue - } - sc = string(*c.ServiceConfig) - break - } - return sc -} diff --git a/vendor/google.golang.org/grpc/resolver/dns/go17.go b/vendor/google.golang.org/grpc/resolver/dns/go17.go deleted file mode 100644 index b466bc8f6d4..00000000000 --- a/vendor/google.golang.org/grpc/resolver/dns/go17.go +++ /dev/null @@ -1,35 +0,0 @@ -// +build go1.6, !go1.8 - -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package dns - -import ( - "net" - - "golang.org/x/net/context" -) - -var ( - lookupHost = func(ctx context.Context, host string) ([]string, error) { return net.LookupHost(host) } - lookupSRV = func(ctx context.Context, service, proto, name string) (string, []*net.SRV, error) { - return net.LookupSRV(service, proto, name) - } - lookupTXT = func(ctx context.Context, name string) ([]string, error) { return net.LookupTXT(name) } -) diff --git a/vendor/google.golang.org/grpc/resolver/dns/go18.go b/vendor/google.golang.org/grpc/resolver/dns/go18.go deleted file mode 100644 index fa34f14cad4..00000000000 --- a/vendor/google.golang.org/grpc/resolver/dns/go18.go +++ /dev/null @@ -1,29 +0,0 @@ -// +build go1.8 - -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package dns - -import "net" - -var ( - lookupHost = net.DefaultResolver.LookupHost - lookupSRV = net.DefaultResolver.LookupSRV - lookupTXT = net.DefaultResolver.LookupTXT -) diff --git a/vendor/google.golang.org/grpc/resolver/passthrough/passthrough.go b/vendor/google.golang.org/grpc/resolver/passthrough/passthrough.go deleted file mode 100644 index b76010d74d1..00000000000 --- a/vendor/google.golang.org/grpc/resolver/passthrough/passthrough.go +++ /dev/null @@ -1,57 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package passthrough implements a pass-through resolver. It sends the target -// name without scheme back to gRPC as resolved address. -package passthrough - -import "google.golang.org/grpc/resolver" - -const scheme = "passthrough" - -type passthroughBuilder struct{} - -func (*passthroughBuilder) Build(target resolver.Target, cc resolver.ClientConn, opts resolver.BuildOption) (resolver.Resolver, error) { - r := &passthroughResolver{ - target: target, - cc: cc, - } - r.start() - return r, nil -} - -func (*passthroughBuilder) Scheme() string { - return scheme -} - -type passthroughResolver struct { - target resolver.Target - cc resolver.ClientConn -} - -func (r *passthroughResolver) start() { - r.cc.NewAddress([]resolver.Address{{Addr: r.target.Endpoint}}) -} - -func (*passthroughResolver) ResolveNow(o resolver.ResolveNowOption) {} - -func (*passthroughResolver) Close() {} - -func init() { - resolver.Register(&passthroughBuilder{}) -} diff --git a/vendor/google.golang.org/grpc/resolver/resolver.go b/vendor/google.golang.org/grpc/resolver/resolver.go deleted file mode 100644 index 506afac88ae..00000000000 --- a/vendor/google.golang.org/grpc/resolver/resolver.go +++ /dev/null @@ -1,154 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package resolver defines APIs for name resolution in gRPC. -// All APIs in this package are experimental. -package resolver - -var ( - // m is a map from scheme to resolver builder. - m = make(map[string]Builder) - // defaultScheme is the default scheme to use. - defaultScheme = "passthrough" -) - -// TODO(bar) install dns resolver in init(){}. - -// Register registers the resolver builder to the resolver map. b.Scheme will be -// used as the scheme registered with this builder. -// -// NOTE: this function must only be called during initialization time (i.e. in -// an init() function), and is not thread-safe. If multiple Resolvers are -// registered with the same name, the one registered last will take effect. -func Register(b Builder) { - m[b.Scheme()] = b -} - -// Get returns the resolver builder registered with the given scheme. -// -// If no builder is register with the scheme, nil will be returned. -func Get(scheme string) Builder { - if b, ok := m[scheme]; ok { - return b - } - return nil -} - -// SetDefaultScheme sets the default scheme that will be used. -// The default default scheme is "passthrough". -func SetDefaultScheme(scheme string) { - defaultScheme = scheme -} - -// GetDefaultScheme gets the default scheme that will be used. -func GetDefaultScheme() string { - return defaultScheme -} - -// AddressType indicates the address type returned by name resolution. -type AddressType uint8 - -const ( - // Backend indicates the address is for a backend server. - Backend AddressType = iota - // GRPCLB indicates the address is for a grpclb load balancer. - GRPCLB -) - -// Address represents a server the client connects to. -// This is the EXPERIMENTAL API and may be changed or extended in the future. -type Address struct { - // Addr is the server address on which a connection will be established. - Addr string - // Type is the type of this address. - Type AddressType - // ServerName is the name of this address. - // - // e.g. if Type is GRPCLB, ServerName should be the name of the remote load - // balancer, not the name of the backend. - ServerName string - // Metadata is the information associated with Addr, which may be used - // to make load balancing decision. - Metadata interface{} -} - -// BuildOption includes additional information for the builder to create -// the resolver. -type BuildOption struct { - // DisableServiceConfig indicates whether resolver should fetch service config data. - DisableServiceConfig bool -} - -// ClientConn contains the callbacks for resolver to notify any updates -// to the gRPC ClientConn. -// -// This interface is to be implemented by gRPC. Users should not need a -// brand new implementation of this interface. For the situations like -// testing, the new implementation should embed this interface. This allows -// gRPC to add new methods to this interface. -type ClientConn interface { - // NewAddress is called by resolver to notify ClientConn a new list - // of resolved addresses. - // The address list should be the complete list of resolved addresses. - NewAddress(addresses []Address) - // NewServiceConfig is called by resolver to notify ClientConn a new - // service config. The service config should be provided as a json string. - NewServiceConfig(serviceConfig string) -} - -// Target represents a target for gRPC, as specified in: -// https://github.com/grpc/grpc/blob/master/doc/naming.md. -type Target struct { - Scheme string - Authority string - Endpoint string -} - -// Builder creates a resolver that will be used to watch name resolution updates. -type Builder interface { - // Build creates a new resolver for the given target. - // - // gRPC dial calls Build synchronously, and fails if the returned error is - // not nil. - Build(target Target, cc ClientConn, opts BuildOption) (Resolver, error) - // Scheme returns the scheme supported by this resolver. - // Scheme is defined at https://github.com/grpc/grpc/blob/master/doc/naming.md. - Scheme() string -} - -// ResolveNowOption includes additional information for ResolveNow. -type ResolveNowOption struct{} - -// Resolver watches for the updates on the specified target. -// Updates include address updates and service config updates. -type Resolver interface { - // ResolveNow will be called by gRPC to try to resolve the target name - // again. It's just a hint, resolver can ignore this if it's not necessary. - // - // It could be called multiple times concurrently. - ResolveNow(ResolveNowOption) - // Close closes the resolver. - Close() -} - -// UnregisterForTesting removes the resolver builder with the given scheme from the -// resolver map. -// This function is for testing only. -func UnregisterForTesting(scheme string) { - delete(m, scheme) -} diff --git a/vendor/google.golang.org/grpc/resolver_conn_wrapper.go b/vendor/google.golang.org/grpc/resolver_conn_wrapper.go deleted file mode 100644 index 1b493db2e6c..00000000000 --- a/vendor/google.golang.org/grpc/resolver_conn_wrapper.go +++ /dev/null @@ -1,158 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "fmt" - "strings" - - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/resolver" -) - -// ccResolverWrapper is a wrapper on top of cc for resolvers. -// It implements resolver.ClientConnection interface. -type ccResolverWrapper struct { - cc *ClientConn - resolver resolver.Resolver - addrCh chan []resolver.Address - scCh chan string - done chan struct{} -} - -// split2 returns the values from strings.SplitN(s, sep, 2). -// If sep is not found, it returns ("", s, false) instead. -func split2(s, sep string) (string, string, bool) { - spl := strings.SplitN(s, sep, 2) - if len(spl) < 2 { - return "", "", false - } - return spl[0], spl[1], true -} - -// parseTarget splits target into a struct containing scheme, authority and -// endpoint. -// -// If target is not a valid scheme://authority/endpoint, it returns {Endpoint: -// target}. -func parseTarget(target string) (ret resolver.Target) { - var ok bool - ret.Scheme, ret.Endpoint, ok = split2(target, "://") - if !ok { - return resolver.Target{Endpoint: target} - } - ret.Authority, ret.Endpoint, ok = split2(ret.Endpoint, "/") - if !ok { - return resolver.Target{Endpoint: target} - } - return ret -} - -// newCCResolverWrapper parses cc.target for scheme and gets the resolver -// builder for this scheme. It then builds the resolver and starts the -// monitoring goroutine for it. -// -// If withResolverBuilder dial option is set, the specified resolver will be -// used instead. -func newCCResolverWrapper(cc *ClientConn) (*ccResolverWrapper, error) { - rb := cc.dopts.resolverBuilder - if rb == nil { - return nil, fmt.Errorf("could not get resolver for scheme: %q", cc.parsedTarget.Scheme) - } - - ccr := &ccResolverWrapper{ - cc: cc, - addrCh: make(chan []resolver.Address, 1), - scCh: make(chan string, 1), - done: make(chan struct{}), - } - - var err error - ccr.resolver, err = rb.Build(cc.parsedTarget, ccr, resolver.BuildOption{DisableServiceConfig: cc.dopts.disableServiceConfig}) - if err != nil { - return nil, err - } - return ccr, nil -} - -func (ccr *ccResolverWrapper) start() { - go ccr.watcher() -} - -// watcher processes address updates and service config updates sequentially. -// Otherwise, we need to resolve possible races between address and service -// config (e.g. they specify different balancer types). -func (ccr *ccResolverWrapper) watcher() { - for { - select { - case <-ccr.done: - return - default: - } - - select { - case addrs := <-ccr.addrCh: - select { - case <-ccr.done: - return - default: - } - grpclog.Infof("ccResolverWrapper: sending new addresses to cc: %v", addrs) - ccr.cc.handleResolvedAddrs(addrs, nil) - case sc := <-ccr.scCh: - select { - case <-ccr.done: - return - default: - } - grpclog.Infof("ccResolverWrapper: got new service config: %v", sc) - ccr.cc.handleServiceConfig(sc) - case <-ccr.done: - return - } - } -} - -func (ccr *ccResolverWrapper) resolveNow(o resolver.ResolveNowOption) { - ccr.resolver.ResolveNow(o) -} - -func (ccr *ccResolverWrapper) close() { - ccr.resolver.Close() - close(ccr.done) -} - -// NewAddress is called by the resolver implemenetion to send addresses to gRPC. -func (ccr *ccResolverWrapper) NewAddress(addrs []resolver.Address) { - select { - case <-ccr.addrCh: - default: - } - ccr.addrCh <- addrs -} - -// NewServiceConfig is called by the resolver implemenetion to send service -// configs to gPRC. -func (ccr *ccResolverWrapper) NewServiceConfig(sc string) { - select { - case <-ccr.scCh: - default: - } - ccr.scCh <- sc -} diff --git a/vendor/google.golang.org/grpc/rpc_util.go b/vendor/google.golang.org/grpc/rpc_util.go deleted file mode 100644 index 69ef3c0b595..00000000000 --- a/vendor/google.golang.org/grpc/rpc_util.go +++ /dev/null @@ -1,727 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "bytes" - "compress/gzip" - "encoding/binary" - "fmt" - "io" - "io/ioutil" - "math" - "net/url" - "strings" - "sync" - "time" - - "golang.org/x/net/context" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/encoding" - "google.golang.org/grpc/encoding/proto" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/peer" - "google.golang.org/grpc/stats" - "google.golang.org/grpc/status" - "google.golang.org/grpc/transport" -) - -// Compressor defines the interface gRPC uses to compress a message. -// -// Deprecated: use package encoding. -type Compressor interface { - // Do compresses p into w. - Do(w io.Writer, p []byte) error - // Type returns the compression algorithm the Compressor uses. - Type() string -} - -type gzipCompressor struct { - pool sync.Pool -} - -// NewGZIPCompressor creates a Compressor based on GZIP. -// -// Deprecated: use package encoding/gzip. -func NewGZIPCompressor() Compressor { - c, _ := NewGZIPCompressorWithLevel(gzip.DefaultCompression) - return c -} - -// NewGZIPCompressorWithLevel is like NewGZIPCompressor but specifies the gzip compression level instead -// of assuming DefaultCompression. -// -// The error returned will be nil if the level is valid. -// -// Deprecated: use package encoding/gzip. -func NewGZIPCompressorWithLevel(level int) (Compressor, error) { - if level < gzip.DefaultCompression || level > gzip.BestCompression { - return nil, fmt.Errorf("grpc: invalid compression level: %d", level) - } - return &gzipCompressor{ - pool: sync.Pool{ - New: func() interface{} { - w, err := gzip.NewWriterLevel(ioutil.Discard, level) - if err != nil { - panic(err) - } - return w - }, - }, - }, nil -} - -func (c *gzipCompressor) Do(w io.Writer, p []byte) error { - z := c.pool.Get().(*gzip.Writer) - defer c.pool.Put(z) - z.Reset(w) - if _, err := z.Write(p); err != nil { - return err - } - return z.Close() -} - -func (c *gzipCompressor) Type() string { - return "gzip" -} - -// Decompressor defines the interface gRPC uses to decompress a message. -// -// Deprecated: use package encoding. -type Decompressor interface { - // Do reads the data from r and uncompress them. - Do(r io.Reader) ([]byte, error) - // Type returns the compression algorithm the Decompressor uses. - Type() string -} - -type gzipDecompressor struct { - pool sync.Pool -} - -// NewGZIPDecompressor creates a Decompressor based on GZIP. -// -// Deprecated: use package encoding/gzip. -func NewGZIPDecompressor() Decompressor { - return &gzipDecompressor{} -} - -func (d *gzipDecompressor) Do(r io.Reader) ([]byte, error) { - var z *gzip.Reader - switch maybeZ := d.pool.Get().(type) { - case nil: - newZ, err := gzip.NewReader(r) - if err != nil { - return nil, err - } - z = newZ - case *gzip.Reader: - z = maybeZ - if err := z.Reset(r); err != nil { - d.pool.Put(z) - return nil, err - } - } - - defer func() { - z.Close() - d.pool.Put(z) - }() - return ioutil.ReadAll(z) -} - -func (d *gzipDecompressor) Type() string { - return "gzip" -} - -// callInfo contains all related configuration and information about an RPC. -type callInfo struct { - compressorType string - failFast bool - stream *clientStream - traceInfo traceInfo // in trace.go - maxReceiveMessageSize *int - maxSendMessageSize *int - creds credentials.PerRPCCredentials - contentSubtype string - codec baseCodec -} - -func defaultCallInfo() *callInfo { - return &callInfo{failFast: true} -} - -// CallOption configures a Call before it starts or extracts information from -// a Call after it completes. -type CallOption interface { - // before is called before the call is sent to any server. If before - // returns a non-nil error, the RPC fails with that error. - before(*callInfo) error - - // after is called after the call has completed. after cannot return an - // error, so any failures should be reported via output parameters. - after(*callInfo) -} - -// EmptyCallOption does not alter the Call configuration. -// It can be embedded in another structure to carry satellite data for use -// by interceptors. -type EmptyCallOption struct{} - -func (EmptyCallOption) before(*callInfo) error { return nil } -func (EmptyCallOption) after(*callInfo) {} - -// Header returns a CallOptions that retrieves the header metadata -// for a unary RPC. -func Header(md *metadata.MD) CallOption { - return HeaderCallOption{HeaderAddr: md} -} - -// HeaderCallOption is a CallOption for collecting response header metadata. -// The metadata field will be populated *after* the RPC completes. -// This is an EXPERIMENTAL API. -type HeaderCallOption struct { - HeaderAddr *metadata.MD -} - -func (o HeaderCallOption) before(c *callInfo) error { return nil } -func (o HeaderCallOption) after(c *callInfo) { - if c.stream != nil { - *o.HeaderAddr, _ = c.stream.Header() - } -} - -// Trailer returns a CallOptions that retrieves the trailer metadata -// for a unary RPC. -func Trailer(md *metadata.MD) CallOption { - return TrailerCallOption{TrailerAddr: md} -} - -// TrailerCallOption is a CallOption for collecting response trailer metadata. -// The metadata field will be populated *after* the RPC completes. -// This is an EXPERIMENTAL API. -type TrailerCallOption struct { - TrailerAddr *metadata.MD -} - -func (o TrailerCallOption) before(c *callInfo) error { return nil } -func (o TrailerCallOption) after(c *callInfo) { - if c.stream != nil { - *o.TrailerAddr = c.stream.Trailer() - } -} - -// Peer returns a CallOption that retrieves peer information for a unary RPC. -// The peer field will be populated *after* the RPC completes. -func Peer(p *peer.Peer) CallOption { - return PeerCallOption{PeerAddr: p} -} - -// PeerCallOption is a CallOption for collecting the identity of the remote -// peer. The peer field will be populated *after* the RPC completes. -// This is an EXPERIMENTAL API. -type PeerCallOption struct { - PeerAddr *peer.Peer -} - -func (o PeerCallOption) before(c *callInfo) error { return nil } -func (o PeerCallOption) after(c *callInfo) { - if c.stream != nil { - if x, ok := peer.FromContext(c.stream.Context()); ok { - *o.PeerAddr = *x - } - } -} - -// FailFast configures the action to take when an RPC is attempted on broken -// connections or unreachable servers. If failFast is true, the RPC will fail -// immediately. Otherwise, the RPC client will block the call until a -// connection is available (or the call is canceled or times out) and will -// retry the call if it fails due to a transient error. gRPC will not retry if -// data was written to the wire unless the server indicates it did not process -// the data. Please refer to -// https://github.com/grpc/grpc/blob/master/doc/wait-for-ready.md. -// -// By default, RPCs are "Fail Fast". -func FailFast(failFast bool) CallOption { - return FailFastCallOption{FailFast: failFast} -} - -// FailFastCallOption is a CallOption for indicating whether an RPC should fail -// fast or not. -// This is an EXPERIMENTAL API. -type FailFastCallOption struct { - FailFast bool -} - -func (o FailFastCallOption) before(c *callInfo) error { - c.failFast = o.FailFast - return nil -} -func (o FailFastCallOption) after(c *callInfo) {} - -// MaxCallRecvMsgSize returns a CallOption which sets the maximum message size the client can receive. -func MaxCallRecvMsgSize(s int) CallOption { - return MaxRecvMsgSizeCallOption{MaxRecvMsgSize: s} -} - -// MaxRecvMsgSizeCallOption is a CallOption that indicates the maximum message -// size the client can receive. -// This is an EXPERIMENTAL API. -type MaxRecvMsgSizeCallOption struct { - MaxRecvMsgSize int -} - -func (o MaxRecvMsgSizeCallOption) before(c *callInfo) error { - c.maxReceiveMessageSize = &o.MaxRecvMsgSize - return nil -} -func (o MaxRecvMsgSizeCallOption) after(c *callInfo) {} - -// MaxCallSendMsgSize returns a CallOption which sets the maximum message size the client can send. -func MaxCallSendMsgSize(s int) CallOption { - return MaxSendMsgSizeCallOption{MaxSendMsgSize: s} -} - -// MaxSendMsgSizeCallOption is a CallOption that indicates the maximum message -// size the client can send. -// This is an EXPERIMENTAL API. -type MaxSendMsgSizeCallOption struct { - MaxSendMsgSize int -} - -func (o MaxSendMsgSizeCallOption) before(c *callInfo) error { - c.maxSendMessageSize = &o.MaxSendMsgSize - return nil -} -func (o MaxSendMsgSizeCallOption) after(c *callInfo) {} - -// PerRPCCredentials returns a CallOption that sets credentials.PerRPCCredentials -// for a call. -func PerRPCCredentials(creds credentials.PerRPCCredentials) CallOption { - return PerRPCCredsCallOption{Creds: creds} -} - -// PerRPCCredsCallOption is a CallOption that indicates the per-RPC -// credentials to use for the call. -// This is an EXPERIMENTAL API. -type PerRPCCredsCallOption struct { - Creds credentials.PerRPCCredentials -} - -func (o PerRPCCredsCallOption) before(c *callInfo) error { - c.creds = o.Creds - return nil -} -func (o PerRPCCredsCallOption) after(c *callInfo) {} - -// UseCompressor returns a CallOption which sets the compressor used when -// sending the request. If WithCompressor is also set, UseCompressor has -// higher priority. -// -// This API is EXPERIMENTAL. -func UseCompressor(name string) CallOption { - return CompressorCallOption{CompressorType: name} -} - -// CompressorCallOption is a CallOption that indicates the compressor to use. -// This is an EXPERIMENTAL API. -type CompressorCallOption struct { - CompressorType string -} - -func (o CompressorCallOption) before(c *callInfo) error { - c.compressorType = o.CompressorType - return nil -} -func (o CompressorCallOption) after(c *callInfo) {} - -// CallContentSubtype returns a CallOption that will set the content-subtype -// for a call. For example, if content-subtype is "json", the Content-Type over -// the wire will be "application/grpc+json". The content-subtype is converted -// to lowercase before being included in Content-Type. See Content-Type on -// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for -// more details. -// -// If CallCustomCodec is not also used, the content-subtype will be used to -// look up the Codec to use in the registry controlled by RegisterCodec. See -// the documentation on RegisterCodec for details on registration. The lookup -// of content-subtype is case-insensitive. If no such Codec is found, the call -// will result in an error with code codes.Internal. -// -// If CallCustomCodec is also used, that Codec will be used for all request and -// response messages, with the content-subtype set to the given contentSubtype -// here for requests. -func CallContentSubtype(contentSubtype string) CallOption { - return ContentSubtypeCallOption{ContentSubtype: strings.ToLower(contentSubtype)} -} - -// ContentSubtypeCallOption is a CallOption that indicates the content-subtype -// used for marshaling messages. -// This is an EXPERIMENTAL API. -type ContentSubtypeCallOption struct { - ContentSubtype string -} - -func (o ContentSubtypeCallOption) before(c *callInfo) error { - c.contentSubtype = o.ContentSubtype - return nil -} -func (o ContentSubtypeCallOption) after(c *callInfo) {} - -// CallCustomCodec returns a CallOption that will set the given Codec to be -// used for all request and response messages for a call. The result of calling -// String() will be used as the content-subtype in a case-insensitive manner. -// -// See Content-Type on -// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for -// more details. Also see the documentation on RegisterCodec and -// CallContentSubtype for more details on the interaction between Codec and -// content-subtype. -// -// This function is provided for advanced users; prefer to use only -// CallContentSubtype to select a registered codec instead. -func CallCustomCodec(codec Codec) CallOption { - return CustomCodecCallOption{Codec: codec} -} - -// CustomCodecCallOption is a CallOption that indicates the codec used for -// marshaling messages. -// This is an EXPERIMENTAL API. -type CustomCodecCallOption struct { - Codec Codec -} - -func (o CustomCodecCallOption) before(c *callInfo) error { - c.codec = o.Codec - return nil -} -func (o CustomCodecCallOption) after(c *callInfo) {} - -// The format of the payload: compressed or not? -type payloadFormat uint8 - -const ( - compressionNone payloadFormat = iota // no compression - compressionMade -) - -// parser reads complete gRPC messages from the underlying reader. -type parser struct { - // r is the underlying reader. - // See the comment on recvMsg for the permissible - // error types. - r io.Reader - - // The header of a gRPC message. Find more detail at - // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md - header [5]byte -} - -// recvMsg reads a complete gRPC message from the stream. -// -// It returns the message and its payload (compression/encoding) -// format. The caller owns the returned msg memory. -// -// If there is an error, possible values are: -// * io.EOF, when no messages remain -// * io.ErrUnexpectedEOF -// * of type transport.ConnectionError -// * of type transport.StreamError -// No other error values or types must be returned, which also means -// that the underlying io.Reader must not return an incompatible -// error. -func (p *parser) recvMsg(maxReceiveMessageSize int) (pf payloadFormat, msg []byte, err error) { - if _, err := p.r.Read(p.header[:]); err != nil { - return 0, nil, err - } - - pf = payloadFormat(p.header[0]) - length := binary.BigEndian.Uint32(p.header[1:]) - - if length == 0 { - return pf, nil, nil - } - if int64(length) > int64(maxInt) { - return 0, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max length allowed on current machine (%d vs. %d)", length, maxInt) - } - if int(length) > maxReceiveMessageSize { - return 0, nil, status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", length, maxReceiveMessageSize) - } - // TODO(bradfitz,zhaoq): garbage. reuse buffer after proto decoding instead - // of making it for each message: - msg = make([]byte, int(length)) - if _, err := p.r.Read(msg); err != nil { - if err == io.EOF { - err = io.ErrUnexpectedEOF - } - return 0, nil, err - } - return pf, msg, nil -} - -// encode serializes msg and returns a buffer of message header and a buffer of msg. -// If msg is nil, it generates the message header and an empty msg buffer. -// TODO(ddyihai): eliminate extra Compressor parameter. -func encode(c baseCodec, msg interface{}, cp Compressor, outPayload *stats.OutPayload, compressor encoding.Compressor) ([]byte, []byte, error) { - var ( - b []byte - cbuf *bytes.Buffer - ) - const ( - payloadLen = 1 - sizeLen = 4 - ) - if msg != nil { - var err error - b, err = c.Marshal(msg) - if err != nil { - return nil, nil, status.Errorf(codes.Internal, "grpc: error while marshaling: %v", err.Error()) - } - if outPayload != nil { - outPayload.Payload = msg - // TODO truncate large payload. - outPayload.Data = b - outPayload.Length = len(b) - } - if compressor != nil || cp != nil { - cbuf = new(bytes.Buffer) - // Has compressor, check Compressor is set by UseCompressor first. - if compressor != nil { - z, _ := compressor.Compress(cbuf) - if _, err := z.Write(b); err != nil { - return nil, nil, status.Errorf(codes.Internal, "grpc: error while compressing: %v", err.Error()) - } - z.Close() - } else { - // If Compressor is not set by UseCompressor, use default Compressor - if err := cp.Do(cbuf, b); err != nil { - return nil, nil, status.Errorf(codes.Internal, "grpc: error while compressing: %v", err.Error()) - } - } - b = cbuf.Bytes() - } - } - if uint(len(b)) > math.MaxUint32 { - return nil, nil, status.Errorf(codes.ResourceExhausted, "grpc: message too large (%d bytes)", len(b)) - } - - bufHeader := make([]byte, payloadLen+sizeLen) - if compressor != nil || cp != nil { - bufHeader[0] = byte(compressionMade) - } else { - bufHeader[0] = byte(compressionNone) - } - - // Write length of b into buf - binary.BigEndian.PutUint32(bufHeader[payloadLen:], uint32(len(b))) - if outPayload != nil { - outPayload.WireLength = payloadLen + sizeLen + len(b) - } - return bufHeader, b, nil -} - -func checkRecvPayload(pf payloadFormat, recvCompress string, haveCompressor bool) *status.Status { - switch pf { - case compressionNone: - case compressionMade: - if recvCompress == "" || recvCompress == encoding.Identity { - return status.New(codes.Internal, "grpc: compressed flag set with identity or empty encoding") - } - if !haveCompressor { - return status.Newf(codes.Unimplemented, "grpc: Decompressor is not installed for grpc-encoding %q", recvCompress) - } - default: - return status.Newf(codes.Internal, "grpc: received unexpected payload format %d", pf) - } - return nil -} - -// For the two compressor parameters, both should not be set, but if they are, -// dc takes precedence over compressor. -// TODO(dfawley): wrap the old compressor/decompressor using the new API? -func recv(p *parser, c baseCodec, s *transport.Stream, dc Decompressor, m interface{}, maxReceiveMessageSize int, inPayload *stats.InPayload, compressor encoding.Compressor) error { - pf, d, err := p.recvMsg(maxReceiveMessageSize) - if err != nil { - return err - } - if inPayload != nil { - inPayload.WireLength = len(d) - } - - if st := checkRecvPayload(pf, s.RecvCompress(), compressor != nil || dc != nil); st != nil { - return st.Err() - } - - if pf == compressionMade { - // To match legacy behavior, if the decompressor is set by WithDecompressor or RPCDecompressor, - // use this decompressor as the default. - if dc != nil { - d, err = dc.Do(bytes.NewReader(d)) - if err != nil { - return status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err) - } - } else { - dcReader, err := compressor.Decompress(bytes.NewReader(d)) - if err != nil { - return status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err) - } - d, err = ioutil.ReadAll(dcReader) - if err != nil { - return status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err) - } - } - } - if len(d) > maxReceiveMessageSize { - // TODO: Revisit the error code. Currently keep it consistent with java - // implementation. - return status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", len(d), maxReceiveMessageSize) - } - if err := c.Unmarshal(d, m); err != nil { - return status.Errorf(codes.Internal, "grpc: failed to unmarshal the received message %v", err) - } - if inPayload != nil { - inPayload.RecvTime = time.Now() - inPayload.Payload = m - // TODO truncate large payload. - inPayload.Data = d - inPayload.Length = len(d) - } - return nil -} - -type rpcInfo struct { - failfast bool -} - -type rpcInfoContextKey struct{} - -func newContextWithRPCInfo(ctx context.Context, failfast bool) context.Context { - return context.WithValue(ctx, rpcInfoContextKey{}, &rpcInfo{failfast: failfast}) -} - -func rpcInfoFromContext(ctx context.Context) (s *rpcInfo, ok bool) { - s, ok = ctx.Value(rpcInfoContextKey{}).(*rpcInfo) - return -} - -// Code returns the error code for err if it was produced by the rpc system. -// Otherwise, it returns codes.Unknown. -// -// Deprecated: use status.FromError and Code method instead. -func Code(err error) codes.Code { - if s, ok := status.FromError(err); ok { - return s.Code() - } - return codes.Unknown -} - -// ErrorDesc returns the error description of err if it was produced by the rpc system. -// Otherwise, it returns err.Error() or empty string when err is nil. -// -// Deprecated: use status.FromError and Message method instead. -func ErrorDesc(err error) string { - if s, ok := status.FromError(err); ok { - return s.Message() - } - return err.Error() -} - -// Errorf returns an error containing an error code and a description; -// Errorf returns nil if c is OK. -// -// Deprecated: use status.Errorf instead. -func Errorf(c codes.Code, format string, a ...interface{}) error { - return status.Errorf(c, format, a...) -} - -// setCallInfoCodec should only be called after CallOptions have been applied. -func setCallInfoCodec(c *callInfo) error { - if c.codec != nil { - // codec was already set by a CallOption; use it. - return nil - } - - if c.contentSubtype == "" { - // No codec specified in CallOptions; use proto by default. - c.codec = encoding.GetCodec(proto.Name) - return nil - } - - // c.contentSubtype is already lowercased in CallContentSubtype - c.codec = encoding.GetCodec(c.contentSubtype) - if c.codec == nil { - return status.Errorf(codes.Internal, "no codec registered for content-subtype %s", c.contentSubtype) - } - return nil -} - -// parseDialTarget returns the network and address to pass to dialer -func parseDialTarget(target string) (net string, addr string) { - net = "tcp" - - m1 := strings.Index(target, ":") - m2 := strings.Index(target, ":/") - - // handle unix:addr which will fail with url.Parse - if m1 >= 0 && m2 < 0 { - if n := target[0:m1]; n == "unix" { - net = n - addr = target[m1+1:] - return net, addr - } - } - if m2 >= 0 { - t, err := url.Parse(target) - if err != nil { - return net, target - } - scheme := t.Scheme - addr = t.Path - if scheme == "unix" { - net = scheme - if addr == "" { - addr = t.Host - } - return net, addr - } - } - - return net, target -} - -// The SupportPackageIsVersion variables are referenced from generated protocol -// buffer files to ensure compatibility with the gRPC version used. The latest -// support package version is 5. -// -// Older versions are kept for compatibility. They may be removed if -// compatibility cannot be maintained. -// -// These constants should not be referenced from any other code. -const ( - SupportPackageIsVersion3 = true - SupportPackageIsVersion4 = true - SupportPackageIsVersion5 = true -) - -// Version is the current grpc version. -const Version = "1.12.0" - -const grpcUA = "grpc-go/" + Version diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go deleted file mode 100644 index 4969331cb3d..00000000000 --- a/vendor/google.golang.org/grpc/server.go +++ /dev/null @@ -1,1486 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "bytes" - "errors" - "fmt" - "io" - "math" - "net" - "net/http" - "reflect" - "runtime" - "strings" - "sync" - "time" - - "io/ioutil" - - "golang.org/x/net/context" - "golang.org/x/net/http2" - "golang.org/x/net/trace" - - "google.golang.org/grpc/channelz" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/encoding" - "google.golang.org/grpc/encoding/proto" - "google.golang.org/grpc/grpclog" - "google.golang.org/grpc/internal" - "google.golang.org/grpc/keepalive" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/stats" - "google.golang.org/grpc/status" - "google.golang.org/grpc/tap" - "google.golang.org/grpc/transport" -) - -const ( - defaultServerMaxReceiveMessageSize = 1024 * 1024 * 4 - defaultServerMaxSendMessageSize = math.MaxInt32 -) - -type methodHandler func(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor UnaryServerInterceptor) (interface{}, error) - -// MethodDesc represents an RPC service's method specification. -type MethodDesc struct { - MethodName string - Handler methodHandler -} - -// ServiceDesc represents an RPC service's specification. -type ServiceDesc struct { - ServiceName string - // The pointer to the service interface. Used to check whether the user - // provided implementation satisfies the interface requirements. - HandlerType interface{} - Methods []MethodDesc - Streams []StreamDesc - Metadata interface{} -} - -// service consists of the information of the server serving this service and -// the methods in this service. -type service struct { - server interface{} // the server for service methods - md map[string]*MethodDesc - sd map[string]*StreamDesc - mdata interface{} -} - -// Server is a gRPC server to serve RPC requests. -type Server struct { - opts options - - mu sync.Mutex // guards following - lis map[net.Listener]bool - conns map[io.Closer]bool - serve bool - drain bool - cv *sync.Cond // signaled when connections close for GracefulStop - m map[string]*service // service name -> service info - events trace.EventLog - - quit chan struct{} - done chan struct{} - quitOnce sync.Once - doneOnce sync.Once - channelzRemoveOnce sync.Once - serveWG sync.WaitGroup // counts active Serve goroutines for GracefulStop - - channelzID int64 // channelz unique identification number - czmu sync.RWMutex - callsStarted int64 - callsFailed int64 - callsSucceeded int64 - lastCallStartedTime time.Time -} - -type options struct { - creds credentials.TransportCredentials - codec baseCodec - cp Compressor - dc Decompressor - unaryInt UnaryServerInterceptor - streamInt StreamServerInterceptor - inTapHandle tap.ServerInHandle - statsHandler stats.Handler - maxConcurrentStreams uint32 - maxReceiveMessageSize int - maxSendMessageSize int - useHandlerImpl bool // use http.Handler-based server - unknownStreamDesc *StreamDesc - keepaliveParams keepalive.ServerParameters - keepalivePolicy keepalive.EnforcementPolicy - initialWindowSize int32 - initialConnWindowSize int32 - writeBufferSize int - readBufferSize int - connectionTimeout time.Duration -} - -var defaultServerOptions = options{ - maxReceiveMessageSize: defaultServerMaxReceiveMessageSize, - maxSendMessageSize: defaultServerMaxSendMessageSize, - connectionTimeout: 120 * time.Second, -} - -// A ServerOption sets options such as credentials, codec and keepalive parameters, etc. -type ServerOption func(*options) - -// WriteBufferSize lets you set the size of write buffer, this determines how much data can be batched -// before doing a write on the wire. -func WriteBufferSize(s int) ServerOption { - return func(o *options) { - o.writeBufferSize = s - } -} - -// ReadBufferSize lets you set the size of read buffer, this determines how much data can be read at most -// for one read syscall. -func ReadBufferSize(s int) ServerOption { - return func(o *options) { - o.readBufferSize = s - } -} - -// InitialWindowSize returns a ServerOption that sets window size for stream. -// The lower bound for window size is 64K and any value smaller than that will be ignored. -func InitialWindowSize(s int32) ServerOption { - return func(o *options) { - o.initialWindowSize = s - } -} - -// InitialConnWindowSize returns a ServerOption that sets window size for a connection. -// The lower bound for window size is 64K and any value smaller than that will be ignored. -func InitialConnWindowSize(s int32) ServerOption { - return func(o *options) { - o.initialConnWindowSize = s - } -} - -// KeepaliveParams returns a ServerOption that sets keepalive and max-age parameters for the server. -func KeepaliveParams(kp keepalive.ServerParameters) ServerOption { - return func(o *options) { - o.keepaliveParams = kp - } -} - -// KeepaliveEnforcementPolicy returns a ServerOption that sets keepalive enforcement policy for the server. -func KeepaliveEnforcementPolicy(kep keepalive.EnforcementPolicy) ServerOption { - return func(o *options) { - o.keepalivePolicy = kep - } -} - -// CustomCodec returns a ServerOption that sets a codec for message marshaling and unmarshaling. -// -// This will override any lookups by content-subtype for Codecs registered with RegisterCodec. -func CustomCodec(codec Codec) ServerOption { - return func(o *options) { - o.codec = codec - } -} - -// RPCCompressor returns a ServerOption that sets a compressor for outbound -// messages. For backward compatibility, all outbound messages will be sent -// using this compressor, regardless of incoming message compression. By -// default, server messages will be sent using the same compressor with which -// request messages were sent. -// -// Deprecated: use encoding.RegisterCompressor instead. -func RPCCompressor(cp Compressor) ServerOption { - return func(o *options) { - o.cp = cp - } -} - -// RPCDecompressor returns a ServerOption that sets a decompressor for inbound -// messages. It has higher priority than decompressors registered via -// encoding.RegisterCompressor. -// -// Deprecated: use encoding.RegisterCompressor instead. -func RPCDecompressor(dc Decompressor) ServerOption { - return func(o *options) { - o.dc = dc - } -} - -// MaxMsgSize returns a ServerOption to set the max message size in bytes the server can receive. -// If this is not set, gRPC uses the default limit. -// -// Deprecated: use MaxRecvMsgSize instead. -func MaxMsgSize(m int) ServerOption { - return MaxRecvMsgSize(m) -} - -// MaxRecvMsgSize returns a ServerOption to set the max message size in bytes the server can receive. -// If this is not set, gRPC uses the default 4MB. -func MaxRecvMsgSize(m int) ServerOption { - return func(o *options) { - o.maxReceiveMessageSize = m - } -} - -// MaxSendMsgSize returns a ServerOption to set the max message size in bytes the server can send. -// If this is not set, gRPC uses the default 4MB. -func MaxSendMsgSize(m int) ServerOption { - return func(o *options) { - o.maxSendMessageSize = m - } -} - -// MaxConcurrentStreams returns a ServerOption that will apply a limit on the number -// of concurrent streams to each ServerTransport. -func MaxConcurrentStreams(n uint32) ServerOption { - return func(o *options) { - o.maxConcurrentStreams = n - } -} - -// Creds returns a ServerOption that sets credentials for server connections. -func Creds(c credentials.TransportCredentials) ServerOption { - return func(o *options) { - o.creds = c - } -} - -// UnaryInterceptor returns a ServerOption that sets the UnaryServerInterceptor for the -// server. Only one unary interceptor can be installed. The construction of multiple -// interceptors (e.g., chaining) can be implemented at the caller. -func UnaryInterceptor(i UnaryServerInterceptor) ServerOption { - return func(o *options) { - if o.unaryInt != nil { - panic("The unary server interceptor was already set and may not be reset.") - } - o.unaryInt = i - } -} - -// StreamInterceptor returns a ServerOption that sets the StreamServerInterceptor for the -// server. Only one stream interceptor can be installed. -func StreamInterceptor(i StreamServerInterceptor) ServerOption { - return func(o *options) { - if o.streamInt != nil { - panic("The stream server interceptor was already set and may not be reset.") - } - o.streamInt = i - } -} - -// InTapHandle returns a ServerOption that sets the tap handle for all the server -// transport to be created. Only one can be installed. -func InTapHandle(h tap.ServerInHandle) ServerOption { - return func(o *options) { - if o.inTapHandle != nil { - panic("The tap handle was already set and may not be reset.") - } - o.inTapHandle = h - } -} - -// StatsHandler returns a ServerOption that sets the stats handler for the server. -func StatsHandler(h stats.Handler) ServerOption { - return func(o *options) { - o.statsHandler = h - } -} - -// UnknownServiceHandler returns a ServerOption that allows for adding a custom -// unknown service handler. The provided method is a bidi-streaming RPC service -// handler that will be invoked instead of returning the "unimplemented" gRPC -// error whenever a request is received for an unregistered service or method. -// The handling function has full access to the Context of the request and the -// stream, and the invocation bypasses interceptors. -func UnknownServiceHandler(streamHandler StreamHandler) ServerOption { - return func(o *options) { - o.unknownStreamDesc = &StreamDesc{ - StreamName: "unknown_service_handler", - Handler: streamHandler, - // We need to assume that the users of the streamHandler will want to use both. - ClientStreams: true, - ServerStreams: true, - } - } -} - -// ConnectionTimeout returns a ServerOption that sets the timeout for -// connection establishment (up to and including HTTP/2 handshaking) for all -// new connections. If this is not set, the default is 120 seconds. A zero or -// negative value will result in an immediate timeout. -// -// This API is EXPERIMENTAL. -func ConnectionTimeout(d time.Duration) ServerOption { - return func(o *options) { - o.connectionTimeout = d - } -} - -// NewServer creates a gRPC server which has no service registered and has not -// started to accept requests yet. -func NewServer(opt ...ServerOption) *Server { - opts := defaultServerOptions - for _, o := range opt { - o(&opts) - } - s := &Server{ - lis: make(map[net.Listener]bool), - opts: opts, - conns: make(map[io.Closer]bool), - m: make(map[string]*service), - quit: make(chan struct{}), - done: make(chan struct{}), - } - s.cv = sync.NewCond(&s.mu) - if EnableTracing { - _, file, line, _ := runtime.Caller(1) - s.events = trace.NewEventLog("grpc.Server", fmt.Sprintf("%s:%d", file, line)) - } - - if channelz.IsOn() { - s.channelzID = channelz.RegisterServer(s, "") - } - return s -} - -// printf records an event in s's event log, unless s has been stopped. -// REQUIRES s.mu is held. -func (s *Server) printf(format string, a ...interface{}) { - if s.events != nil { - s.events.Printf(format, a...) - } -} - -// errorf records an error in s's event log, unless s has been stopped. -// REQUIRES s.mu is held. -func (s *Server) errorf(format string, a ...interface{}) { - if s.events != nil { - s.events.Errorf(format, a...) - } -} - -// RegisterService registers a service and its implementation to the gRPC -// server. It is called from the IDL generated code. This must be called before -// invoking Serve. -func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) { - ht := reflect.TypeOf(sd.HandlerType).Elem() - st := reflect.TypeOf(ss) - if !st.Implements(ht) { - grpclog.Fatalf("grpc: Server.RegisterService found the handler of type %v that does not satisfy %v", st, ht) - } - s.register(sd, ss) -} - -func (s *Server) register(sd *ServiceDesc, ss interface{}) { - s.mu.Lock() - defer s.mu.Unlock() - s.printf("RegisterService(%q)", sd.ServiceName) - if s.serve { - grpclog.Fatalf("grpc: Server.RegisterService after Server.Serve for %q", sd.ServiceName) - } - if _, ok := s.m[sd.ServiceName]; ok { - grpclog.Fatalf("grpc: Server.RegisterService found duplicate service registration for %q", sd.ServiceName) - } - srv := &service{ - server: ss, - md: make(map[string]*MethodDesc), - sd: make(map[string]*StreamDesc), - mdata: sd.Metadata, - } - for i := range sd.Methods { - d := &sd.Methods[i] - srv.md[d.MethodName] = d - } - for i := range sd.Streams { - d := &sd.Streams[i] - srv.sd[d.StreamName] = d - } - s.m[sd.ServiceName] = srv -} - -// MethodInfo contains the information of an RPC including its method name and type. -type MethodInfo struct { - // Name is the method name only, without the service name or package name. - Name string - // IsClientStream indicates whether the RPC is a client streaming RPC. - IsClientStream bool - // IsServerStream indicates whether the RPC is a server streaming RPC. - IsServerStream bool -} - -// ServiceInfo contains unary RPC method info, streaming RPC method info and metadata for a service. -type ServiceInfo struct { - Methods []MethodInfo - // Metadata is the metadata specified in ServiceDesc when registering service. - Metadata interface{} -} - -// GetServiceInfo returns a map from service names to ServiceInfo. -// Service names include the package names, in the form of .. -func (s *Server) GetServiceInfo() map[string]ServiceInfo { - ret := make(map[string]ServiceInfo) - for n, srv := range s.m { - methods := make([]MethodInfo, 0, len(srv.md)+len(srv.sd)) - for m := range srv.md { - methods = append(methods, MethodInfo{ - Name: m, - IsClientStream: false, - IsServerStream: false, - }) - } - for m, d := range srv.sd { - methods = append(methods, MethodInfo{ - Name: m, - IsClientStream: d.ClientStreams, - IsServerStream: d.ServerStreams, - }) - } - - ret[n] = ServiceInfo{ - Methods: methods, - Metadata: srv.mdata, - } - } - return ret -} - -// ErrServerStopped indicates that the operation is now illegal because of -// the server being stopped. -var ErrServerStopped = errors.New("grpc: the server has been stopped") - -func (s *Server) useTransportAuthenticator(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) { - if s.opts.creds == nil { - return rawConn, nil, nil - } - return s.opts.creds.ServerHandshake(rawConn) -} - -type listenSocket struct { - net.Listener - channelzID int64 -} - -func (l *listenSocket) ChannelzMetric() *channelz.SocketInternalMetric { - return &channelz.SocketInternalMetric{ - LocalAddr: l.Listener.Addr(), - } -} - -func (l *listenSocket) Close() error { - err := l.Listener.Close() - if channelz.IsOn() { - channelz.RemoveEntry(l.channelzID) - } - return err -} - -// Serve accepts incoming connections on the listener lis, creating a new -// ServerTransport and service goroutine for each. The service goroutines -// read gRPC requests and then call the registered handlers to reply to them. -// Serve returns when lis.Accept fails with fatal errors. lis will be closed when -// this method returns. -// Serve will return a non-nil error unless Stop or GracefulStop is called. -func (s *Server) Serve(lis net.Listener) error { - s.mu.Lock() - s.printf("serving") - s.serve = true - if s.lis == nil { - // Serve called after Stop or GracefulStop. - s.mu.Unlock() - lis.Close() - return ErrServerStopped - } - - s.serveWG.Add(1) - defer func() { - s.serveWG.Done() - select { - // Stop or GracefulStop called; block until done and return nil. - case <-s.quit: - <-s.done - default: - } - }() - - ls := &listenSocket{Listener: lis} - s.lis[ls] = true - - if channelz.IsOn() { - ls.channelzID = channelz.RegisterListenSocket(ls, s.channelzID, "") - } - s.mu.Unlock() - - defer func() { - s.mu.Lock() - if s.lis != nil && s.lis[ls] { - ls.Close() - delete(s.lis, ls) - } - s.mu.Unlock() - }() - - var tempDelay time.Duration // how long to sleep on accept failure - - for { - rawConn, err := lis.Accept() - if err != nil { - if ne, ok := err.(interface { - Temporary() bool - }); ok && ne.Temporary() { - if tempDelay == 0 { - tempDelay = 5 * time.Millisecond - } else { - tempDelay *= 2 - } - if max := 1 * time.Second; tempDelay > max { - tempDelay = max - } - s.mu.Lock() - s.printf("Accept error: %v; retrying in %v", err, tempDelay) - s.mu.Unlock() - timer := time.NewTimer(tempDelay) - select { - case <-timer.C: - case <-s.quit: - timer.Stop() - return nil - } - continue - } - s.mu.Lock() - s.printf("done serving; Accept = %v", err) - s.mu.Unlock() - - select { - case <-s.quit: - return nil - default: - } - return err - } - tempDelay = 0 - // Start a new goroutine to deal with rawConn so we don't stall this Accept - // loop goroutine. - // - // Make sure we account for the goroutine so GracefulStop doesn't nil out - // s.conns before this conn can be added. - s.serveWG.Add(1) - go func() { - s.handleRawConn(rawConn) - s.serveWG.Done() - }() - } -} - -// handleRawConn forks a goroutine to handle a just-accepted connection that -// has not had any I/O performed on it yet. -func (s *Server) handleRawConn(rawConn net.Conn) { - rawConn.SetDeadline(time.Now().Add(s.opts.connectionTimeout)) - conn, authInfo, err := s.useTransportAuthenticator(rawConn) - if err != nil { - s.mu.Lock() - s.errorf("ServerHandshake(%q) failed: %v", rawConn.RemoteAddr(), err) - s.mu.Unlock() - grpclog.Warningf("grpc: Server.Serve failed to complete security handshake from %q: %v", rawConn.RemoteAddr(), err) - // If serverHandshake returns ErrConnDispatched, keep rawConn open. - if err != credentials.ErrConnDispatched { - rawConn.Close() - } - rawConn.SetDeadline(time.Time{}) - return - } - - s.mu.Lock() - if s.conns == nil { - s.mu.Unlock() - conn.Close() - return - } - s.mu.Unlock() - - var serve func() - c := conn.(io.Closer) - if s.opts.useHandlerImpl { - serve = func() { s.serveUsingHandler(conn) } - } else { - // Finish handshaking (HTTP2) - st := s.newHTTP2Transport(conn, authInfo) - if st == nil { - return - } - c = st - serve = func() { s.serveStreams(st) } - } - - rawConn.SetDeadline(time.Time{}) - if !s.addConn(c) { - return - } - go func() { - serve() - s.removeConn(c) - }() -} - -// newHTTP2Transport sets up a http/2 transport (using the -// gRPC http2 server transport in transport/http2_server.go). -func (s *Server) newHTTP2Transport(c net.Conn, authInfo credentials.AuthInfo) transport.ServerTransport { - config := &transport.ServerConfig{ - MaxStreams: s.opts.maxConcurrentStreams, - AuthInfo: authInfo, - InTapHandle: s.opts.inTapHandle, - StatsHandler: s.opts.statsHandler, - KeepaliveParams: s.opts.keepaliveParams, - KeepalivePolicy: s.opts.keepalivePolicy, - InitialWindowSize: s.opts.initialWindowSize, - InitialConnWindowSize: s.opts.initialConnWindowSize, - WriteBufferSize: s.opts.writeBufferSize, - ReadBufferSize: s.opts.readBufferSize, - ChannelzParentID: s.channelzID, - } - st, err := transport.NewServerTransport("http2", c, config) - if err != nil { - s.mu.Lock() - s.errorf("NewServerTransport(%q) failed: %v", c.RemoteAddr(), err) - s.mu.Unlock() - c.Close() - grpclog.Warningln("grpc: Server.Serve failed to create ServerTransport: ", err) - return nil - } - - return st -} - -func (s *Server) serveStreams(st transport.ServerTransport) { - defer st.Close() - var wg sync.WaitGroup - st.HandleStreams(func(stream *transport.Stream) { - wg.Add(1) - go func() { - defer wg.Done() - s.handleStream(st, stream, s.traceInfo(st, stream)) - }() - }, func(ctx context.Context, method string) context.Context { - if !EnableTracing { - return ctx - } - tr := trace.New("grpc.Recv."+methodFamily(method), method) - return trace.NewContext(ctx, tr) - }) - wg.Wait() -} - -var _ http.Handler = (*Server)(nil) - -// serveUsingHandler is called from handleRawConn when s is configured -// to handle requests via the http.Handler interface. It sets up a -// net/http.Server to handle the just-accepted conn. The http.Server -// is configured to route all incoming requests (all HTTP/2 streams) -// to ServeHTTP, which creates a new ServerTransport for each stream. -// serveUsingHandler blocks until conn closes. -// -// This codepath is only used when Server.TestingUseHandlerImpl has -// been configured. This lets the end2end tests exercise the ServeHTTP -// method as one of the environment types. -// -// conn is the *tls.Conn that's already been authenticated. -func (s *Server) serveUsingHandler(conn net.Conn) { - h2s := &http2.Server{ - MaxConcurrentStreams: s.opts.maxConcurrentStreams, - } - h2s.ServeConn(conn, &http2.ServeConnOpts{ - Handler: s, - }) -} - -// ServeHTTP implements the Go standard library's http.Handler -// interface by responding to the gRPC request r, by looking up -// the requested gRPC method in the gRPC server s. -// -// The provided HTTP request must have arrived on an HTTP/2 -// connection. When using the Go standard library's server, -// practically this means that the Request must also have arrived -// over TLS. -// -// To share one port (such as 443 for https) between gRPC and an -// existing http.Handler, use a root http.Handler such as: -// -// if r.ProtoMajor == 2 && strings.HasPrefix( -// r.Header.Get("Content-Type"), "application/grpc") { -// grpcServer.ServeHTTP(w, r) -// } else { -// yourMux.ServeHTTP(w, r) -// } -// -// Note that ServeHTTP uses Go's HTTP/2 server implementation which is totally -// separate from grpc-go's HTTP/2 server. Performance and features may vary -// between the two paths. ServeHTTP does not support some gRPC features -// available through grpc-go's HTTP/2 server, and it is currently EXPERIMENTAL -// and subject to change. -func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request) { - st, err := transport.NewServerHandlerTransport(w, r, s.opts.statsHandler) - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - if !s.addConn(st) { - return - } - defer s.removeConn(st) - s.serveStreams(st) -} - -// traceInfo returns a traceInfo and associates it with stream, if tracing is enabled. -// If tracing is not enabled, it returns nil. -func (s *Server) traceInfo(st transport.ServerTransport, stream *transport.Stream) (trInfo *traceInfo) { - tr, ok := trace.FromContext(stream.Context()) - if !ok { - return nil - } - - trInfo = &traceInfo{ - tr: tr, - } - trInfo.firstLine.client = false - trInfo.firstLine.remoteAddr = st.RemoteAddr() - - if dl, ok := stream.Context().Deadline(); ok { - trInfo.firstLine.deadline = dl.Sub(time.Now()) - } - return trInfo -} - -func (s *Server) addConn(c io.Closer) bool { - s.mu.Lock() - defer s.mu.Unlock() - if s.conns == nil { - c.Close() - return false - } - if s.drain { - // Transport added after we drained our existing conns: drain it - // immediately. - c.(transport.ServerTransport).Drain() - } - s.conns[c] = true - return true -} - -func (s *Server) removeConn(c io.Closer) { - s.mu.Lock() - defer s.mu.Unlock() - if s.conns != nil { - delete(s.conns, c) - s.cv.Broadcast() - } -} - -// ChannelzMetric returns ServerInternalMetric of current server. -// This is an EXPERIMENTAL API. -func (s *Server) ChannelzMetric() *channelz.ServerInternalMetric { - s.czmu.RLock() - defer s.czmu.RUnlock() - return &channelz.ServerInternalMetric{ - CallsStarted: s.callsStarted, - CallsSucceeded: s.callsSucceeded, - CallsFailed: s.callsFailed, - LastCallStartedTimestamp: s.lastCallStartedTime, - } -} - -func (s *Server) incrCallsStarted() { - s.czmu.Lock() - s.callsStarted++ - s.lastCallStartedTime = time.Now() - s.czmu.Unlock() -} - -func (s *Server) incrCallsSucceeded() { - s.czmu.Lock() - s.callsSucceeded++ - s.czmu.Unlock() -} - -func (s *Server) incrCallsFailed() { - s.czmu.Lock() - s.callsFailed++ - s.czmu.Unlock() -} - -func (s *Server) sendResponse(t transport.ServerTransport, stream *transport.Stream, msg interface{}, cp Compressor, opts *transport.Options, comp encoding.Compressor) error { - var ( - outPayload *stats.OutPayload - ) - if s.opts.statsHandler != nil { - outPayload = &stats.OutPayload{} - } - hdr, data, err := encode(s.getCodec(stream.ContentSubtype()), msg, cp, outPayload, comp) - if err != nil { - grpclog.Errorln("grpc: server failed to encode response: ", err) - return err - } - if len(data) > s.opts.maxSendMessageSize { - return status.Errorf(codes.ResourceExhausted, "grpc: trying to send message larger than max (%d vs. %d)", len(data), s.opts.maxSendMessageSize) - } - err = t.Write(stream, hdr, data, opts) - if err == nil && outPayload != nil { - outPayload.SentTime = time.Now() - s.opts.statsHandler.HandleRPC(stream.Context(), outPayload) - } - return err -} - -func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc, trInfo *traceInfo) (err error) { - if channelz.IsOn() { - s.incrCallsStarted() - defer func() { - if err != nil && err != io.EOF { - s.incrCallsFailed() - } else { - s.incrCallsSucceeded() - } - }() - } - sh := s.opts.statsHandler - if sh != nil { - beginTime := time.Now() - begin := &stats.Begin{ - BeginTime: beginTime, - } - sh.HandleRPC(stream.Context(), begin) - defer func() { - end := &stats.End{ - BeginTime: beginTime, - EndTime: time.Now(), - } - if err != nil && err != io.EOF { - end.Error = toRPCErr(err) - } - sh.HandleRPC(stream.Context(), end) - }() - } - if trInfo != nil { - defer trInfo.tr.Finish() - trInfo.firstLine.client = false - trInfo.tr.LazyLog(&trInfo.firstLine, false) - defer func() { - if err != nil && err != io.EOF { - trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) - trInfo.tr.SetError() - } - }() - } - - // comp and cp are used for compression. decomp and dc are used for - // decompression. If comp and decomp are both set, they are the same; - // however they are kept separate to ensure that at most one of the - // compressor/decompressor variable pairs are set for use later. - var comp, decomp encoding.Compressor - var cp Compressor - var dc Decompressor - - // If dc is set and matches the stream's compression, use it. Otherwise, try - // to find a matching registered compressor for decomp. - if rc := stream.RecvCompress(); s.opts.dc != nil && s.opts.dc.Type() == rc { - dc = s.opts.dc - } else if rc != "" && rc != encoding.Identity { - decomp = encoding.GetCompressor(rc) - if decomp == nil { - st := status.Newf(codes.Unimplemented, "grpc: Decompressor is not installed for grpc-encoding %q", rc) - t.WriteStatus(stream, st) - return st.Err() - } - } - - // If cp is set, use it. Otherwise, attempt to compress the response using - // the incoming message compression method. - // - // NOTE: this needs to be ahead of all handling, https://github.com/grpc/grpc-go/issues/686. - if s.opts.cp != nil { - cp = s.opts.cp - stream.SetSendCompress(cp.Type()) - } else if rc := stream.RecvCompress(); rc != "" && rc != encoding.Identity { - // Legacy compressor not specified; attempt to respond with same encoding. - comp = encoding.GetCompressor(rc) - if comp != nil { - stream.SetSendCompress(rc) - } - } - - p := &parser{r: stream} - pf, req, err := p.recvMsg(s.opts.maxReceiveMessageSize) - if err == io.EOF { - // The entire stream is done (for unary RPC only). - return err - } - if err == io.ErrUnexpectedEOF { - err = status.Errorf(codes.Internal, io.ErrUnexpectedEOF.Error()) - } - if err != nil { - if st, ok := status.FromError(err); ok { - if e := t.WriteStatus(stream, st); e != nil { - grpclog.Warningf("grpc: Server.processUnaryRPC failed to write status %v", e) - } - } else { - switch st := err.(type) { - case transport.ConnectionError: - // Nothing to do here. - case transport.StreamError: - if e := t.WriteStatus(stream, status.New(st.Code, st.Desc)); e != nil { - grpclog.Warningf("grpc: Server.processUnaryRPC failed to write status %v", e) - } - default: - panic(fmt.Sprintf("grpc: Unexpected error (%T) from recvMsg: %v", st, st)) - } - } - return err - } - if channelz.IsOn() { - t.IncrMsgRecv() - } - if st := checkRecvPayload(pf, stream.RecvCompress(), dc != nil || decomp != nil); st != nil { - if e := t.WriteStatus(stream, st); e != nil { - grpclog.Warningf("grpc: Server.processUnaryRPC failed to write status %v", e) - } - return st.Err() - } - var inPayload *stats.InPayload - if sh != nil { - inPayload = &stats.InPayload{ - RecvTime: time.Now(), - } - } - df := func(v interface{}) error { - if inPayload != nil { - inPayload.WireLength = len(req) - } - if pf == compressionMade { - var err error - if dc != nil { - req, err = dc.Do(bytes.NewReader(req)) - if err != nil { - return status.Errorf(codes.Internal, err.Error()) - } - } else { - tmp, _ := decomp.Decompress(bytes.NewReader(req)) - req, err = ioutil.ReadAll(tmp) - if err != nil { - return status.Errorf(codes.Internal, "grpc: failed to decompress the received message %v", err) - } - } - } - if len(req) > s.opts.maxReceiveMessageSize { - // TODO: Revisit the error code. Currently keep it consistent with - // java implementation. - return status.Errorf(codes.ResourceExhausted, "grpc: received message larger than max (%d vs. %d)", len(req), s.opts.maxReceiveMessageSize) - } - if err := s.getCodec(stream.ContentSubtype()).Unmarshal(req, v); err != nil { - return status.Errorf(codes.Internal, "grpc: error unmarshalling request: %v", err) - } - if inPayload != nil { - inPayload.Payload = v - inPayload.Data = req - inPayload.Length = len(req) - sh.HandleRPC(stream.Context(), inPayload) - } - if trInfo != nil { - trInfo.tr.LazyLog(&payload{sent: false, msg: v}, true) - } - return nil - } - ctx := NewContextWithServerTransportStream(stream.Context(), stream) - reply, appErr := md.Handler(srv.server, ctx, df, s.opts.unaryInt) - if appErr != nil { - appStatus, ok := status.FromError(appErr) - if !ok { - // Convert appErr if it is not a grpc status error. - appErr = status.Error(codes.Unknown, appErr.Error()) - appStatus, _ = status.FromError(appErr) - } - if trInfo != nil { - trInfo.tr.LazyLog(stringer(appStatus.Message()), true) - trInfo.tr.SetError() - } - if e := t.WriteStatus(stream, appStatus); e != nil { - grpclog.Warningf("grpc: Server.processUnaryRPC failed to write status: %v", e) - } - return appErr - } - if trInfo != nil { - trInfo.tr.LazyLog(stringer("OK"), false) - } - opts := &transport.Options{ - Last: true, - Delay: false, - } - - if err := s.sendResponse(t, stream, reply, cp, opts, comp); err != nil { - if err == io.EOF { - // The entire stream is done (for unary RPC only). - return err - } - if s, ok := status.FromError(err); ok { - if e := t.WriteStatus(stream, s); e != nil { - grpclog.Warningf("grpc: Server.processUnaryRPC failed to write status: %v", e) - } - } else { - switch st := err.(type) { - case transport.ConnectionError: - // Nothing to do here. - case transport.StreamError: - if e := t.WriteStatus(stream, status.New(st.Code, st.Desc)); e != nil { - grpclog.Warningf("grpc: Server.processUnaryRPC failed to write status %v", e) - } - default: - panic(fmt.Sprintf("grpc: Unexpected error (%T) from sendResponse: %v", st, st)) - } - } - return err - } - if channelz.IsOn() { - t.IncrMsgSent() - } - if trInfo != nil { - trInfo.tr.LazyLog(&payload{sent: true, msg: reply}, true) - } - // TODO: Should we be logging if writing status failed here, like above? - // Should the logging be in WriteStatus? Should we ignore the WriteStatus - // error or allow the stats handler to see it? - return t.WriteStatus(stream, status.New(codes.OK, "")) -} - -func (s *Server) processStreamingRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, sd *StreamDesc, trInfo *traceInfo) (err error) { - if channelz.IsOn() { - s.incrCallsStarted() - defer func() { - if err != nil && err != io.EOF { - s.incrCallsFailed() - } else { - s.incrCallsSucceeded() - } - }() - } - sh := s.opts.statsHandler - if sh != nil { - beginTime := time.Now() - begin := &stats.Begin{ - BeginTime: beginTime, - } - sh.HandleRPC(stream.Context(), begin) - defer func() { - end := &stats.End{ - BeginTime: beginTime, - EndTime: time.Now(), - } - if err != nil && err != io.EOF { - end.Error = toRPCErr(err) - } - sh.HandleRPC(stream.Context(), end) - }() - } - ctx := NewContextWithServerTransportStream(stream.Context(), stream) - ss := &serverStream{ - ctx: ctx, - t: t, - s: stream, - p: &parser{r: stream}, - codec: s.getCodec(stream.ContentSubtype()), - maxReceiveMessageSize: s.opts.maxReceiveMessageSize, - maxSendMessageSize: s.opts.maxSendMessageSize, - trInfo: trInfo, - statsHandler: sh, - } - - // If dc is set and matches the stream's compression, use it. Otherwise, try - // to find a matching registered compressor for decomp. - if rc := stream.RecvCompress(); s.opts.dc != nil && s.opts.dc.Type() == rc { - ss.dc = s.opts.dc - } else if rc != "" && rc != encoding.Identity { - ss.decomp = encoding.GetCompressor(rc) - if ss.decomp == nil { - st := status.Newf(codes.Unimplemented, "grpc: Decompressor is not installed for grpc-encoding %q", rc) - t.WriteStatus(ss.s, st) - return st.Err() - } - } - - // If cp is set, use it. Otherwise, attempt to compress the response using - // the incoming message compression method. - // - // NOTE: this needs to be ahead of all handling, https://github.com/grpc/grpc-go/issues/686. - if s.opts.cp != nil { - ss.cp = s.opts.cp - stream.SetSendCompress(s.opts.cp.Type()) - } else if rc := stream.RecvCompress(); rc != "" && rc != encoding.Identity { - // Legacy compressor not specified; attempt to respond with same encoding. - ss.comp = encoding.GetCompressor(rc) - if ss.comp != nil { - stream.SetSendCompress(rc) - } - } - - if trInfo != nil { - trInfo.tr.LazyLog(&trInfo.firstLine, false) - defer func() { - ss.mu.Lock() - if err != nil && err != io.EOF { - ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) - ss.trInfo.tr.SetError() - } - ss.trInfo.tr.Finish() - ss.trInfo.tr = nil - ss.mu.Unlock() - }() - } - var appErr error - var server interface{} - if srv != nil { - server = srv.server - } - if s.opts.streamInt == nil { - appErr = sd.Handler(server, ss) - } else { - info := &StreamServerInfo{ - FullMethod: stream.Method(), - IsClientStream: sd.ClientStreams, - IsServerStream: sd.ServerStreams, - } - appErr = s.opts.streamInt(server, ss, info, sd.Handler) - } - if appErr != nil { - appStatus, ok := status.FromError(appErr) - if !ok { - switch err := appErr.(type) { - case transport.StreamError: - appStatus = status.New(err.Code, err.Desc) - default: - appStatus = status.New(codes.Unknown, appErr.Error()) - } - appErr = appStatus.Err() - } - if trInfo != nil { - ss.mu.Lock() - ss.trInfo.tr.LazyLog(stringer(appStatus.Message()), true) - ss.trInfo.tr.SetError() - ss.mu.Unlock() - } - t.WriteStatus(ss.s, appStatus) - // TODO: Should we log an error from WriteStatus here and below? - return appErr - } - if trInfo != nil { - ss.mu.Lock() - ss.trInfo.tr.LazyLog(stringer("OK"), false) - ss.mu.Unlock() - } - return t.WriteStatus(ss.s, status.New(codes.OK, "")) -} - -func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream, trInfo *traceInfo) { - sm := stream.Method() - if sm != "" && sm[0] == '/' { - sm = sm[1:] - } - pos := strings.LastIndex(sm, "/") - if pos == -1 { - if trInfo != nil { - trInfo.tr.LazyLog(&fmtStringer{"Malformed method name %q", []interface{}{sm}}, true) - trInfo.tr.SetError() - } - errDesc := fmt.Sprintf("malformed method name: %q", stream.Method()) - if err := t.WriteStatus(stream, status.New(codes.ResourceExhausted, errDesc)); err != nil { - if trInfo != nil { - trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) - trInfo.tr.SetError() - } - grpclog.Warningf("grpc: Server.handleStream failed to write status: %v", err) - } - if trInfo != nil { - trInfo.tr.Finish() - } - return - } - service := sm[:pos] - method := sm[pos+1:] - srv, ok := s.m[service] - if !ok { - if unknownDesc := s.opts.unknownStreamDesc; unknownDesc != nil { - s.processStreamingRPC(t, stream, nil, unknownDesc, trInfo) - return - } - if trInfo != nil { - trInfo.tr.LazyLog(&fmtStringer{"Unknown service %v", []interface{}{service}}, true) - trInfo.tr.SetError() - } - errDesc := fmt.Sprintf("unknown service %v", service) - if err := t.WriteStatus(stream, status.New(codes.Unimplemented, errDesc)); err != nil { - if trInfo != nil { - trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) - trInfo.tr.SetError() - } - grpclog.Warningf("grpc: Server.handleStream failed to write status: %v", err) - } - if trInfo != nil { - trInfo.tr.Finish() - } - return - } - // Unary RPC or Streaming RPC? - if md, ok := srv.md[method]; ok { - s.processUnaryRPC(t, stream, srv, md, trInfo) - return - } - if sd, ok := srv.sd[method]; ok { - s.processStreamingRPC(t, stream, srv, sd, trInfo) - return - } - if trInfo != nil { - trInfo.tr.LazyLog(&fmtStringer{"Unknown method %v", []interface{}{method}}, true) - trInfo.tr.SetError() - } - if unknownDesc := s.opts.unknownStreamDesc; unknownDesc != nil { - s.processStreamingRPC(t, stream, nil, unknownDesc, trInfo) - return - } - errDesc := fmt.Sprintf("unknown method %v", method) - if err := t.WriteStatus(stream, status.New(codes.Unimplemented, errDesc)); err != nil { - if trInfo != nil { - trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) - trInfo.tr.SetError() - } - grpclog.Warningf("grpc: Server.handleStream failed to write status: %v", err) - } - if trInfo != nil { - trInfo.tr.Finish() - } -} - -// The key to save ServerTransportStream in the context. -type streamKey struct{} - -// NewContextWithServerTransportStream creates a new context from ctx and -// attaches stream to it. -// -// This API is EXPERIMENTAL. -func NewContextWithServerTransportStream(ctx context.Context, stream ServerTransportStream) context.Context { - return context.WithValue(ctx, streamKey{}, stream) -} - -// ServerTransportStream is a minimal interface that a transport stream must -// implement. This can be used to mock an actual transport stream for tests of -// handler code that use, for example, grpc.SetHeader (which requires some -// stream to be in context). -// -// See also NewContextWithServerTransportStream. -// -// This API is EXPERIMENTAL. -type ServerTransportStream interface { - Method() string - SetHeader(md metadata.MD) error - SendHeader(md metadata.MD) error - SetTrailer(md metadata.MD) error -} - -// ServerTransportStreamFromContext returns the ServerTransportStream saved in -// ctx. Returns nil if the given context has no stream associated with it -// (which implies it is not an RPC invocation context). -// -// This API is EXPERIMENTAL. -func ServerTransportStreamFromContext(ctx context.Context) ServerTransportStream { - s, _ := ctx.Value(streamKey{}).(ServerTransportStream) - return s -} - -// Stop stops the gRPC server. It immediately closes all open -// connections and listeners. -// It cancels all active RPCs on the server side and the corresponding -// pending RPCs on the client side will get notified by connection -// errors. -func (s *Server) Stop() { - s.quitOnce.Do(func() { - close(s.quit) - }) - - defer func() { - s.serveWG.Wait() - s.doneOnce.Do(func() { - close(s.done) - }) - }() - - s.channelzRemoveOnce.Do(func() { - if channelz.IsOn() { - channelz.RemoveEntry(s.channelzID) - } - }) - - s.mu.Lock() - listeners := s.lis - s.lis = nil - st := s.conns - s.conns = nil - // interrupt GracefulStop if Stop and GracefulStop are called concurrently. - s.cv.Broadcast() - s.mu.Unlock() - - for lis := range listeners { - lis.Close() - } - for c := range st { - c.Close() - } - - s.mu.Lock() - if s.events != nil { - s.events.Finish() - s.events = nil - } - s.mu.Unlock() -} - -// GracefulStop stops the gRPC server gracefully. It stops the server from -// accepting new connections and RPCs and blocks until all the pending RPCs are -// finished. -func (s *Server) GracefulStop() { - s.quitOnce.Do(func() { - close(s.quit) - }) - - defer func() { - s.doneOnce.Do(func() { - close(s.done) - }) - }() - - s.channelzRemoveOnce.Do(func() { - if channelz.IsOn() { - channelz.RemoveEntry(s.channelzID) - } - }) - s.mu.Lock() - if s.conns == nil { - s.mu.Unlock() - return - } - - for lis := range s.lis { - lis.Close() - } - s.lis = nil - if !s.drain { - for c := range s.conns { - c.(transport.ServerTransport).Drain() - } - s.drain = true - } - - // Wait for serving threads to be ready to exit. Only then can we be sure no - // new conns will be created. - s.mu.Unlock() - s.serveWG.Wait() - s.mu.Lock() - - for len(s.conns) != 0 { - s.cv.Wait() - } - s.conns = nil - if s.events != nil { - s.events.Finish() - s.events = nil - } - s.mu.Unlock() -} - -func init() { - internal.TestingUseHandlerImpl = func(arg interface{}) { - arg.(*Server).opts.useHandlerImpl = true - } -} - -// contentSubtype must be lowercase -// cannot return nil -func (s *Server) getCodec(contentSubtype string) baseCodec { - if s.opts.codec != nil { - return s.opts.codec - } - if contentSubtype == "" { - return encoding.GetCodec(proto.Name) - } - codec := encoding.GetCodec(contentSubtype) - if codec == nil { - return encoding.GetCodec(proto.Name) - } - return codec -} - -// SetHeader sets the header metadata. -// When called multiple times, all the provided metadata will be merged. -// All the metadata will be sent out when one of the following happens: -// - grpc.SendHeader() is called; -// - The first response is sent out; -// - An RPC status is sent out (error or success). -func SetHeader(ctx context.Context, md metadata.MD) error { - if md.Len() == 0 { - return nil - } - stream := ServerTransportStreamFromContext(ctx) - if stream == nil { - return status.Errorf(codes.Internal, "grpc: failed to fetch the stream from the context %v", ctx) - } - return stream.SetHeader(md) -} - -// SendHeader sends header metadata. It may be called at most once. -// The provided md and headers set by SetHeader() will be sent. -func SendHeader(ctx context.Context, md metadata.MD) error { - stream := ServerTransportStreamFromContext(ctx) - if stream == nil { - return status.Errorf(codes.Internal, "grpc: failed to fetch the stream from the context %v", ctx) - } - if err := stream.SendHeader(md); err != nil { - return toRPCErr(err) - } - return nil -} - -// SetTrailer sets the trailer metadata that will be sent when an RPC returns. -// When called more than once, all the provided metadata will be merged. -func SetTrailer(ctx context.Context, md metadata.MD) error { - if md.Len() == 0 { - return nil - } - stream := ServerTransportStreamFromContext(ctx) - if stream == nil { - return status.Errorf(codes.Internal, "grpc: failed to fetch the stream from the context %v", ctx) - } - return stream.SetTrailer(md) -} - -// Method returns the method string for the server context. The returned -// string is in the format of "/service/method". -func Method(ctx context.Context) (string, bool) { - s := ServerTransportStreamFromContext(ctx) - if s == nil { - return "", false - } - return s.Method(), true -} diff --git a/vendor/google.golang.org/grpc/service_config.go b/vendor/google.golang.org/grpc/service_config.go deleted file mode 100644 index 015631d8d38..00000000000 --- a/vendor/google.golang.org/grpc/service_config.go +++ /dev/null @@ -1,233 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "encoding/json" - "fmt" - "strconv" - "strings" - "time" - - "google.golang.org/grpc/grpclog" -) - -const maxInt = int(^uint(0) >> 1) - -// MethodConfig defines the configuration recommended by the service providers for a -// particular method. -// -// Deprecated: Users should not use this struct. Service config should be received -// through name resolver, as specified here -// https://github.com/grpc/grpc/blob/master/doc/service_config.md -type MethodConfig struct { - // WaitForReady indicates whether RPCs sent to this method should wait until - // the connection is ready by default (!failfast). The value specified via the - // gRPC client API will override the value set here. - WaitForReady *bool - // Timeout is the default timeout for RPCs sent to this method. The actual - // deadline used will be the minimum of the value specified here and the value - // set by the application via the gRPC client API. If either one is not set, - // then the other will be used. If neither is set, then the RPC has no deadline. - Timeout *time.Duration - // MaxReqSize is the maximum allowed payload size for an individual request in a - // stream (client->server) in bytes. The size which is measured is the serialized - // payload after per-message compression (but before stream compression) in bytes. - // The actual value used is the minimum of the value specified here and the value set - // by the application via the gRPC client API. If either one is not set, then the other - // will be used. If neither is set, then the built-in default is used. - MaxReqSize *int - // MaxRespSize is the maximum allowed payload size for an individual response in a - // stream (server->client) in bytes. - MaxRespSize *int -} - -// ServiceConfig is provided by the service provider and contains parameters for how -// clients that connect to the service should behave. -// -// Deprecated: Users should not use this struct. Service config should be received -// through name resolver, as specified here -// https://github.com/grpc/grpc/blob/master/doc/service_config.md -type ServiceConfig struct { - // LB is the load balancer the service providers recommends. The balancer specified - // via grpc.WithBalancer will override this. - LB *string - // Methods contains a map for the methods in this service. - // If there is an exact match for a method (i.e. /service/method) in the map, use the corresponding MethodConfig. - // If there's no exact match, look for the default config for the service (/service/) and use the corresponding MethodConfig if it exists. - // Otherwise, the method has no MethodConfig to use. - Methods map[string]MethodConfig - - stickinessMetadataKey *string -} - -func parseDuration(s *string) (*time.Duration, error) { - if s == nil { - return nil, nil - } - if !strings.HasSuffix(*s, "s") { - return nil, fmt.Errorf("malformed duration %q", *s) - } - ss := strings.SplitN((*s)[:len(*s)-1], ".", 3) - if len(ss) > 2 { - return nil, fmt.Errorf("malformed duration %q", *s) - } - // hasDigits is set if either the whole or fractional part of the number is - // present, since both are optional but one is required. - hasDigits := false - var d time.Duration - if len(ss[0]) > 0 { - i, err := strconv.ParseInt(ss[0], 10, 32) - if err != nil { - return nil, fmt.Errorf("malformed duration %q: %v", *s, err) - } - d = time.Duration(i) * time.Second - hasDigits = true - } - if len(ss) == 2 && len(ss[1]) > 0 { - if len(ss[1]) > 9 { - return nil, fmt.Errorf("malformed duration %q", *s) - } - f, err := strconv.ParseInt(ss[1], 10, 64) - if err != nil { - return nil, fmt.Errorf("malformed duration %q: %v", *s, err) - } - for i := 9; i > len(ss[1]); i-- { - f *= 10 - } - d += time.Duration(f) - hasDigits = true - } - if !hasDigits { - return nil, fmt.Errorf("malformed duration %q", *s) - } - - return &d, nil -} - -type jsonName struct { - Service *string - Method *string -} - -func (j jsonName) generatePath() (string, bool) { - if j.Service == nil { - return "", false - } - res := "/" + *j.Service + "/" - if j.Method != nil { - res += *j.Method - } - return res, true -} - -// TODO(lyuxuan): delete this struct after cleaning up old service config implementation. -type jsonMC struct { - Name *[]jsonName - WaitForReady *bool - Timeout *string - MaxRequestMessageBytes *int64 - MaxResponseMessageBytes *int64 -} - -// TODO(lyuxuan): delete this struct after cleaning up old service config implementation. -type jsonSC struct { - LoadBalancingPolicy *string - StickinessMetadataKey *string - MethodConfig *[]jsonMC -} - -func parseServiceConfig(js string) (ServiceConfig, error) { - var rsc jsonSC - err := json.Unmarshal([]byte(js), &rsc) - if err != nil { - grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err) - return ServiceConfig{}, err - } - sc := ServiceConfig{ - LB: rsc.LoadBalancingPolicy, - Methods: make(map[string]MethodConfig), - - stickinessMetadataKey: rsc.StickinessMetadataKey, - } - if rsc.MethodConfig == nil { - return sc, nil - } - - for _, m := range *rsc.MethodConfig { - if m.Name == nil { - continue - } - d, err := parseDuration(m.Timeout) - if err != nil { - grpclog.Warningf("grpc: parseServiceConfig error unmarshaling %s due to %v", js, err) - return ServiceConfig{}, err - } - - mc := MethodConfig{ - WaitForReady: m.WaitForReady, - Timeout: d, - } - if m.MaxRequestMessageBytes != nil { - if *m.MaxRequestMessageBytes > int64(maxInt) { - mc.MaxReqSize = newInt(maxInt) - } else { - mc.MaxReqSize = newInt(int(*m.MaxRequestMessageBytes)) - } - } - if m.MaxResponseMessageBytes != nil { - if *m.MaxResponseMessageBytes > int64(maxInt) { - mc.MaxRespSize = newInt(maxInt) - } else { - mc.MaxRespSize = newInt(int(*m.MaxResponseMessageBytes)) - } - } - for _, n := range *m.Name { - if path, valid := n.generatePath(); valid { - sc.Methods[path] = mc - } - } - } - - return sc, nil -} - -func min(a, b *int) *int { - if *a < *b { - return a - } - return b -} - -func getMaxSize(mcMax, doptMax *int, defaultVal int) *int { - if mcMax == nil && doptMax == nil { - return &defaultVal - } - if mcMax != nil && doptMax != nil { - return min(mcMax, doptMax) - } - if mcMax != nil { - return mcMax - } - return doptMax -} - -func newInt(b int) *int { - return &b -} diff --git a/vendor/google.golang.org/grpc/stats/handlers.go b/vendor/google.golang.org/grpc/stats/handlers.go deleted file mode 100644 index 05b384c6931..00000000000 --- a/vendor/google.golang.org/grpc/stats/handlers.go +++ /dev/null @@ -1,64 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package stats - -import ( - "net" - - "golang.org/x/net/context" -) - -// ConnTagInfo defines the relevant information needed by connection context tagger. -type ConnTagInfo struct { - // RemoteAddr is the remote address of the corresponding connection. - RemoteAddr net.Addr - // LocalAddr is the local address of the corresponding connection. - LocalAddr net.Addr -} - -// RPCTagInfo defines the relevant information needed by RPC context tagger. -type RPCTagInfo struct { - // FullMethodName is the RPC method in the format of /package.service/method. - FullMethodName string - // FailFast indicates if this RPC is failfast. - // This field is only valid on client side, it's always false on server side. - FailFast bool -} - -// Handler defines the interface for the related stats handling (e.g., RPCs, connections). -type Handler interface { - // TagRPC can attach some information to the given context. - // The context used for the rest lifetime of the RPC will be derived from - // the returned context. - TagRPC(context.Context, *RPCTagInfo) context.Context - // HandleRPC processes the RPC stats. - HandleRPC(context.Context, RPCStats) - - // TagConn can attach some information to the given context. - // The returned context will be used for stats handling. - // For conn stats handling, the context used in HandleConn for this - // connection will be derived from the context returned. - // For RPC stats handling, - // - On server side, the context used in HandleRPC for all RPCs on this - // connection will be derived from the context returned. - // - On client side, the context is not derived from the context returned. - TagConn(context.Context, *ConnTagInfo) context.Context - // HandleConn processes the Conn stats. - HandleConn(context.Context, ConnStats) -} diff --git a/vendor/google.golang.org/grpc/stats/stats.go b/vendor/google.golang.org/grpc/stats/stats.go deleted file mode 100644 index 3f13190a0ac..00000000000 --- a/vendor/google.golang.org/grpc/stats/stats.go +++ /dev/null @@ -1,296 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -//go:generate protoc --go_out=plugins=grpc:. grpc_testing/test.proto - -// Package stats is for collecting and reporting various network and RPC stats. -// This package is for monitoring purpose only. All fields are read-only. -// All APIs are experimental. -package stats // import "google.golang.org/grpc/stats" - -import ( - "net" - "time" - - "golang.org/x/net/context" -) - -// RPCStats contains stats information about RPCs. -type RPCStats interface { - isRPCStats() - // IsClient returns true if this RPCStats is from client side. - IsClient() bool -} - -// Begin contains stats when an RPC begins. -// FailFast is only valid if this Begin is from client side. -type Begin struct { - // Client is true if this Begin is from client side. - Client bool - // BeginTime is the time when the RPC begins. - BeginTime time.Time - // FailFast indicates if this RPC is failfast. - FailFast bool -} - -// IsClient indicates if the stats information is from client side. -func (s *Begin) IsClient() bool { return s.Client } - -func (s *Begin) isRPCStats() {} - -// InPayload contains the information for an incoming payload. -type InPayload struct { - // Client is true if this InPayload is from client side. - Client bool - // Payload is the payload with original type. - Payload interface{} - // Data is the serialized message payload. - Data []byte - // Length is the length of uncompressed data. - Length int - // WireLength is the length of data on wire (compressed, signed, encrypted). - WireLength int - // RecvTime is the time when the payload is received. - RecvTime time.Time -} - -// IsClient indicates if the stats information is from client side. -func (s *InPayload) IsClient() bool { return s.Client } - -func (s *InPayload) isRPCStats() {} - -// InHeader contains stats when a header is received. -type InHeader struct { - // Client is true if this InHeader is from client side. - Client bool - // WireLength is the wire length of header. - WireLength int - - // The following fields are valid only if Client is false. - // FullMethod is the full RPC method string, i.e., /package.service/method. - FullMethod string - // RemoteAddr is the remote address of the corresponding connection. - RemoteAddr net.Addr - // LocalAddr is the local address of the corresponding connection. - LocalAddr net.Addr - // Compression is the compression algorithm used for the RPC. - Compression string -} - -// IsClient indicates if the stats information is from client side. -func (s *InHeader) IsClient() bool { return s.Client } - -func (s *InHeader) isRPCStats() {} - -// InTrailer contains stats when a trailer is received. -type InTrailer struct { - // Client is true if this InTrailer is from client side. - Client bool - // WireLength is the wire length of trailer. - WireLength int -} - -// IsClient indicates if the stats information is from client side. -func (s *InTrailer) IsClient() bool { return s.Client } - -func (s *InTrailer) isRPCStats() {} - -// OutPayload contains the information for an outgoing payload. -type OutPayload struct { - // Client is true if this OutPayload is from client side. - Client bool - // Payload is the payload with original type. - Payload interface{} - // Data is the serialized message payload. - Data []byte - // Length is the length of uncompressed data. - Length int - // WireLength is the length of data on wire (compressed, signed, encrypted). - WireLength int - // SentTime is the time when the payload is sent. - SentTime time.Time -} - -// IsClient indicates if this stats information is from client side. -func (s *OutPayload) IsClient() bool { return s.Client } - -func (s *OutPayload) isRPCStats() {} - -// OutHeader contains stats when a header is sent. -type OutHeader struct { - // Client is true if this OutHeader is from client side. - Client bool - - // The following fields are valid only if Client is true. - // FullMethod is the full RPC method string, i.e., /package.service/method. - FullMethod string - // RemoteAddr is the remote address of the corresponding connection. - RemoteAddr net.Addr - // LocalAddr is the local address of the corresponding connection. - LocalAddr net.Addr - // Compression is the compression algorithm used for the RPC. - Compression string -} - -// IsClient indicates if this stats information is from client side. -func (s *OutHeader) IsClient() bool { return s.Client } - -func (s *OutHeader) isRPCStats() {} - -// OutTrailer contains stats when a trailer is sent. -type OutTrailer struct { - // Client is true if this OutTrailer is from client side. - Client bool - // WireLength is the wire length of trailer. - WireLength int -} - -// IsClient indicates if this stats information is from client side. -func (s *OutTrailer) IsClient() bool { return s.Client } - -func (s *OutTrailer) isRPCStats() {} - -// End contains stats when an RPC ends. -type End struct { - // Client is true if this End is from client side. - Client bool - // BeginTime is the time when the RPC began. - BeginTime time.Time - // EndTime is the time when the RPC ends. - EndTime time.Time - // Error is the error the RPC ended with. It is an error generated from - // status.Status and can be converted back to status.Status using - // status.FromError if non-nil. - Error error -} - -// IsClient indicates if this is from client side. -func (s *End) IsClient() bool { return s.Client } - -func (s *End) isRPCStats() {} - -// ConnStats contains stats information about connections. -type ConnStats interface { - isConnStats() - // IsClient returns true if this ConnStats is from client side. - IsClient() bool -} - -// ConnBegin contains the stats of a connection when it is established. -type ConnBegin struct { - // Client is true if this ConnBegin is from client side. - Client bool -} - -// IsClient indicates if this is from client side. -func (s *ConnBegin) IsClient() bool { return s.Client } - -func (s *ConnBegin) isConnStats() {} - -// ConnEnd contains the stats of a connection when it ends. -type ConnEnd struct { - // Client is true if this ConnEnd is from client side. - Client bool -} - -// IsClient indicates if this is from client side. -func (s *ConnEnd) IsClient() bool { return s.Client } - -func (s *ConnEnd) isConnStats() {} - -type incomingTagsKey struct{} -type outgoingTagsKey struct{} - -// SetTags attaches stats tagging data to the context, which will be sent in -// the outgoing RPC with the header grpc-tags-bin. Subsequent calls to -// SetTags will overwrite the values from earlier calls. -// -// NOTE: this is provided only for backward compatibility with existing clients -// and will likely be removed in an upcoming release. New uses should transmit -// this type of data using metadata with a different, non-reserved (i.e. does -// not begin with "grpc-") header name. -func SetTags(ctx context.Context, b []byte) context.Context { - return context.WithValue(ctx, outgoingTagsKey{}, b) -} - -// Tags returns the tags from the context for the inbound RPC. -// -// NOTE: this is provided only for backward compatibility with existing clients -// and will likely be removed in an upcoming release. New uses should transmit -// this type of data using metadata with a different, non-reserved (i.e. does -// not begin with "grpc-") header name. -func Tags(ctx context.Context) []byte { - b, _ := ctx.Value(incomingTagsKey{}).([]byte) - return b -} - -// SetIncomingTags attaches stats tagging data to the context, to be read by -// the application (not sent in outgoing RPCs). -// -// This is intended for gRPC-internal use ONLY. -func SetIncomingTags(ctx context.Context, b []byte) context.Context { - return context.WithValue(ctx, incomingTagsKey{}, b) -} - -// OutgoingTags returns the tags from the context for the outbound RPC. -// -// This is intended for gRPC-internal use ONLY. -func OutgoingTags(ctx context.Context) []byte { - b, _ := ctx.Value(outgoingTagsKey{}).([]byte) - return b -} - -type incomingTraceKey struct{} -type outgoingTraceKey struct{} - -// SetTrace attaches stats tagging data to the context, which will be sent in -// the outgoing RPC with the header grpc-trace-bin. Subsequent calls to -// SetTrace will overwrite the values from earlier calls. -// -// NOTE: this is provided only for backward compatibility with existing clients -// and will likely be removed in an upcoming release. New uses should transmit -// this type of data using metadata with a different, non-reserved (i.e. does -// not begin with "grpc-") header name. -func SetTrace(ctx context.Context, b []byte) context.Context { - return context.WithValue(ctx, outgoingTraceKey{}, b) -} - -// Trace returns the trace from the context for the inbound RPC. -// -// NOTE: this is provided only for backward compatibility with existing clients -// and will likely be removed in an upcoming release. New uses should transmit -// this type of data using metadata with a different, non-reserved (i.e. does -// not begin with "grpc-") header name. -func Trace(ctx context.Context) []byte { - b, _ := ctx.Value(incomingTraceKey{}).([]byte) - return b -} - -// SetIncomingTrace attaches stats tagging data to the context, to be read by -// the application (not sent in outgoing RPCs). It is intended for -// gRPC-internal use. -func SetIncomingTrace(ctx context.Context, b []byte) context.Context { - return context.WithValue(ctx, incomingTraceKey{}, b) -} - -// OutgoingTrace returns the trace from the context for the outbound RPC. It is -// intended for gRPC-internal use. -func OutgoingTrace(ctx context.Context) []byte { - b, _ := ctx.Value(outgoingTraceKey{}).([]byte) - return b -} diff --git a/vendor/google.golang.org/grpc/status/status.go b/vendor/google.golang.org/grpc/status/status.go deleted file mode 100644 index 9c61b094508..00000000000 --- a/vendor/google.golang.org/grpc/status/status.go +++ /dev/null @@ -1,189 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package status implements errors returned by gRPC. These errors are -// serialized and transmitted on the wire between server and client, and allow -// for additional data to be transmitted via the Details field in the status -// proto. gRPC service handlers should return an error created by this -// package, and gRPC clients should expect a corresponding error to be -// returned from the RPC call. -// -// This package upholds the invariants that a non-nil error may not -// contain an OK code, and an OK code must result in a nil error. -package status - -import ( - "errors" - "fmt" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/ptypes" - spb "google.golang.org/genproto/googleapis/rpc/status" - "google.golang.org/grpc/codes" -) - -// statusError is an alias of a status proto. It implements error and Status, -// and a nil statusError should never be returned by this package. -type statusError spb.Status - -func (se *statusError) Error() string { - p := (*spb.Status)(se) - return fmt.Sprintf("rpc error: code = %s desc = %s", codes.Code(p.GetCode()), p.GetMessage()) -} - -func (se *statusError) GRPCStatus() *Status { - return &Status{s: (*spb.Status)(se)} -} - -// Status represents an RPC status code, message, and details. It is immutable -// and should be created with New, Newf, or FromProto. -type Status struct { - s *spb.Status -} - -// Code returns the status code contained in s. -func (s *Status) Code() codes.Code { - if s == nil || s.s == nil { - return codes.OK - } - return codes.Code(s.s.Code) -} - -// Message returns the message contained in s. -func (s *Status) Message() string { - if s == nil || s.s == nil { - return "" - } - return s.s.Message -} - -// Proto returns s's status as an spb.Status proto message. -func (s *Status) Proto() *spb.Status { - if s == nil { - return nil - } - return proto.Clone(s.s).(*spb.Status) -} - -// Err returns an immutable error representing s; returns nil if s.Code() is -// OK. -func (s *Status) Err() error { - if s.Code() == codes.OK { - return nil - } - return (*statusError)(s.s) -} - -// New returns a Status representing c and msg. -func New(c codes.Code, msg string) *Status { - return &Status{s: &spb.Status{Code: int32(c), Message: msg}} -} - -// Newf returns New(c, fmt.Sprintf(format, a...)). -func Newf(c codes.Code, format string, a ...interface{}) *Status { - return New(c, fmt.Sprintf(format, a...)) -} - -// Error returns an error representing c and msg. If c is OK, returns nil. -func Error(c codes.Code, msg string) error { - return New(c, msg).Err() -} - -// Errorf returns Error(c, fmt.Sprintf(format, a...)). -func Errorf(c codes.Code, format string, a ...interface{}) error { - return Error(c, fmt.Sprintf(format, a...)) -} - -// ErrorProto returns an error representing s. If s.Code is OK, returns nil. -func ErrorProto(s *spb.Status) error { - return FromProto(s).Err() -} - -// FromProto returns a Status representing s. -func FromProto(s *spb.Status) *Status { - return &Status{s: proto.Clone(s).(*spb.Status)} -} - -// FromError returns a Status representing err if it was produced from this -// package or has a method `GRPCStatus() *Status`. Otherwise, ok is false and a -// Status is returned with codes.Unknown and the original error message. -func FromError(err error) (s *Status, ok bool) { - if err == nil { - return &Status{s: &spb.Status{Code: int32(codes.OK)}}, true - } - if se, ok := err.(interface{ GRPCStatus() *Status }); ok { - return se.GRPCStatus(), true - } - return New(codes.Unknown, err.Error()), false -} - -// Convert is a convenience function which removes the need to handle the -// boolean return value from FromError. -func Convert(err error) *Status { - s, _ := FromError(err) - return s -} - -// WithDetails returns a new status with the provided details messages appended to the status. -// If any errors are encountered, it returns nil and the first error encountered. -func (s *Status) WithDetails(details ...proto.Message) (*Status, error) { - if s.Code() == codes.OK { - return nil, errors.New("no error details for status with code OK") - } - // s.Code() != OK implies that s.Proto() != nil. - p := s.Proto() - for _, detail := range details { - any, err := ptypes.MarshalAny(detail) - if err != nil { - return nil, err - } - p.Details = append(p.Details, any) - } - return &Status{s: p}, nil -} - -// Details returns a slice of details messages attached to the status. -// If a detail cannot be decoded, the error is returned in place of the detail. -func (s *Status) Details() []interface{} { - if s == nil || s.s == nil { - return nil - } - details := make([]interface{}, 0, len(s.s.Details)) - for _, any := range s.s.Details { - detail := &ptypes.DynamicAny{} - if err := ptypes.UnmarshalAny(any, detail); err != nil { - details = append(details, err) - continue - } - details = append(details, detail.Message) - } - return details -} - -// Code returns the Code of the error if it is a Status error, codes.OK if err -// is nil, or codes.Unknown otherwise. -func Code(err error) codes.Code { - // Don't use FromError to avoid allocation of OK status. - if err == nil { - return codes.OK - } - if se, ok := err.(interface{ GRPCStatus() *Status }); ok { - return se.GRPCStatus().Code() - } - return codes.Unknown -} diff --git a/vendor/google.golang.org/grpc/stream.go b/vendor/google.golang.org/grpc/stream.go deleted file mode 100644 index 82921a15a3a..00000000000 --- a/vendor/google.golang.org/grpc/stream.go +++ /dev/null @@ -1,765 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "errors" - "io" - "sync" - "time" - - "golang.org/x/net/context" - "golang.org/x/net/trace" - "google.golang.org/grpc/balancer" - "google.golang.org/grpc/channelz" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/encoding" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/stats" - "google.golang.org/grpc/status" - "google.golang.org/grpc/transport" -) - -// StreamHandler defines the handler called by gRPC server to complete the -// execution of a streaming RPC. If a StreamHandler returns an error, it -// should be produced by the status package, or else gRPC will use -// codes.Unknown as the status code and err.Error() as the status message -// of the RPC. -type StreamHandler func(srv interface{}, stream ServerStream) error - -// StreamDesc represents a streaming RPC service's method specification. -type StreamDesc struct { - StreamName string - Handler StreamHandler - - // At least one of these is true. - ServerStreams bool - ClientStreams bool -} - -// Stream defines the common interface a client or server stream has to satisfy. -// -// All errors returned from Stream are compatible with the status package. -type Stream interface { - // Context returns the context for this stream. - Context() context.Context - // SendMsg blocks until it sends m, the stream is done or the stream - // breaks. - // On error, it aborts the stream and returns an RPC status on client - // side. On server side, it simply returns the error to the caller. - // SendMsg is called by generated code. Also Users can call SendMsg - // directly when it is really needed in their use cases. - // It's safe to have a goroutine calling SendMsg and another goroutine calling - // recvMsg on the same stream at the same time. - // But it is not safe to call SendMsg on the same stream in different goroutines. - SendMsg(m interface{}) error - // RecvMsg blocks until it receives a message or the stream is - // done. On client side, it returns io.EOF when the stream is done. On - // any other error, it aborts the stream and returns an RPC status. On - // server side, it simply returns the error to the caller. - // It's safe to have a goroutine calling SendMsg and another goroutine calling - // recvMsg on the same stream at the same time. - // But it is not safe to call RecvMsg on the same stream in different goroutines. - RecvMsg(m interface{}) error -} - -// ClientStream defines the interface a client stream has to satisfy. -type ClientStream interface { - // Header returns the header metadata received from the server if there - // is any. It blocks if the metadata is not ready to read. - Header() (metadata.MD, error) - // Trailer returns the trailer metadata from the server, if there is any. - // It must only be called after stream.CloseAndRecv has returned, or - // stream.Recv has returned a non-nil error (including io.EOF). - Trailer() metadata.MD - // CloseSend closes the send direction of the stream. It closes the stream - // when non-nil error is met. - CloseSend() error - // Stream.SendMsg() may return a non-nil error when something wrong happens sending - // the request. The returned error indicates the status of this sending, not the final - // status of the RPC. - // - // Always call Stream.RecvMsg() to drain the stream and get the final - // status, otherwise there could be leaked resources. - Stream -} - -// NewStream creates a new Stream for the client side. This is typically -// called by generated code. -func (cc *ClientConn) NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error) { - // allow interceptor to see all applicable call options, which means those - // configured as defaults from dial option as well as per-call options - opts = combine(cc.dopts.callOptions, opts) - - if cc.dopts.streamInt != nil { - return cc.dopts.streamInt(ctx, desc, cc, method, newClientStream, opts...) - } - return newClientStream(ctx, desc, cc, method, opts...) -} - -// NewClientStream creates a new Stream for the client side. This is typically -// called by generated code. -// -// DEPRECATED: Use ClientConn.NewStream instead. -func NewClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (ClientStream, error) { - return cc.NewStream(ctx, desc, method, opts...) -} - -func newClientStream(ctx context.Context, desc *StreamDesc, cc *ClientConn, method string, opts ...CallOption) (_ ClientStream, err error) { - if channelz.IsOn() { - cc.incrCallsStarted() - defer func() { - if err != nil { - cc.incrCallsFailed() - } - }() - } - c := defaultCallInfo() - mc := cc.GetMethodConfig(method) - if mc.WaitForReady != nil { - c.failFast = !*mc.WaitForReady - } - - // Possible context leak: - // The cancel function for the child context we create will only be called - // when RecvMsg returns a non-nil error, if the ClientConn is closed, or if - // an error is generated by SendMsg. - // https://github.com/grpc/grpc-go/issues/1818. - var cancel context.CancelFunc - if mc.Timeout != nil && *mc.Timeout >= 0 { - ctx, cancel = context.WithTimeout(ctx, *mc.Timeout) - } else { - ctx, cancel = context.WithCancel(ctx) - } - defer func() { - if err != nil { - cancel() - } - }() - - for _, o := range opts { - if err := o.before(c); err != nil { - return nil, toRPCErr(err) - } - } - c.maxSendMessageSize = getMaxSize(mc.MaxReqSize, c.maxSendMessageSize, defaultClientMaxSendMessageSize) - c.maxReceiveMessageSize = getMaxSize(mc.MaxRespSize, c.maxReceiveMessageSize, defaultClientMaxReceiveMessageSize) - if err := setCallInfoCodec(c); err != nil { - return nil, err - } - - callHdr := &transport.CallHdr{ - Host: cc.authority, - Method: method, - // If it's not client streaming, we should already have the request to be sent, - // so we don't flush the header. - // If it's client streaming, the user may never send a request or send it any - // time soon, so we ask the transport to flush the header. - Flush: desc.ClientStreams, - ContentSubtype: c.contentSubtype, - } - - // Set our outgoing compression according to the UseCompressor CallOption, if - // set. In that case, also find the compressor from the encoding package. - // Otherwise, use the compressor configured by the WithCompressor DialOption, - // if set. - var cp Compressor - var comp encoding.Compressor - if ct := c.compressorType; ct != "" { - callHdr.SendCompress = ct - if ct != encoding.Identity { - comp = encoding.GetCompressor(ct) - if comp == nil { - return nil, status.Errorf(codes.Internal, "grpc: Compressor is not installed for requested grpc-encoding %q", ct) - } - } - } else if cc.dopts.cp != nil { - callHdr.SendCompress = cc.dopts.cp.Type() - cp = cc.dopts.cp - } - if c.creds != nil { - callHdr.Creds = c.creds - } - var trInfo traceInfo - if EnableTracing { - trInfo.tr = trace.New("grpc.Sent."+methodFamily(method), method) - trInfo.firstLine.client = true - if deadline, ok := ctx.Deadline(); ok { - trInfo.firstLine.deadline = deadline.Sub(time.Now()) - } - trInfo.tr.LazyLog(&trInfo.firstLine, false) - ctx = trace.NewContext(ctx, trInfo.tr) - defer func() { - if err != nil { - // Need to call tr.finish() if error is returned. - // Because tr will not be returned to caller. - trInfo.tr.LazyPrintf("RPC: [%v]", err) - trInfo.tr.SetError() - trInfo.tr.Finish() - } - }() - } - ctx = newContextWithRPCInfo(ctx, c.failFast) - sh := cc.dopts.copts.StatsHandler - var beginTime time.Time - if sh != nil { - ctx = sh.TagRPC(ctx, &stats.RPCTagInfo{FullMethodName: method, FailFast: c.failFast}) - beginTime = time.Now() - begin := &stats.Begin{ - Client: true, - BeginTime: beginTime, - FailFast: c.failFast, - } - sh.HandleRPC(ctx, begin) - defer func() { - if err != nil { - // Only handle end stats if err != nil. - end := &stats.End{ - Client: true, - Error: err, - BeginTime: beginTime, - EndTime: time.Now(), - } - sh.HandleRPC(ctx, end) - } - }() - } - - var ( - t transport.ClientTransport - s *transport.Stream - done func(balancer.DoneInfo) - ) - for { - // Check to make sure the context has expired. This will prevent us from - // looping forever if an error occurs for wait-for-ready RPCs where no data - // is sent on the wire. - select { - case <-ctx.Done(): - return nil, toRPCErr(ctx.Err()) - default: - } - - t, done, err = cc.getTransport(ctx, c.failFast) - if err != nil { - return nil, err - } - - s, err = t.NewStream(ctx, callHdr) - if err != nil { - if done != nil { - done(balancer.DoneInfo{Err: err}) - done = nil - } - // In the event of any error from NewStream, we never attempted to write - // anything to the wire, so we can retry indefinitely for non-fail-fast - // RPCs. - if !c.failFast { - continue - } - return nil, toRPCErr(err) - } - break - } - - cs := &clientStream{ - opts: opts, - c: c, - cc: cc, - desc: desc, - codec: c.codec, - cp: cp, - comp: comp, - cancel: cancel, - attempt: &csAttempt{ - t: t, - s: s, - p: &parser{r: s}, - done: done, - dc: cc.dopts.dc, - ctx: ctx, - trInfo: trInfo, - statsHandler: sh, - beginTime: beginTime, - }, - } - cs.c.stream = cs - cs.attempt.cs = cs - if desc != unaryStreamDesc { - // Listen on cc and stream contexts to cleanup when the user closes the - // ClientConn or cancels the stream context. In all other cases, an error - // should already be injected into the recv buffer by the transport, which - // the client will eventually receive, and then we will cancel the stream's - // context in clientStream.finish. - go func() { - select { - case <-cc.ctx.Done(): - cs.finish(ErrClientConnClosing) - case <-ctx.Done(): - cs.finish(toRPCErr(ctx.Err())) - } - }() - } - return cs, nil -} - -// clientStream implements a client side Stream. -type clientStream struct { - opts []CallOption - c *callInfo - cc *ClientConn - desc *StreamDesc - - codec baseCodec - cp Compressor - comp encoding.Compressor - - cancel context.CancelFunc // cancels all attempts - - sentLast bool // sent an end stream - - mu sync.Mutex // guards finished - finished bool // TODO: replace with atomic cmpxchg or sync.Once? - - attempt *csAttempt // the active client stream attempt - // TODO(hedging): hedging will have multiple attempts simultaneously. -} - -// csAttempt implements a single transport stream attempt within a -// clientStream. -type csAttempt struct { - cs *clientStream - t transport.ClientTransport - s *transport.Stream - p *parser - done func(balancer.DoneInfo) - - dc Decompressor - decomp encoding.Compressor - decompSet bool - - ctx context.Context // the application's context, wrapped by stats/tracing - - mu sync.Mutex // guards trInfo.tr - // trInfo.tr is set when created (if EnableTracing is true), - // and cleared when the finish method is called. - trInfo traceInfo - - statsHandler stats.Handler - beginTime time.Time -} - -func (cs *clientStream) Context() context.Context { - // TODO(retry): commit the current attempt (the context has peer-aware data). - return cs.attempt.context() -} - -func (cs *clientStream) Header() (metadata.MD, error) { - m, err := cs.attempt.header() - if err != nil { - // TODO(retry): maybe retry on error or commit attempt on success. - err = toRPCErr(err) - cs.finish(err) - } - return m, err -} - -func (cs *clientStream) Trailer() metadata.MD { - // TODO(retry): on error, maybe retry (trailers-only). - return cs.attempt.trailer() -} - -func (cs *clientStream) SendMsg(m interface{}) (err error) { - // TODO(retry): buffer message for replaying if not committed. - return cs.attempt.sendMsg(m) -} - -func (cs *clientStream) RecvMsg(m interface{}) (err error) { - // TODO(retry): maybe retry on error or commit attempt on success. - return cs.attempt.recvMsg(m) -} - -func (cs *clientStream) CloseSend() error { - cs.attempt.closeSend() - return nil -} - -func (cs *clientStream) finish(err error) { - if err == io.EOF { - // Ending a stream with EOF indicates a success. - err = nil - } - cs.mu.Lock() - if cs.finished { - cs.mu.Unlock() - return - } - cs.finished = true - cs.mu.Unlock() - if channelz.IsOn() { - if err != nil { - cs.cc.incrCallsFailed() - } else { - cs.cc.incrCallsSucceeded() - } - } - // TODO(retry): commit current attempt if necessary. - cs.attempt.finish(err) - for _, o := range cs.opts { - o.after(cs.c) - } - cs.cancel() -} - -func (a *csAttempt) context() context.Context { - return a.s.Context() -} - -func (a *csAttempt) header() (metadata.MD, error) { - return a.s.Header() -} - -func (a *csAttempt) trailer() metadata.MD { - return a.s.Trailer() -} - -func (a *csAttempt) sendMsg(m interface{}) (err error) { - // TODO Investigate how to signal the stats handling party. - // generate error stats if err != nil && err != io.EOF? - cs := a.cs - defer func() { - // For non-client-streaming RPCs, we return nil instead of EOF on success - // because the generated code requires it. finish is not called; RecvMsg() - // will call it with the stream's status independently. - if err == io.EOF && !cs.desc.ClientStreams { - err = nil - } - if err != nil && err != io.EOF { - // Call finish on the client stream for errors generated by this SendMsg - // call, as these indicate problems created by this client. (Transport - // errors are converted to an io.EOF error below; the real error will be - // returned from RecvMsg eventually in that case, or be retried.) - cs.finish(err) - } - }() - // TODO: Check cs.sentLast and error if we already ended the stream. - if EnableTracing { - a.mu.Lock() - if a.trInfo.tr != nil { - a.trInfo.tr.LazyLog(&payload{sent: true, msg: m}, true) - } - a.mu.Unlock() - } - var outPayload *stats.OutPayload - if a.statsHandler != nil { - outPayload = &stats.OutPayload{ - Client: true, - } - } - hdr, data, err := encode(cs.codec, m, cs.cp, outPayload, cs.comp) - if err != nil { - return err - } - if len(data) > *cs.c.maxSendMessageSize { - return status.Errorf(codes.ResourceExhausted, "trying to send message larger than max (%d vs. %d)", len(data), *cs.c.maxSendMessageSize) - } - if !cs.desc.ClientStreams { - cs.sentLast = true - } - err = a.t.Write(a.s, hdr, data, &transport.Options{Last: !cs.desc.ClientStreams}) - if err == nil { - if outPayload != nil { - outPayload.SentTime = time.Now() - a.statsHandler.HandleRPC(a.ctx, outPayload) - } - if channelz.IsOn() { - a.t.IncrMsgSent() - } - return nil - } - return io.EOF -} - -func (a *csAttempt) recvMsg(m interface{}) (err error) { - cs := a.cs - defer func() { - if err != nil || !cs.desc.ServerStreams { - // err != nil or non-server-streaming indicates end of stream. - cs.finish(err) - } - }() - var inPayload *stats.InPayload - if a.statsHandler != nil { - inPayload = &stats.InPayload{ - Client: true, - } - } - if !a.decompSet { - // Block until we receive headers containing received message encoding. - if ct := a.s.RecvCompress(); ct != "" && ct != encoding.Identity { - if a.dc == nil || a.dc.Type() != ct { - // No configured decompressor, or it does not match the incoming - // message encoding; attempt to find a registered compressor that does. - a.dc = nil - a.decomp = encoding.GetCompressor(ct) - } - } else { - // No compression is used; disable our decompressor. - a.dc = nil - } - // Only initialize this state once per stream. - a.decompSet = true - } - err = recv(a.p, cs.codec, a.s, a.dc, m, *cs.c.maxReceiveMessageSize, inPayload, a.decomp) - if err != nil { - if err == io.EOF { - if statusErr := a.s.Status().Err(); statusErr != nil { - return statusErr - } - return io.EOF // indicates successful end of stream. - } - return toRPCErr(err) - } - if EnableTracing { - a.mu.Lock() - if a.trInfo.tr != nil { - a.trInfo.tr.LazyLog(&payload{sent: false, msg: m}, true) - } - a.mu.Unlock() - } - if inPayload != nil { - a.statsHandler.HandleRPC(a.ctx, inPayload) - } - if channelz.IsOn() { - a.t.IncrMsgRecv() - } - if cs.desc.ServerStreams { - // Subsequent messages should be received by subsequent RecvMsg calls. - return nil - } - - // Special handling for non-server-stream rpcs. - // This recv expects EOF or errors, so we don't collect inPayload. - err = recv(a.p, cs.codec, a.s, a.dc, m, *cs.c.maxReceiveMessageSize, nil, a.decomp) - if err == nil { - return toRPCErr(errors.New("grpc: client streaming protocol violation: get , want ")) - } - if err == io.EOF { - return a.s.Status().Err() // non-server streaming Recv returns nil on success - } - return toRPCErr(err) -} - -func (a *csAttempt) closeSend() { - cs := a.cs - if cs.sentLast { - return - } - cs.sentLast = true - cs.attempt.t.Write(cs.attempt.s, nil, nil, &transport.Options{Last: true}) - // We ignore errors from Write. Any error it would return would also be - // returned by a subsequent RecvMsg call, and the user is supposed to always - // finish the stream by calling RecvMsg until it returns err != nil. -} - -func (a *csAttempt) finish(err error) { - a.mu.Lock() - a.t.CloseStream(a.s, err) - - if a.done != nil { - a.done(balancer.DoneInfo{ - Err: err, - BytesSent: true, - BytesReceived: a.s.BytesReceived(), - }) - } - if a.statsHandler != nil { - end := &stats.End{ - Client: true, - BeginTime: a.beginTime, - EndTime: time.Now(), - Error: err, - } - a.statsHandler.HandleRPC(a.ctx, end) - } - if a.trInfo.tr != nil { - if err == nil { - a.trInfo.tr.LazyPrintf("RPC: [OK]") - } else { - a.trInfo.tr.LazyPrintf("RPC: [%v]", err) - a.trInfo.tr.SetError() - } - a.trInfo.tr.Finish() - a.trInfo.tr = nil - } - a.mu.Unlock() -} - -// ServerStream defines the interface a server stream has to satisfy. -type ServerStream interface { - // SetHeader sets the header metadata. It may be called multiple times. - // When call multiple times, all the provided metadata will be merged. - // All the metadata will be sent out when one of the following happens: - // - ServerStream.SendHeader() is called; - // - The first response is sent out; - // - An RPC status is sent out (error or success). - SetHeader(metadata.MD) error - // SendHeader sends the header metadata. - // The provided md and headers set by SetHeader() will be sent. - // It fails if called multiple times. - SendHeader(metadata.MD) error - // SetTrailer sets the trailer metadata which will be sent with the RPC status. - // When called more than once, all the provided metadata will be merged. - SetTrailer(metadata.MD) - Stream -} - -// serverStream implements a server side Stream. -type serverStream struct { - ctx context.Context - t transport.ServerTransport - s *transport.Stream - p *parser - codec baseCodec - - cp Compressor - dc Decompressor - comp encoding.Compressor - decomp encoding.Compressor - - maxReceiveMessageSize int - maxSendMessageSize int - trInfo *traceInfo - - statsHandler stats.Handler - - mu sync.Mutex // protects trInfo.tr after the service handler runs. -} - -func (ss *serverStream) Context() context.Context { - return ss.ctx -} - -func (ss *serverStream) SetHeader(md metadata.MD) error { - if md.Len() == 0 { - return nil - } - return ss.s.SetHeader(md) -} - -func (ss *serverStream) SendHeader(md metadata.MD) error { - return ss.t.WriteHeader(ss.s, md) -} - -func (ss *serverStream) SetTrailer(md metadata.MD) { - if md.Len() == 0 { - return - } - ss.s.SetTrailer(md) -} - -func (ss *serverStream) SendMsg(m interface{}) (err error) { - defer func() { - if ss.trInfo != nil { - ss.mu.Lock() - if ss.trInfo.tr != nil { - if err == nil { - ss.trInfo.tr.LazyLog(&payload{sent: true, msg: m}, true) - } else { - ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) - ss.trInfo.tr.SetError() - } - } - ss.mu.Unlock() - } - if err != nil && err != io.EOF { - st, _ := status.FromError(toRPCErr(err)) - ss.t.WriteStatus(ss.s, st) - } - if channelz.IsOn() && err == nil { - ss.t.IncrMsgSent() - } - }() - var outPayload *stats.OutPayload - if ss.statsHandler != nil { - outPayload = &stats.OutPayload{} - } - hdr, data, err := encode(ss.codec, m, ss.cp, outPayload, ss.comp) - if err != nil { - return err - } - if len(data) > ss.maxSendMessageSize { - return status.Errorf(codes.ResourceExhausted, "trying to send message larger than max (%d vs. %d)", len(data), ss.maxSendMessageSize) - } - if err := ss.t.Write(ss.s, hdr, data, &transport.Options{Last: false}); err != nil { - return toRPCErr(err) - } - if outPayload != nil { - outPayload.SentTime = time.Now() - ss.statsHandler.HandleRPC(ss.s.Context(), outPayload) - } - return nil -} - -func (ss *serverStream) RecvMsg(m interface{}) (err error) { - defer func() { - if ss.trInfo != nil { - ss.mu.Lock() - if ss.trInfo.tr != nil { - if err == nil { - ss.trInfo.tr.LazyLog(&payload{sent: false, msg: m}, true) - } else if err != io.EOF { - ss.trInfo.tr.LazyLog(&fmtStringer{"%v", []interface{}{err}}, true) - ss.trInfo.tr.SetError() - } - } - ss.mu.Unlock() - } - if err != nil && err != io.EOF { - st, _ := status.FromError(toRPCErr(err)) - ss.t.WriteStatus(ss.s, st) - } - if channelz.IsOn() && err == nil { - ss.t.IncrMsgRecv() - } - }() - var inPayload *stats.InPayload - if ss.statsHandler != nil { - inPayload = &stats.InPayload{} - } - if err := recv(ss.p, ss.codec, ss.s, ss.dc, m, ss.maxReceiveMessageSize, inPayload, ss.decomp); err != nil { - if err == io.EOF { - return err - } - if err == io.ErrUnexpectedEOF { - err = status.Errorf(codes.Internal, io.ErrUnexpectedEOF.Error()) - } - return toRPCErr(err) - } - if inPayload != nil { - ss.statsHandler.HandleRPC(ss.s.Context(), inPayload) - } - return nil -} - -// MethodFromServerStream returns the method string for the input stream. -// The returned string is in the format of "/service/method". -func MethodFromServerStream(stream ServerStream) (string, bool) { - return Method(stream.Context()) -} diff --git a/vendor/google.golang.org/grpc/tap/tap.go b/vendor/google.golang.org/grpc/tap/tap.go deleted file mode 100644 index 22b8fb50dea..00000000000 --- a/vendor/google.golang.org/grpc/tap/tap.go +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package tap defines the function handles which are executed on the transport -// layer of gRPC-Go and related information. Everything here is EXPERIMENTAL. -package tap - -import ( - "golang.org/x/net/context" -) - -// Info defines the relevant information needed by the handles. -type Info struct { - // FullMethodName is the string of grpc method (in the format of - // /package.service/method). - FullMethodName string - // TODO: More to be added. -} - -// ServerInHandle defines the function which runs before a new stream is created -// on the server side. If it returns a non-nil error, the stream will not be -// created and a RST_STREAM will be sent back to the client with REFUSED_STREAM. -// The client will receive an RPC error "code = Unavailable, desc = stream -// terminated by RST_STREAM with error code: REFUSED_STREAM". -// -// It's intended to be used in situations where you don't want to waste the -// resources to accept the new stream (e.g. rate-limiting). And the content of -// the error will be ignored and won't be sent back to the client. For other -// general usages, please use interceptors. -// -// Note that it is executed in the per-connection I/O goroutine(s) instead of -// per-RPC goroutine. Therefore, users should NOT have any -// blocking/time-consuming work in this handle. Otherwise all the RPCs would -// slow down. Also, for the same reason, this handle won't be called -// concurrently by gRPC. -type ServerInHandle func(ctx context.Context, info *Info) (context.Context, error) diff --git a/vendor/google.golang.org/grpc/trace.go b/vendor/google.golang.org/grpc/trace.go deleted file mode 100644 index c1c96dedcb7..00000000000 --- a/vendor/google.golang.org/grpc/trace.go +++ /dev/null @@ -1,113 +0,0 @@ -/* - * - * Copyright 2015 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package grpc - -import ( - "bytes" - "fmt" - "io" - "net" - "strings" - "time" - - "golang.org/x/net/trace" -) - -// EnableTracing controls whether to trace RPCs using the golang.org/x/net/trace package. -// This should only be set before any RPCs are sent or received by this program. -var EnableTracing bool - -// methodFamily returns the trace family for the given method. -// It turns "/pkg.Service/GetFoo" into "pkg.Service". -func methodFamily(m string) string { - m = strings.TrimPrefix(m, "/") // remove leading slash - if i := strings.Index(m, "/"); i >= 0 { - m = m[:i] // remove everything from second slash - } - if i := strings.LastIndex(m, "."); i >= 0 { - m = m[i+1:] // cut down to last dotted component - } - return m -} - -// traceInfo contains tracing information for an RPC. -type traceInfo struct { - tr trace.Trace - firstLine firstLine -} - -// firstLine is the first line of an RPC trace. -type firstLine struct { - client bool // whether this is a client (outgoing) RPC - remoteAddr net.Addr - deadline time.Duration // may be zero -} - -func (f *firstLine) String() string { - var line bytes.Buffer - io.WriteString(&line, "RPC: ") - if f.client { - io.WriteString(&line, "to") - } else { - io.WriteString(&line, "from") - } - fmt.Fprintf(&line, " %v deadline:", f.remoteAddr) - if f.deadline != 0 { - fmt.Fprint(&line, f.deadline) - } else { - io.WriteString(&line, "none") - } - return line.String() -} - -const truncateSize = 100 - -func truncate(x string, l int) string { - if l > len(x) { - return x - } - return x[:l] -} - -// payload represents an RPC request or response payload. -type payload struct { - sent bool // whether this is an outgoing payload - msg interface{} // e.g. a proto.Message - // TODO(dsymonds): add stringifying info to codec, and limit how much we hold here? -} - -func (p payload) String() string { - if p.sent { - return truncate(fmt.Sprintf("sent: %v", p.msg), truncateSize) - } - return truncate(fmt.Sprintf("recv: %v", p.msg), truncateSize) -} - -type fmtStringer struct { - format string - a []interface{} -} - -func (f *fmtStringer) String() string { - return fmt.Sprintf(f.format, f.a...) -} - -type stringer string - -func (s stringer) String() string { return string(s) } diff --git a/vendor/google.golang.org/grpc/transport/bdp_estimator.go b/vendor/google.golang.org/grpc/transport/bdp_estimator.go deleted file mode 100644 index 63cd2627c87..00000000000 --- a/vendor/google.golang.org/grpc/transport/bdp_estimator.go +++ /dev/null @@ -1,140 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package transport - -import ( - "sync" - "time" -) - -const ( - // bdpLimit is the maximum value the flow control windows - // will be increased to. - bdpLimit = (1 << 20) * 4 - // alpha is a constant factor used to keep a moving average - // of RTTs. - alpha = 0.9 - // If the current bdp sample is greater than or equal to - // our beta * our estimated bdp and the current bandwidth - // sample is the maximum bandwidth observed so far, we - // increase our bbp estimate by a factor of gamma. - beta = 0.66 - // To put our bdp to be smaller than or equal to twice the real BDP, - // we should multiply our current sample with 4/3, however to round things out - // we use 2 as the multiplication factor. - gamma = 2 -) - -// Adding arbitrary data to ping so that its ack can be identified. -// Easter-egg: what does the ping message say? -var bdpPing = &ping{data: [8]byte{2, 4, 16, 16, 9, 14, 7, 7}} - -type bdpEstimator struct { - // sentAt is the time when the ping was sent. - sentAt time.Time - - mu sync.Mutex - // bdp is the current bdp estimate. - bdp uint32 - // sample is the number of bytes received in one measurement cycle. - sample uint32 - // bwMax is the maximum bandwidth noted so far (bytes/sec). - bwMax float64 - // bool to keep track of the beginning of a new measurement cycle. - isSent bool - // Callback to update the window sizes. - updateFlowControl func(n uint32) - // sampleCount is the number of samples taken so far. - sampleCount uint64 - // round trip time (seconds) - rtt float64 -} - -// timesnap registers the time bdp ping was sent out so that -// network rtt can be calculated when its ack is received. -// It is called (by controller) when the bdpPing is -// being written on the wire. -func (b *bdpEstimator) timesnap(d [8]byte) { - if bdpPing.data != d { - return - } - b.sentAt = time.Now() -} - -// add adds bytes to the current sample for calculating bdp. -// It returns true only if a ping must be sent. This can be used -// by the caller (handleData) to make decision about batching -// a window update with it. -func (b *bdpEstimator) add(n uint32) bool { - b.mu.Lock() - defer b.mu.Unlock() - if b.bdp == bdpLimit { - return false - } - if !b.isSent { - b.isSent = true - b.sample = n - b.sentAt = time.Time{} - b.sampleCount++ - return true - } - b.sample += n - return false -} - -// calculate is called when an ack for a bdp ping is received. -// Here we calculate the current bdp and bandwidth sample and -// decide if the flow control windows should go up. -func (b *bdpEstimator) calculate(d [8]byte) { - // Check if the ping acked for was the bdp ping. - if bdpPing.data != d { - return - } - b.mu.Lock() - rttSample := time.Since(b.sentAt).Seconds() - if b.sampleCount < 10 { - // Bootstrap rtt with an average of first 10 rtt samples. - b.rtt += (rttSample - b.rtt) / float64(b.sampleCount) - } else { - // Heed to the recent past more. - b.rtt += (rttSample - b.rtt) * float64(alpha) - } - b.isSent = false - // The number of bytes accumulated so far in the sample is smaller - // than or equal to 1.5 times the real BDP on a saturated connection. - bwCurrent := float64(b.sample) / (b.rtt * float64(1.5)) - if bwCurrent > b.bwMax { - b.bwMax = bwCurrent - } - // If the current sample (which is smaller than or equal to the 1.5 times the real BDP) is - // greater than or equal to 2/3rd our perceived bdp AND this is the maximum bandwidth seen so far, we - // should update our perception of the network BDP. - if float64(b.sample) >= beta*float64(b.bdp) && bwCurrent == b.bwMax && b.bdp != bdpLimit { - sampleFloat := float64(b.sample) - b.bdp = uint32(gamma * sampleFloat) - if b.bdp > bdpLimit { - b.bdp = bdpLimit - } - bdp := b.bdp - b.mu.Unlock() - b.updateFlowControl(bdp) - return - } - b.mu.Unlock() -} diff --git a/vendor/google.golang.org/grpc/transport/controlbuf.go b/vendor/google.golang.org/grpc/transport/controlbuf.go deleted file mode 100644 index e147cd51bf1..00000000000 --- a/vendor/google.golang.org/grpc/transport/controlbuf.go +++ /dev/null @@ -1,769 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package transport - -import ( - "bytes" - "fmt" - "runtime" - "sync" - - "golang.org/x/net/http2" - "golang.org/x/net/http2/hpack" -) - -type itemNode struct { - it interface{} - next *itemNode -} - -type itemList struct { - head *itemNode - tail *itemNode -} - -func (il *itemList) enqueue(i interface{}) { - n := &itemNode{it: i} - if il.tail == nil { - il.head, il.tail = n, n - return - } - il.tail.next = n - il.tail = n -} - -// peek returns the first item in the list without removing it from the -// list. -func (il *itemList) peek() interface{} { - return il.head.it -} - -func (il *itemList) dequeue() interface{} { - if il.head == nil { - return nil - } - i := il.head.it - il.head = il.head.next - if il.head == nil { - il.tail = nil - } - return i -} - -func (il *itemList) dequeueAll() *itemNode { - h := il.head - il.head, il.tail = nil, nil - return h -} - -func (il *itemList) isEmpty() bool { - return il.head == nil -} - -// The following defines various control items which could flow through -// the control buffer of transport. They represent different aspects of -// control tasks, e.g., flow control, settings, streaming resetting, etc. - -type headerFrame struct { - streamID uint32 - hf []hpack.HeaderField - endStream bool // Valid on server side. - initStream func(uint32) (bool, error) // Used only on the client side. - onWrite func() - wq *writeQuota // write quota for the stream created. - cleanup *cleanupStream // Valid on the server side. - onOrphaned func(error) // Valid on client-side -} - -type cleanupStream struct { - streamID uint32 - idPtr *uint32 - rst bool - rstCode http2.ErrCode - onWrite func() -} - -type dataFrame struct { - streamID uint32 - endStream bool - h []byte - d []byte - // onEachWrite is called every time - // a part of d is written out. - onEachWrite func() -} - -type incomingWindowUpdate struct { - streamID uint32 - increment uint32 -} - -type outgoingWindowUpdate struct { - streamID uint32 - increment uint32 -} - -type incomingSettings struct { - ss []http2.Setting -} - -type outgoingSettings struct { - ss []http2.Setting -} - -type settingsAck struct { -} - -type incomingGoAway struct { -} - -type goAway struct { - code http2.ErrCode - debugData []byte - headsUp bool - closeConn bool -} - -type ping struct { - ack bool - data [8]byte -} - -type outFlowControlSizeRequest struct { - resp chan uint32 -} - -type outStreamState int - -const ( - active outStreamState = iota - empty - waitingOnStreamQuota -) - -type outStream struct { - id uint32 - state outStreamState - itl *itemList - bytesOutStanding int - wq *writeQuota - - next *outStream - prev *outStream -} - -func (s *outStream) deleteSelf() { - if s.prev != nil { - s.prev.next = s.next - } - if s.next != nil { - s.next.prev = s.prev - } - s.next, s.prev = nil, nil -} - -type outStreamList struct { - // Following are sentinel objects that mark the - // beginning and end of the list. They do not - // contain any item lists. All valid objects are - // inserted in between them. - // This is needed so that an outStream object can - // deleteSelf() in O(1) time without knowing which - // list it belongs to. - head *outStream - tail *outStream -} - -func newOutStreamList() *outStreamList { - head, tail := new(outStream), new(outStream) - head.next = tail - tail.prev = head - return &outStreamList{ - head: head, - tail: tail, - } -} - -func (l *outStreamList) enqueue(s *outStream) { - e := l.tail.prev - e.next = s - s.prev = e - s.next = l.tail - l.tail.prev = s -} - -// remove from the beginning of the list. -func (l *outStreamList) dequeue() *outStream { - b := l.head.next - if b == l.tail { - return nil - } - b.deleteSelf() - return b -} - -type controlBuffer struct { - ch chan struct{} - done <-chan struct{} - mu sync.Mutex - consumerWaiting bool - list *itemList - err error -} - -func newControlBuffer(done <-chan struct{}) *controlBuffer { - return &controlBuffer{ - ch: make(chan struct{}, 1), - list: &itemList{}, - done: done, - } -} - -func (c *controlBuffer) put(it interface{}) error { - _, err := c.executeAndPut(nil, it) - return err -} - -func (c *controlBuffer) executeAndPut(f func(it interface{}) bool, it interface{}) (bool, error) { - var wakeUp bool - c.mu.Lock() - if c.err != nil { - c.mu.Unlock() - return false, c.err - } - if f != nil { - if !f(it) { // f wasn't successful - c.mu.Unlock() - return false, nil - } - } - if c.consumerWaiting { - wakeUp = true - c.consumerWaiting = false - } - c.list.enqueue(it) - c.mu.Unlock() - if wakeUp { - select { - case c.ch <- struct{}{}: - default: - } - } - return true, nil -} - -func (c *controlBuffer) get(block bool) (interface{}, error) { - for { - c.mu.Lock() - if c.err != nil { - c.mu.Unlock() - return nil, c.err - } - if !c.list.isEmpty() { - h := c.list.dequeue() - c.mu.Unlock() - return h, nil - } - if !block { - c.mu.Unlock() - return nil, nil - } - c.consumerWaiting = true - c.mu.Unlock() - select { - case <-c.ch: - case <-c.done: - c.finish() - return nil, ErrConnClosing - } - } -} - -func (c *controlBuffer) finish() { - c.mu.Lock() - if c.err != nil { - c.mu.Unlock() - return - } - c.err = ErrConnClosing - // There may be headers for streams in the control buffer. - // These streams need to be cleaned out since the transport - // is still not aware of these yet. - for head := c.list.dequeueAll(); head != nil; head = head.next { - hdr, ok := head.it.(*headerFrame) - if !ok { - continue - } - if hdr.onOrphaned != nil { // It will be nil on the server-side. - hdr.onOrphaned(ErrConnClosing) - } - } - c.mu.Unlock() -} - -type side int - -const ( - clientSide side = iota - serverSide -) - -type loopyWriter struct { - side side - cbuf *controlBuffer - sendQuota uint32 - oiws uint32 // outbound initial window size. - estdStreams map[uint32]*outStream // Established streams. - activeStreams *outStreamList // Streams that are sending data. - framer *framer - hBuf *bytes.Buffer // The buffer for HPACK encoding. - hEnc *hpack.Encoder // HPACK encoder. - bdpEst *bdpEstimator - draining bool - - // Side-specific handlers - ssGoAwayHandler func(*goAway) (bool, error) -} - -func newLoopyWriter(s side, fr *framer, cbuf *controlBuffer, bdpEst *bdpEstimator) *loopyWriter { - var buf bytes.Buffer - l := &loopyWriter{ - side: s, - cbuf: cbuf, - sendQuota: defaultWindowSize, - oiws: defaultWindowSize, - estdStreams: make(map[uint32]*outStream), - activeStreams: newOutStreamList(), - framer: fr, - hBuf: &buf, - hEnc: hpack.NewEncoder(&buf), - bdpEst: bdpEst, - } - return l -} - -const minBatchSize = 1000 - -// run should be run in a separate goroutine. -func (l *loopyWriter) run() { - var ( - it interface{} - err error - isEmpty bool - ) - defer func() { - errorf("transport: loopyWriter.run returning. Err: %v", err) - }() - for { - it, err = l.cbuf.get(true) - if err != nil { - return - } - if err = l.handle(it); err != nil { - return - } - if _, err = l.processData(); err != nil { - return - } - gosched := true - hasdata: - for { - it, err = l.cbuf.get(false) - if err != nil { - return - } - if it != nil { - if err = l.handle(it); err != nil { - return - } - if _, err = l.processData(); err != nil { - return - } - continue hasdata - } - if isEmpty, err = l.processData(); err != nil { - return - } - if !isEmpty { - continue hasdata - } - if gosched { - gosched = false - if l.framer.writer.offset < minBatchSize { - runtime.Gosched() - continue hasdata - } - } - l.framer.writer.Flush() - break hasdata - - } - } -} - -func (l *loopyWriter) outgoingWindowUpdateHandler(w *outgoingWindowUpdate) error { - return l.framer.fr.WriteWindowUpdate(w.streamID, w.increment) -} - -func (l *loopyWriter) incomingWindowUpdateHandler(w *incomingWindowUpdate) error { - // Otherwise update the quota. - if w.streamID == 0 { - l.sendQuota += w.increment - return nil - } - // Find the stream and update it. - if str, ok := l.estdStreams[w.streamID]; ok { - str.bytesOutStanding -= int(w.increment) - if strQuota := int(l.oiws) - str.bytesOutStanding; strQuota > 0 && str.state == waitingOnStreamQuota { - str.state = active - l.activeStreams.enqueue(str) - return nil - } - } - return nil -} - -func (l *loopyWriter) outgoingSettingsHandler(s *outgoingSettings) error { - return l.framer.fr.WriteSettings(s.ss...) -} - -func (l *loopyWriter) incomingSettingsHandler(s *incomingSettings) error { - if err := l.applySettings(s.ss); err != nil { - return err - } - return l.framer.fr.WriteSettingsAck() -} - -func (l *loopyWriter) headerHandler(h *headerFrame) error { - if l.side == serverSide { - if h.endStream { // Case 1.A: Server wants to close stream. - // Make sure it's not a trailers only response. - if str, ok := l.estdStreams[h.streamID]; ok { - if str.state != empty { // either active or waiting on stream quota. - // add it str's list of items. - str.itl.enqueue(h) - return nil - } - } - if err := l.writeHeader(h.streamID, h.endStream, h.hf, h.onWrite); err != nil { - return err - } - return l.cleanupStreamHandler(h.cleanup) - } - // Case 1.B: Server is responding back with headers. - str := &outStream{ - state: empty, - itl: &itemList{}, - wq: h.wq, - } - l.estdStreams[h.streamID] = str - return l.writeHeader(h.streamID, h.endStream, h.hf, h.onWrite) - } - // Case 2: Client wants to originate stream. - str := &outStream{ - id: h.streamID, - state: empty, - itl: &itemList{}, - wq: h.wq, - } - str.itl.enqueue(h) - return l.originateStream(str) -} - -func (l *loopyWriter) originateStream(str *outStream) error { - hdr := str.itl.dequeue().(*headerFrame) - sendPing, err := hdr.initStream(str.id) - if err != nil { - if err == ErrConnClosing { - return err - } - // Other errors(errStreamDrain) need not close transport. - return nil - } - if err = l.writeHeader(str.id, hdr.endStream, hdr.hf, hdr.onWrite); err != nil { - return err - } - l.estdStreams[str.id] = str - if sendPing { - return l.pingHandler(&ping{data: [8]byte{}}) - } - return nil -} - -func (l *loopyWriter) writeHeader(streamID uint32, endStream bool, hf []hpack.HeaderField, onWrite func()) error { - if onWrite != nil { - onWrite() - } - l.hBuf.Reset() - for _, f := range hf { - if err := l.hEnc.WriteField(f); err != nil { - warningf("transport: loopyWriter.writeHeader encountered error while encoding headers:", err) - } - } - var ( - err error - endHeaders, first bool - ) - first = true - for !endHeaders { - size := l.hBuf.Len() - if size > http2MaxFrameLen { - size = http2MaxFrameLen - } else { - endHeaders = true - } - if first { - first = false - err = l.framer.fr.WriteHeaders(http2.HeadersFrameParam{ - StreamID: streamID, - BlockFragment: l.hBuf.Next(size), - EndStream: endStream, - EndHeaders: endHeaders, - }) - } else { - err = l.framer.fr.WriteContinuation( - streamID, - endHeaders, - l.hBuf.Next(size), - ) - } - if err != nil { - return err - } - } - return nil -} - -func (l *loopyWriter) preprocessData(df *dataFrame) error { - str, ok := l.estdStreams[df.streamID] - if !ok { - return nil - } - // If we got data for a stream it means that - // stream was originated and the headers were sent out. - str.itl.enqueue(df) - if str.state == empty { - str.state = active - l.activeStreams.enqueue(str) - } - return nil -} - -func (l *loopyWriter) pingHandler(p *ping) error { - if !p.ack { - l.bdpEst.timesnap(p.data) - } - return l.framer.fr.WritePing(p.ack, p.data) - -} - -func (l *loopyWriter) outFlowControlSizeRequestHandler(o *outFlowControlSizeRequest) error { - o.resp <- l.sendQuota - return nil -} - -func (l *loopyWriter) cleanupStreamHandler(c *cleanupStream) error { - c.onWrite() - if str, ok := l.estdStreams[c.streamID]; ok { - // On the server side it could be a trailers-only response or - // a RST_STREAM before stream initialization thus the stream might - // not be established yet. - delete(l.estdStreams, c.streamID) - str.deleteSelf() - } - if c.rst { // If RST_STREAM needs to be sent. - if err := l.framer.fr.WriteRSTStream(c.streamID, c.rstCode); err != nil { - return err - } - } - if l.side == clientSide && l.draining && len(l.estdStreams) == 0 { - return ErrConnClosing - } - return nil -} - -func (l *loopyWriter) incomingGoAwayHandler(*incomingGoAway) error { - if l.side == clientSide { - l.draining = true - if len(l.estdStreams) == 0 { - return ErrConnClosing - } - } - return nil -} - -func (l *loopyWriter) goAwayHandler(g *goAway) error { - // Handling of outgoing GoAway is very specific to side. - if l.ssGoAwayHandler != nil { - draining, err := l.ssGoAwayHandler(g) - if err != nil { - return err - } - l.draining = draining - } - return nil -} - -func (l *loopyWriter) handle(i interface{}) error { - switch i := i.(type) { - case *incomingWindowUpdate: - return l.incomingWindowUpdateHandler(i) - case *outgoingWindowUpdate: - return l.outgoingWindowUpdateHandler(i) - case *incomingSettings: - return l.incomingSettingsHandler(i) - case *outgoingSettings: - return l.outgoingSettingsHandler(i) - case *headerFrame: - return l.headerHandler(i) - case *cleanupStream: - return l.cleanupStreamHandler(i) - case *incomingGoAway: - return l.incomingGoAwayHandler(i) - case *dataFrame: - return l.preprocessData(i) - case *ping: - return l.pingHandler(i) - case *goAway: - return l.goAwayHandler(i) - case *outFlowControlSizeRequest: - return l.outFlowControlSizeRequestHandler(i) - default: - return fmt.Errorf("transport: unknown control message type %T", i) - } -} - -func (l *loopyWriter) applySettings(ss []http2.Setting) error { - for _, s := range ss { - switch s.ID { - case http2.SettingInitialWindowSize: - o := l.oiws - l.oiws = s.Val - if o < l.oiws { - // If the new limit is greater make all depleted streams active. - for _, stream := range l.estdStreams { - if stream.state == waitingOnStreamQuota { - stream.state = active - l.activeStreams.enqueue(stream) - } - } - } - } - } - return nil -} - -func (l *loopyWriter) processData() (bool, error) { - if l.sendQuota == 0 { - return true, nil - } - str := l.activeStreams.dequeue() - if str == nil { - return true, nil - } - dataItem := str.itl.peek().(*dataFrame) - if len(dataItem.h) == 0 && len(dataItem.d) == 0 { - // Client sends out empty data frame with endStream = true - if err := l.framer.fr.WriteData(dataItem.streamID, dataItem.endStream, nil); err != nil { - return false, err - } - str.itl.dequeue() - if str.itl.isEmpty() { - str.state = empty - } else if trailer, ok := str.itl.peek().(*headerFrame); ok { // the next item is trailers. - if err := l.writeHeader(trailer.streamID, trailer.endStream, trailer.hf, trailer.onWrite); err != nil { - return false, err - } - if err := l.cleanupStreamHandler(trailer.cleanup); err != nil { - return false, nil - } - } else { - l.activeStreams.enqueue(str) - } - return false, nil - } - var ( - idx int - buf []byte - ) - if len(dataItem.h) != 0 { // data header has not been written out yet. - buf = dataItem.h - } else { - idx = 1 - buf = dataItem.d - } - size := http2MaxFrameLen - if len(buf) < size { - size = len(buf) - } - if strQuota := int(l.oiws) - str.bytesOutStanding; strQuota <= 0 { - str.state = waitingOnStreamQuota - return false, nil - } else if strQuota < size { - size = strQuota - } - - if l.sendQuota < uint32(size) { - size = int(l.sendQuota) - } - // Now that outgoing flow controls are checked we can replenish str's write quota - str.wq.replenish(size) - var endStream bool - // This last data message on this stream and all - // of it can be written in this go. - if dataItem.endStream && size == len(buf) { - // buf contains either data or it contains header but data is empty. - if idx == 1 || len(dataItem.d) == 0 { - endStream = true - } - } - if dataItem.onEachWrite != nil { - dataItem.onEachWrite() - } - if err := l.framer.fr.WriteData(dataItem.streamID, endStream, buf[:size]); err != nil { - return false, err - } - buf = buf[size:] - str.bytesOutStanding += size - l.sendQuota -= uint32(size) - if idx == 0 { - dataItem.h = buf - } else { - dataItem.d = buf - } - - if len(dataItem.h) == 0 && len(dataItem.d) == 0 { // All the data from that message was written out. - str.itl.dequeue() - } - if str.itl.isEmpty() { - str.state = empty - } else if trailer, ok := str.itl.peek().(*headerFrame); ok { // The next item is trailers. - if err := l.writeHeader(trailer.streamID, trailer.endStream, trailer.hf, trailer.onWrite); err != nil { - return false, err - } - if err := l.cleanupStreamHandler(trailer.cleanup); err != nil { - return false, err - } - } else if int(l.oiws)-str.bytesOutStanding <= 0 { // Ran out of stream quota. - str.state = waitingOnStreamQuota - } else { // Otherwise add it back to the list of active streams. - l.activeStreams.enqueue(str) - } - return false, nil -} diff --git a/vendor/google.golang.org/grpc/transport/flowcontrol.go b/vendor/google.golang.org/grpc/transport/flowcontrol.go deleted file mode 100644 index 378f5c4502c..00000000000 --- a/vendor/google.golang.org/grpc/transport/flowcontrol.go +++ /dev/null @@ -1,236 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package transport - -import ( - "fmt" - "math" - "sync" - "sync/atomic" - "time" -) - -const ( - // The default value of flow control window size in HTTP2 spec. - defaultWindowSize = 65535 - // The initial window size for flow control. - initialWindowSize = defaultWindowSize // for an RPC - infinity = time.Duration(math.MaxInt64) - defaultClientKeepaliveTime = infinity - defaultClientKeepaliveTimeout = 20 * time.Second - defaultMaxStreamsClient = 100 - defaultMaxConnectionIdle = infinity - defaultMaxConnectionAge = infinity - defaultMaxConnectionAgeGrace = infinity - defaultServerKeepaliveTime = 2 * time.Hour - defaultServerKeepaliveTimeout = 20 * time.Second - defaultKeepalivePolicyMinTime = 5 * time.Minute - // max window limit set by HTTP2 Specs. - maxWindowSize = math.MaxInt32 - // defaultWriteQuota is the default value for number of data - // bytes that each stream can schedule before some of it being - // flushed out. - defaultWriteQuota = 64 * 1024 -) - -// writeQuota is a soft limit on the amount of data a stream can -// schedule before some of it is written out. -type writeQuota struct { - quota int32 - // get waits on read from when quota goes less than or equal to zero. - // replenish writes on it when quota goes positive again. - ch chan struct{} - // done is triggered in error case. - done <-chan struct{} -} - -func newWriteQuota(sz int32, done <-chan struct{}) *writeQuota { - return &writeQuota{ - quota: sz, - ch: make(chan struct{}, 1), - done: done, - } -} - -func (w *writeQuota) get(sz int32) error { - for { - if atomic.LoadInt32(&w.quota) > 0 { - atomic.AddInt32(&w.quota, -sz) - return nil - } - select { - case <-w.ch: - continue - case <-w.done: - return errStreamDone - } - } -} - -func (w *writeQuota) replenish(n int) { - sz := int32(n) - a := atomic.AddInt32(&w.quota, sz) - b := a - sz - if b <= 0 && a > 0 { - select { - case w.ch <- struct{}{}: - default: - } - } -} - -type trInFlow struct { - limit uint32 - unacked uint32 - effectiveWindowSize uint32 -} - -func (f *trInFlow) newLimit(n uint32) uint32 { - d := n - f.limit - f.limit = n - f.updateEffectiveWindowSize() - return d -} - -func (f *trInFlow) onData(n uint32) uint32 { - f.unacked += n - if f.unacked >= f.limit/4 { - w := f.unacked - f.unacked = 0 - f.updateEffectiveWindowSize() - return w - } - f.updateEffectiveWindowSize() - return 0 -} - -func (f *trInFlow) reset() uint32 { - w := f.unacked - f.unacked = 0 - f.updateEffectiveWindowSize() - return w -} - -func (f *trInFlow) updateEffectiveWindowSize() { - atomic.StoreUint32(&f.effectiveWindowSize, f.limit-f.unacked) -} - -func (f *trInFlow) getSize() uint32 { - return atomic.LoadUint32(&f.effectiveWindowSize) -} - -// TODO(mmukhi): Simplify this code. -// inFlow deals with inbound flow control -type inFlow struct { - mu sync.Mutex - // The inbound flow control limit for pending data. - limit uint32 - // pendingData is the overall data which have been received but not been - // consumed by applications. - pendingData uint32 - // The amount of data the application has consumed but grpc has not sent - // window update for them. Used to reduce window update frequency. - pendingUpdate uint32 - // delta is the extra window update given by receiver when an application - // is reading data bigger in size than the inFlow limit. - delta uint32 -} - -// newLimit updates the inflow window to a new value n. -// It assumes that n is always greater than the old limit. -func (f *inFlow) newLimit(n uint32) uint32 { - f.mu.Lock() - d := n - f.limit - f.limit = n - f.mu.Unlock() - return d -} - -func (f *inFlow) maybeAdjust(n uint32) uint32 { - if n > uint32(math.MaxInt32) { - n = uint32(math.MaxInt32) - } - f.mu.Lock() - // estSenderQuota is the receiver's view of the maximum number of bytes the sender - // can send without a window update. - estSenderQuota := int32(f.limit - (f.pendingData + f.pendingUpdate)) - // estUntransmittedData is the maximum number of bytes the sends might not have put - // on the wire yet. A value of 0 or less means that we have already received all or - // more bytes than the application is requesting to read. - estUntransmittedData := int32(n - f.pendingData) // Casting into int32 since it could be negative. - // This implies that unless we send a window update, the sender won't be able to send all the bytes - // for this message. Therefore we must send an update over the limit since there's an active read - // request from the application. - if estUntransmittedData > estSenderQuota { - // Sender's window shouldn't go more than 2^31 - 1 as specified in the HTTP spec. - if f.limit+n > maxWindowSize { - f.delta = maxWindowSize - f.limit - } else { - // Send a window update for the whole message and not just the difference between - // estUntransmittedData and estSenderQuota. This will be helpful in case the message - // is padded; We will fallback on the current available window(at least a 1/4th of the limit). - f.delta = n - } - f.mu.Unlock() - return f.delta - } - f.mu.Unlock() - return 0 -} - -// onData is invoked when some data frame is received. It updates pendingData. -func (f *inFlow) onData(n uint32) error { - f.mu.Lock() - f.pendingData += n - if f.pendingData+f.pendingUpdate > f.limit+f.delta { - limit := f.limit - rcvd := f.pendingData + f.pendingUpdate - f.mu.Unlock() - return fmt.Errorf("received %d-bytes data exceeding the limit %d bytes", rcvd, limit) - } - f.mu.Unlock() - return nil -} - -// onRead is invoked when the application reads the data. It returns the window size -// to be sent to the peer. -func (f *inFlow) onRead(n uint32) uint32 { - f.mu.Lock() - if f.pendingData == 0 { - f.mu.Unlock() - return 0 - } - f.pendingData -= n - if n > f.delta { - n -= f.delta - f.delta = 0 - } else { - f.delta -= n - n = 0 - } - f.pendingUpdate += n - if f.pendingUpdate >= f.limit/4 { - wu := f.pendingUpdate - f.pendingUpdate = 0 - f.mu.Unlock() - return wu - } - f.mu.Unlock() - return 0 -} diff --git a/vendor/google.golang.org/grpc/transport/go16.go b/vendor/google.golang.org/grpc/transport/go16.go deleted file mode 100644 index 5babcf9b877..00000000000 --- a/vendor/google.golang.org/grpc/transport/go16.go +++ /dev/null @@ -1,51 +0,0 @@ -// +build go1.6,!go1.7 - -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package transport - -import ( - "net" - "net/http" - - "google.golang.org/grpc/codes" - - "golang.org/x/net/context" -) - -// dialContext connects to the address on the named network. -func dialContext(ctx context.Context, network, address string) (net.Conn, error) { - return (&net.Dialer{Cancel: ctx.Done()}).Dial(network, address) -} - -// ContextErr converts the error from context package into a StreamError. -func ContextErr(err error) StreamError { - switch err { - case context.DeadlineExceeded: - return streamErrorf(codes.DeadlineExceeded, "%v", err) - case context.Canceled: - return streamErrorf(codes.Canceled, "%v", err) - } - return streamErrorf(codes.Internal, "Unexpected error from context packet: %v", err) -} - -// contextFromRequest returns a background context. -func contextFromRequest(r *http.Request) context.Context { - return context.Background() -} diff --git a/vendor/google.golang.org/grpc/transport/go17.go b/vendor/google.golang.org/grpc/transport/go17.go deleted file mode 100644 index b7fa6bdb9ca..00000000000 --- a/vendor/google.golang.org/grpc/transport/go17.go +++ /dev/null @@ -1,52 +0,0 @@ -// +build go1.7 - -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package transport - -import ( - "context" - "net" - "net/http" - - "google.golang.org/grpc/codes" - - netctx "golang.org/x/net/context" -) - -// dialContext connects to the address on the named network. -func dialContext(ctx context.Context, network, address string) (net.Conn, error) { - return (&net.Dialer{}).DialContext(ctx, network, address) -} - -// ContextErr converts the error from context package into a StreamError. -func ContextErr(err error) StreamError { - switch err { - case context.DeadlineExceeded, netctx.DeadlineExceeded: - return streamErrorf(codes.DeadlineExceeded, "%v", err) - case context.Canceled, netctx.Canceled: - return streamErrorf(codes.Canceled, "%v", err) - } - return streamErrorf(codes.Internal, "Unexpected error from context packet: %v", err) -} - -// contextFromRequest returns a context from the HTTP Request. -func contextFromRequest(r *http.Request) context.Context { - return r.Context() -} diff --git a/vendor/google.golang.org/grpc/transport/handler_server.go b/vendor/google.golang.org/grpc/transport/handler_server.go deleted file mode 100644 index f71b7482174..00000000000 --- a/vendor/google.golang.org/grpc/transport/handler_server.go +++ /dev/null @@ -1,451 +0,0 @@ -/* - * - * Copyright 2016 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// This file is the implementation of a gRPC server using HTTP/2 which -// uses the standard Go http2 Server implementation (via the -// http.Handler interface), rather than speaking low-level HTTP/2 -// frames itself. It is the implementation of *grpc.Server.ServeHTTP. - -package transport - -import ( - "errors" - "fmt" - "io" - "net" - "net/http" - "strings" - "sync" - "time" - - "github.com/golang/protobuf/proto" - "golang.org/x/net/context" - "golang.org/x/net/http2" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/peer" - "google.golang.org/grpc/stats" - "google.golang.org/grpc/status" -) - -// NewServerHandlerTransport returns a ServerTransport handling gRPC -// from inside an http.Handler. It requires that the http Server -// supports HTTP/2. -func NewServerHandlerTransport(w http.ResponseWriter, r *http.Request, stats stats.Handler) (ServerTransport, error) { - if r.ProtoMajor != 2 { - return nil, errors.New("gRPC requires HTTP/2") - } - if r.Method != "POST" { - return nil, errors.New("invalid gRPC request method") - } - contentType := r.Header.Get("Content-Type") - // TODO: do we assume contentType is lowercase? we did before - contentSubtype, validContentType := contentSubtype(contentType) - if !validContentType { - return nil, errors.New("invalid gRPC request content-type") - } - if _, ok := w.(http.Flusher); !ok { - return nil, errors.New("gRPC requires a ResponseWriter supporting http.Flusher") - } - if _, ok := w.(http.CloseNotifier); !ok { - return nil, errors.New("gRPC requires a ResponseWriter supporting http.CloseNotifier") - } - - st := &serverHandlerTransport{ - rw: w, - req: r, - closedCh: make(chan struct{}), - writes: make(chan func()), - contentType: contentType, - contentSubtype: contentSubtype, - stats: stats, - } - - if v := r.Header.Get("grpc-timeout"); v != "" { - to, err := decodeTimeout(v) - if err != nil { - return nil, streamErrorf(codes.Internal, "malformed time-out: %v", err) - } - st.timeoutSet = true - st.timeout = to - } - - metakv := []string{"content-type", contentType} - if r.Host != "" { - metakv = append(metakv, ":authority", r.Host) - } - for k, vv := range r.Header { - k = strings.ToLower(k) - if isReservedHeader(k) && !isWhitelistedHeader(k) { - continue - } - for _, v := range vv { - v, err := decodeMetadataHeader(k, v) - if err != nil { - return nil, streamErrorf(codes.Internal, "malformed binary metadata: %v", err) - } - metakv = append(metakv, k, v) - } - } - st.headerMD = metadata.Pairs(metakv...) - - return st, nil -} - -// serverHandlerTransport is an implementation of ServerTransport -// which replies to exactly one gRPC request (exactly one HTTP request), -// using the net/http.Handler interface. This http.Handler is guaranteed -// at this point to be speaking over HTTP/2, so it's able to speak valid -// gRPC. -type serverHandlerTransport struct { - rw http.ResponseWriter - req *http.Request - timeoutSet bool - timeout time.Duration - didCommonHeaders bool - - headerMD metadata.MD - - closeOnce sync.Once - closedCh chan struct{} // closed on Close - - // writes is a channel of code to run serialized in the - // ServeHTTP (HandleStreams) goroutine. The channel is closed - // when WriteStatus is called. - writes chan func() - - // block concurrent WriteStatus calls - // e.g. grpc/(*serverStream).SendMsg/RecvMsg - writeStatusMu sync.Mutex - - // we just mirror the request content-type - contentType string - // we store both contentType and contentSubtype so we don't keep recreating them - // TODO make sure this is consistent across handler_server and http2_server - contentSubtype string - - stats stats.Handler -} - -func (ht *serverHandlerTransport) Close() error { - ht.closeOnce.Do(ht.closeCloseChanOnce) - return nil -} - -func (ht *serverHandlerTransport) closeCloseChanOnce() { close(ht.closedCh) } - -func (ht *serverHandlerTransport) RemoteAddr() net.Addr { return strAddr(ht.req.RemoteAddr) } - -// strAddr is a net.Addr backed by either a TCP "ip:port" string, or -// the empty string if unknown. -type strAddr string - -func (a strAddr) Network() string { - if a != "" { - // Per the documentation on net/http.Request.RemoteAddr, if this is - // set, it's set to the IP:port of the peer (hence, TCP): - // https://golang.org/pkg/net/http/#Request - // - // If we want to support Unix sockets later, we can - // add our own grpc-specific convention within the - // grpc codebase to set RemoteAddr to a different - // format, or probably better: we can attach it to the - // context and use that from serverHandlerTransport.RemoteAddr. - return "tcp" - } - return "" -} - -func (a strAddr) String() string { return string(a) } - -// do runs fn in the ServeHTTP goroutine. -func (ht *serverHandlerTransport) do(fn func()) error { - // Avoid a panic writing to closed channel. Imperfect but maybe good enough. - select { - case <-ht.closedCh: - return ErrConnClosing - default: - select { - case ht.writes <- fn: - return nil - case <-ht.closedCh: - return ErrConnClosing - } - } -} - -func (ht *serverHandlerTransport) WriteStatus(s *Stream, st *status.Status) error { - ht.writeStatusMu.Lock() - defer ht.writeStatusMu.Unlock() - - err := ht.do(func() { - ht.writeCommonHeaders(s) - - // And flush, in case no header or body has been sent yet. - // This forces a separation of headers and trailers if this is the - // first call (for example, in end2end tests's TestNoService). - ht.rw.(http.Flusher).Flush() - - h := ht.rw.Header() - h.Set("Grpc-Status", fmt.Sprintf("%d", st.Code())) - if m := st.Message(); m != "" { - h.Set("Grpc-Message", encodeGrpcMessage(m)) - } - - if p := st.Proto(); p != nil && len(p.Details) > 0 { - stBytes, err := proto.Marshal(p) - if err != nil { - // TODO: return error instead, when callers are able to handle it. - panic(err) - } - - h.Set("Grpc-Status-Details-Bin", encodeBinHeader(stBytes)) - } - - if md := s.Trailer(); len(md) > 0 { - for k, vv := range md { - // Clients don't tolerate reading restricted headers after some non restricted ones were sent. - if isReservedHeader(k) { - continue - } - for _, v := range vv { - // http2 ResponseWriter mechanism to send undeclared Trailers after - // the headers have possibly been written. - h.Add(http2.TrailerPrefix+k, encodeMetadataHeader(k, v)) - } - } - } - }) - - if err == nil { // transport has not been closed - if ht.stats != nil { - ht.stats.HandleRPC(s.Context(), &stats.OutTrailer{}) - } - ht.Close() - close(ht.writes) - } - return err -} - -// writeCommonHeaders sets common headers on the first write -// call (Write, WriteHeader, or WriteStatus). -func (ht *serverHandlerTransport) writeCommonHeaders(s *Stream) { - if ht.didCommonHeaders { - return - } - ht.didCommonHeaders = true - - h := ht.rw.Header() - h["Date"] = nil // suppress Date to make tests happy; TODO: restore - h.Set("Content-Type", ht.contentType) - - // Predeclare trailers we'll set later in WriteStatus (after the body). - // This is a SHOULD in the HTTP RFC, and the way you add (known) - // Trailers per the net/http.ResponseWriter contract. - // See https://golang.org/pkg/net/http/#ResponseWriter - // and https://golang.org/pkg/net/http/#example_ResponseWriter_trailers - h.Add("Trailer", "Grpc-Status") - h.Add("Trailer", "Grpc-Message") - h.Add("Trailer", "Grpc-Status-Details-Bin") - - if s.sendCompress != "" { - h.Set("Grpc-Encoding", s.sendCompress) - } -} - -func (ht *serverHandlerTransport) Write(s *Stream, hdr []byte, data []byte, opts *Options) error { - return ht.do(func() { - ht.writeCommonHeaders(s) - ht.rw.Write(hdr) - ht.rw.Write(data) - if !opts.Delay { - ht.rw.(http.Flusher).Flush() - } - }) -} - -func (ht *serverHandlerTransport) WriteHeader(s *Stream, md metadata.MD) error { - err := ht.do(func() { - ht.writeCommonHeaders(s) - h := ht.rw.Header() - for k, vv := range md { - // Clients don't tolerate reading restricted headers after some non restricted ones were sent. - if isReservedHeader(k) { - continue - } - for _, v := range vv { - v = encodeMetadataHeader(k, v) - h.Add(k, v) - } - } - ht.rw.WriteHeader(200) - ht.rw.(http.Flusher).Flush() - }) - - if err == nil { - if ht.stats != nil { - ht.stats.HandleRPC(s.Context(), &stats.OutHeader{}) - } - } - return err -} - -func (ht *serverHandlerTransport) HandleStreams(startStream func(*Stream), traceCtx func(context.Context, string) context.Context) { - // With this transport type there will be exactly 1 stream: this HTTP request. - - ctx := contextFromRequest(ht.req) - var cancel context.CancelFunc - if ht.timeoutSet { - ctx, cancel = context.WithTimeout(ctx, ht.timeout) - } else { - ctx, cancel = context.WithCancel(ctx) - } - - // requestOver is closed when either the request's context is done - // or the status has been written via WriteStatus. - requestOver := make(chan struct{}) - - // clientGone receives a single value if peer is gone, either - // because the underlying connection is dead or because the - // peer sends an http2 RST_STREAM. - clientGone := ht.rw.(http.CloseNotifier).CloseNotify() - go func() { - select { - case <-requestOver: - return - case <-ht.closedCh: - case <-clientGone: - } - cancel() - }() - - req := ht.req - - s := &Stream{ - id: 0, // irrelevant - requestRead: func(int) {}, - cancel: cancel, - buf: newRecvBuffer(), - st: ht, - method: req.URL.Path, - recvCompress: req.Header.Get("grpc-encoding"), - contentSubtype: ht.contentSubtype, - } - pr := &peer.Peer{ - Addr: ht.RemoteAddr(), - } - if req.TLS != nil { - pr.AuthInfo = credentials.TLSInfo{State: *req.TLS} - } - ctx = metadata.NewIncomingContext(ctx, ht.headerMD) - s.ctx = peer.NewContext(ctx, pr) - if ht.stats != nil { - s.ctx = ht.stats.TagRPC(s.ctx, &stats.RPCTagInfo{FullMethodName: s.method}) - inHeader := &stats.InHeader{ - FullMethod: s.method, - RemoteAddr: ht.RemoteAddr(), - Compression: s.recvCompress, - } - ht.stats.HandleRPC(s.ctx, inHeader) - } - s.trReader = &transportReader{ - reader: &recvBufferReader{ctx: s.ctx, ctxDone: s.ctx.Done(), recv: s.buf}, - windowHandler: func(int) {}, - } - - // readerDone is closed when the Body.Read-ing goroutine exits. - readerDone := make(chan struct{}) - go func() { - defer close(readerDone) - - // TODO: minimize garbage, optimize recvBuffer code/ownership - const readSize = 8196 - for buf := make([]byte, readSize); ; { - n, err := req.Body.Read(buf) - if n > 0 { - s.buf.put(recvMsg{data: buf[:n:n]}) - buf = buf[n:] - } - if err != nil { - s.buf.put(recvMsg{err: mapRecvMsgError(err)}) - return - } - if len(buf) == 0 { - buf = make([]byte, readSize) - } - } - }() - - // startStream is provided by the *grpc.Server's serveStreams. - // It starts a goroutine serving s and exits immediately. - // The goroutine that is started is the one that then calls - // into ht, calling WriteHeader, Write, WriteStatus, Close, etc. - startStream(s) - - ht.runStream() - close(requestOver) - - // Wait for reading goroutine to finish. - req.Body.Close() - <-readerDone -} - -func (ht *serverHandlerTransport) runStream() { - for { - select { - case fn, ok := <-ht.writes: - if !ok { - return - } - fn() - case <-ht.closedCh: - return - } - } -} - -func (ht *serverHandlerTransport) IncrMsgSent() {} - -func (ht *serverHandlerTransport) IncrMsgRecv() {} - -func (ht *serverHandlerTransport) Drain() { - panic("Drain() is not implemented") -} - -// mapRecvMsgError returns the non-nil err into the appropriate -// error value as expected by callers of *grpc.parser.recvMsg. -// In particular, in can only be: -// * io.EOF -// * io.ErrUnexpectedEOF -// * of type transport.ConnectionError -// * of type transport.StreamError -func mapRecvMsgError(err error) error { - if err == io.EOF || err == io.ErrUnexpectedEOF { - return err - } - if se, ok := err.(http2.StreamError); ok { - if code, ok := http2ErrConvTab[se.Code]; ok { - return StreamError{ - Code: code, - Desc: se.Error(), - } - } - } - return connectionErrorf(true, err, err.Error()) -} diff --git a/vendor/google.golang.org/grpc/transport/http2_client.go b/vendor/google.golang.org/grpc/transport/http2_client.go deleted file mode 100644 index 1fdabd954ef..00000000000 --- a/vendor/google.golang.org/grpc/transport/http2_client.go +++ /dev/null @@ -1,1284 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package transport - -import ( - "io" - "math" - "net" - "strings" - "sync" - "sync/atomic" - "time" - - "golang.org/x/net/context" - "golang.org/x/net/http2" - "golang.org/x/net/http2/hpack" - - "google.golang.org/grpc/channelz" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/keepalive" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/peer" - "google.golang.org/grpc/stats" - "google.golang.org/grpc/status" -) - -// http2Client implements the ClientTransport interface with HTTP2. -type http2Client struct { - ctx context.Context - cancel context.CancelFunc - ctxDone <-chan struct{} // Cache the ctx.Done() chan. - userAgent string - md interface{} - conn net.Conn // underlying communication channel - loopy *loopyWriter - remoteAddr net.Addr - localAddr net.Addr - authInfo credentials.AuthInfo // auth info about the connection - - readerDone chan struct{} // sync point to enable testing. - writerDone chan struct{} // sync point to enable testing. - // goAway is closed to notify the upper layer (i.e., addrConn.transportMonitor) - // that the server sent GoAway on this transport. - goAway chan struct{} - // awakenKeepalive is used to wake up keepalive when after it has gone dormant. - awakenKeepalive chan struct{} - - framer *framer - // controlBuf delivers all the control related tasks (e.g., window - // updates, reset streams, and various settings) to the controller. - controlBuf *controlBuffer - fc *trInFlow - // The scheme used: https if TLS is on, http otherwise. - scheme string - - isSecure bool - - creds []credentials.PerRPCCredentials - - // Boolean to keep track of reading activity on transport. - // 1 is true and 0 is false. - activity uint32 // Accessed atomically. - kp keepalive.ClientParameters - - statsHandler stats.Handler - - initialWindowSize int32 - - bdpEst *bdpEstimator - // onSuccess is a callback that client transport calls upon - // receiving server preface to signal that a succefull HTTP2 - // connection was established. - onSuccess func() - - maxConcurrentStreams uint32 - streamQuota int64 - streamsQuotaAvailable chan struct{} - waitingStreams uint32 - nextID uint32 - - mu sync.Mutex // guard the following variables - state transportState - activeStreams map[uint32]*Stream - // prevGoAway ID records the Last-Stream-ID in the previous GOAway frame. - prevGoAwayID uint32 - // goAwayReason records the http2.ErrCode and debug data received with the - // GoAway frame. - goAwayReason GoAwayReason - - // Fields below are for channelz metric collection. - channelzID int64 // channelz unique identification number - czmu sync.RWMutex - kpCount int64 - // The number of streams that have started, including already finished ones. - streamsStarted int64 - // The number of streams that have ended successfully by receiving EoS bit set - // frame from server. - streamsSucceeded int64 - streamsFailed int64 - lastStreamCreated time.Time - msgSent int64 - msgRecv int64 - lastMsgSent time.Time - lastMsgRecv time.Time -} - -func dial(ctx context.Context, fn func(context.Context, string) (net.Conn, error), addr string) (net.Conn, error) { - if fn != nil { - return fn(ctx, addr) - } - return dialContext(ctx, "tcp", addr) -} - -func isTemporary(err error) bool { - switch err := err.(type) { - case interface { - Temporary() bool - }: - return err.Temporary() - case interface { - Timeout() bool - }: - // Timeouts may be resolved upon retry, and are thus treated as - // temporary. - return err.Timeout() - } - return true -} - -// newHTTP2Client constructs a connected ClientTransport to addr based on HTTP2 -// and starts to receive messages on it. Non-nil error returns if construction -// fails. -func newHTTP2Client(connectCtx, ctx context.Context, addr TargetInfo, opts ConnectOptions, onSuccess func()) (_ ClientTransport, err error) { - scheme := "http" - ctx, cancel := context.WithCancel(ctx) - defer func() { - if err != nil { - cancel() - } - }() - - conn, err := dial(connectCtx, opts.Dialer, addr.Addr) - if err != nil { - if opts.FailOnNonTempDialError { - return nil, connectionErrorf(isTemporary(err), err, "transport: error while dialing: %v", err) - } - return nil, connectionErrorf(true, err, "transport: Error while dialing %v", err) - } - // Any further errors will close the underlying connection - defer func(conn net.Conn) { - if err != nil { - conn.Close() - } - }(conn) - var ( - isSecure bool - authInfo credentials.AuthInfo - ) - if creds := opts.TransportCredentials; creds != nil { - scheme = "https" - conn, authInfo, err = creds.ClientHandshake(connectCtx, addr.Authority, conn) - if err != nil { - return nil, connectionErrorf(isTemporary(err), err, "transport: authentication handshake failed: %v", err) - } - isSecure = true - } - kp := opts.KeepaliveParams - // Validate keepalive parameters. - if kp.Time == 0 { - kp.Time = defaultClientKeepaliveTime - } - if kp.Timeout == 0 { - kp.Timeout = defaultClientKeepaliveTimeout - } - dynamicWindow := true - icwz := int32(initialWindowSize) - if opts.InitialConnWindowSize >= defaultWindowSize { - icwz = opts.InitialConnWindowSize - dynamicWindow = false - } - writeBufSize := defaultWriteBufSize - if opts.WriteBufferSize > 0 { - writeBufSize = opts.WriteBufferSize - } - readBufSize := defaultReadBufSize - if opts.ReadBufferSize > 0 { - readBufSize = opts.ReadBufferSize - } - t := &http2Client{ - ctx: ctx, - ctxDone: ctx.Done(), // Cache Done chan. - cancel: cancel, - userAgent: opts.UserAgent, - md: addr.Metadata, - conn: conn, - remoteAddr: conn.RemoteAddr(), - localAddr: conn.LocalAddr(), - authInfo: authInfo, - readerDone: make(chan struct{}), - writerDone: make(chan struct{}), - goAway: make(chan struct{}), - awakenKeepalive: make(chan struct{}, 1), - framer: newFramer(conn, writeBufSize, readBufSize), - fc: &trInFlow{limit: uint32(icwz)}, - scheme: scheme, - activeStreams: make(map[uint32]*Stream), - isSecure: isSecure, - creds: opts.PerRPCCredentials, - kp: kp, - statsHandler: opts.StatsHandler, - initialWindowSize: initialWindowSize, - onSuccess: onSuccess, - nextID: 1, - maxConcurrentStreams: defaultMaxStreamsClient, - streamQuota: defaultMaxStreamsClient, - streamsQuotaAvailable: make(chan struct{}, 1), - } - t.controlBuf = newControlBuffer(t.ctxDone) - if opts.InitialWindowSize >= defaultWindowSize { - t.initialWindowSize = opts.InitialWindowSize - dynamicWindow = false - } - if dynamicWindow { - t.bdpEst = &bdpEstimator{ - bdp: initialWindowSize, - updateFlowControl: t.updateFlowControl, - } - } - // Make sure awakenKeepalive can't be written upon. - // keepalive routine will make it writable, if need be. - t.awakenKeepalive <- struct{}{} - if t.statsHandler != nil { - t.ctx = t.statsHandler.TagConn(t.ctx, &stats.ConnTagInfo{ - RemoteAddr: t.remoteAddr, - LocalAddr: t.localAddr, - }) - connBegin := &stats.ConnBegin{ - Client: true, - } - t.statsHandler.HandleConn(t.ctx, connBegin) - } - if channelz.IsOn() { - t.channelzID = channelz.RegisterNormalSocket(t, opts.ChannelzParentID, "") - } - // Start the reader goroutine for incoming message. Each transport has - // a dedicated goroutine which reads HTTP2 frame from network. Then it - // dispatches the frame to the corresponding stream entity. - go t.reader() - // Send connection preface to server. - n, err := t.conn.Write(clientPreface) - if err != nil { - t.Close() - return nil, connectionErrorf(true, err, "transport: failed to write client preface: %v", err) - } - if n != len(clientPreface) { - t.Close() - return nil, connectionErrorf(true, err, "transport: preface mismatch, wrote %d bytes; want %d", n, len(clientPreface)) - } - if t.initialWindowSize != defaultWindowSize { - err = t.framer.fr.WriteSettings(http2.Setting{ - ID: http2.SettingInitialWindowSize, - Val: uint32(t.initialWindowSize), - }) - } else { - err = t.framer.fr.WriteSettings() - } - if err != nil { - t.Close() - return nil, connectionErrorf(true, err, "transport: failed to write initial settings frame: %v", err) - } - // Adjust the connection flow control window if needed. - if delta := uint32(icwz - defaultWindowSize); delta > 0 { - if err := t.framer.fr.WriteWindowUpdate(0, delta); err != nil { - t.Close() - return nil, connectionErrorf(true, err, "transport: failed to write window update: %v", err) - } - } - t.framer.writer.Flush() - go func() { - t.loopy = newLoopyWriter(clientSide, t.framer, t.controlBuf, t.bdpEst) - t.loopy.run() - t.conn.Close() - close(t.writerDone) - }() - if t.kp.Time != infinity { - go t.keepalive() - } - return t, nil -} - -func (t *http2Client) newStream(ctx context.Context, callHdr *CallHdr) *Stream { - // TODO(zhaoq): Handle uint32 overflow of Stream.id. - s := &Stream{ - done: make(chan struct{}), - method: callHdr.Method, - sendCompress: callHdr.SendCompress, - buf: newRecvBuffer(), - headerChan: make(chan struct{}), - contentSubtype: callHdr.ContentSubtype, - } - s.wq = newWriteQuota(defaultWriteQuota, s.done) - s.requestRead = func(n int) { - t.adjustWindow(s, uint32(n)) - } - // The client side stream context should have exactly the same life cycle with the user provided context. - // That means, s.ctx should be read-only. And s.ctx is done iff ctx is done. - // So we use the original context here instead of creating a copy. - s.ctx = ctx - s.trReader = &transportReader{ - reader: &recvBufferReader{ - ctx: s.ctx, - ctxDone: s.ctx.Done(), - recv: s.buf, - }, - windowHandler: func(n int) { - t.updateWindow(s, uint32(n)) - }, - } - return s -} - -func (t *http2Client) getPeer() *peer.Peer { - pr := &peer.Peer{ - Addr: t.remoteAddr, - } - // Attach Auth info if there is any. - if t.authInfo != nil { - pr.AuthInfo = t.authInfo - } - return pr -} - -func (t *http2Client) createHeaderFields(ctx context.Context, callHdr *CallHdr) ([]hpack.HeaderField, error) { - aud := t.createAudience(callHdr) - authData, err := t.getTrAuthData(ctx, aud) - if err != nil { - return nil, err - } - callAuthData, err := t.getCallAuthData(ctx, aud, callHdr) - if err != nil { - return nil, err - } - // TODO(mmukhi): Benchmark if the performance gets better if count the metadata and other header fields - // first and create a slice of that exact size. - // Make the slice of certain predictable size to reduce allocations made by append. - hfLen := 7 // :method, :scheme, :path, :authority, content-type, user-agent, te - hfLen += len(authData) + len(callAuthData) - headerFields := make([]hpack.HeaderField, 0, hfLen) - headerFields = append(headerFields, hpack.HeaderField{Name: ":method", Value: "POST"}) - headerFields = append(headerFields, hpack.HeaderField{Name: ":scheme", Value: t.scheme}) - headerFields = append(headerFields, hpack.HeaderField{Name: ":path", Value: callHdr.Method}) - headerFields = append(headerFields, hpack.HeaderField{Name: ":authority", Value: callHdr.Host}) - headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: contentType(callHdr.ContentSubtype)}) - headerFields = append(headerFields, hpack.HeaderField{Name: "user-agent", Value: t.userAgent}) - headerFields = append(headerFields, hpack.HeaderField{Name: "te", Value: "trailers"}) - - if callHdr.SendCompress != "" { - headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: callHdr.SendCompress}) - } - if dl, ok := ctx.Deadline(); ok { - // Send out timeout regardless its value. The server can detect timeout context by itself. - // TODO(mmukhi): Perhaps this field should be updated when actually writing out to the wire. - timeout := dl.Sub(time.Now()) - headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-timeout", Value: encodeTimeout(timeout)}) - } - for k, v := range authData { - headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)}) - } - for k, v := range callAuthData { - headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)}) - } - if b := stats.OutgoingTags(ctx); b != nil { - headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-tags-bin", Value: encodeBinHeader(b)}) - } - if b := stats.OutgoingTrace(ctx); b != nil { - headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-trace-bin", Value: encodeBinHeader(b)}) - } - - if md, added, ok := metadata.FromOutgoingContextRaw(ctx); ok { - var k string - for _, vv := range added { - for i, v := range vv { - if i%2 == 0 { - k = v - continue - } - // HTTP doesn't allow you to set pseudoheaders after non pseudoheaders were set. - if isReservedHeader(k) { - continue - } - headerFields = append(headerFields, hpack.HeaderField{Name: strings.ToLower(k), Value: encodeMetadataHeader(k, v)}) - } - } - for k, vv := range md { - // HTTP doesn't allow you to set pseudoheaders after non pseudoheaders were set. - if isReservedHeader(k) { - continue - } - for _, v := range vv { - headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)}) - } - } - } - if md, ok := t.md.(*metadata.MD); ok { - for k, vv := range *md { - if isReservedHeader(k) { - continue - } - for _, v := range vv { - headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)}) - } - } - } - return headerFields, nil -} - -func (t *http2Client) createAudience(callHdr *CallHdr) string { - // Create an audience string only if needed. - if len(t.creds) == 0 && callHdr.Creds == nil { - return "" - } - // Construct URI required to get auth request metadata. - // Omit port if it is the default one. - host := strings.TrimSuffix(callHdr.Host, ":443") - pos := strings.LastIndex(callHdr.Method, "/") - if pos == -1 { - pos = len(callHdr.Method) - } - return "https://" + host + callHdr.Method[:pos] -} - -func (t *http2Client) getTrAuthData(ctx context.Context, audience string) (map[string]string, error) { - authData := map[string]string{} - for _, c := range t.creds { - data, err := c.GetRequestMetadata(ctx, audience) - if err != nil { - if _, ok := status.FromError(err); ok { - return nil, err - } - - return nil, streamErrorf(codes.Unauthenticated, "transport: %v", err) - } - for k, v := range data { - // Capital header names are illegal in HTTP/2. - k = strings.ToLower(k) - authData[k] = v - } - } - return authData, nil -} - -func (t *http2Client) getCallAuthData(ctx context.Context, audience string, callHdr *CallHdr) (map[string]string, error) { - callAuthData := map[string]string{} - // Check if credentials.PerRPCCredentials were provided via call options. - // Note: if these credentials are provided both via dial options and call - // options, then both sets of credentials will be applied. - if callCreds := callHdr.Creds; callCreds != nil { - if !t.isSecure && callCreds.RequireTransportSecurity() { - return nil, streamErrorf(codes.Unauthenticated, "transport: cannot send secure credentials on an insecure connection") - } - data, err := callCreds.GetRequestMetadata(ctx, audience) - if err != nil { - return nil, streamErrorf(codes.Internal, "transport: %v", err) - } - for k, v := range data { - // Capital header names are illegal in HTTP/2 - k = strings.ToLower(k) - callAuthData[k] = v - } - } - return callAuthData, nil -} - -// NewStream creates a stream and registers it into the transport as "active" -// streams. -func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr) (_ *Stream, err error) { - ctx = peer.NewContext(ctx, t.getPeer()) - headerFields, err := t.createHeaderFields(ctx, callHdr) - if err != nil { - return nil, err - } - s := t.newStream(ctx, callHdr) - cleanup := func(err error) { - if s.swapState(streamDone) == streamDone { - // If it was already done, return. - return - } - // The stream was unprocessed by the server. - atomic.StoreUint32(&s.unprocessed, 1) - s.write(recvMsg{err: err}) - close(s.done) - // If headerChan isn't closed, then close it. - if atomic.SwapUint32(&s.headerDone, 1) == 0 { - close(s.headerChan) - } - - } - hdr := &headerFrame{ - hf: headerFields, - endStream: false, - initStream: func(id uint32) (bool, error) { - t.mu.Lock() - if state := t.state; state != reachable { - t.mu.Unlock() - // Do a quick cleanup. - err := error(errStreamDrain) - if state == closing { - err = ErrConnClosing - } - cleanup(err) - return false, err - } - t.activeStreams[id] = s - if channelz.IsOn() { - t.czmu.Lock() - t.streamsStarted++ - t.lastStreamCreated = time.Now() - t.czmu.Unlock() - } - var sendPing bool - // If the number of active streams change from 0 to 1, then check if keepalive - // has gone dormant. If so, wake it up. - if len(t.activeStreams) == 1 { - select { - case t.awakenKeepalive <- struct{}{}: - sendPing = true - // Fill the awakenKeepalive channel again as this channel must be - // kept non-writable except at the point that the keepalive() - // goroutine is waiting either to be awaken or shutdown. - t.awakenKeepalive <- struct{}{} - default: - } - } - t.mu.Unlock() - return sendPing, nil - }, - onOrphaned: cleanup, - wq: s.wq, - } - firstTry := true - var ch chan struct{} - checkForStreamQuota := func(it interface{}) bool { - if t.streamQuota <= 0 { // Can go negative if server decreases it. - if firstTry { - t.waitingStreams++ - } - ch = t.streamsQuotaAvailable - return false - } - if !firstTry { - t.waitingStreams-- - } - t.streamQuota-- - h := it.(*headerFrame) - h.streamID = t.nextID - t.nextID += 2 - s.id = h.streamID - s.fc = &inFlow{limit: uint32(t.initialWindowSize)} - if t.streamQuota > 0 && t.waitingStreams > 0 { - select { - case t.streamsQuotaAvailable <- struct{}{}: - default: - } - } - return true - } - for { - success, err := t.controlBuf.executeAndPut(checkForStreamQuota, hdr) - if err != nil { - return nil, err - } - if success { - break - } - firstTry = false - select { - case <-ch: - case <-s.ctx.Done(): - return nil, ContextErr(s.ctx.Err()) - case <-t.goAway: - return nil, errStreamDrain - case <-t.ctx.Done(): - return nil, ErrConnClosing - } - } - if t.statsHandler != nil { - outHeader := &stats.OutHeader{ - Client: true, - FullMethod: callHdr.Method, - RemoteAddr: t.remoteAddr, - LocalAddr: t.localAddr, - Compression: callHdr.SendCompress, - } - t.statsHandler.HandleRPC(s.ctx, outHeader) - } - return s, nil -} - -// CloseStream clears the footprint of a stream when the stream is not needed any more. -// This must not be executed in reader's goroutine. -func (t *http2Client) CloseStream(s *Stream, err error) { - var ( - rst bool - rstCode http2.ErrCode - ) - if err != nil { - rst = true - rstCode = http2.ErrCodeCancel - } - t.closeStream(s, err, rst, rstCode, nil, nil, false) -} - -func (t *http2Client) closeStream(s *Stream, err error, rst bool, rstCode http2.ErrCode, st *status.Status, mdata map[string][]string, eosReceived bool) { - // Set stream status to done. - if s.swapState(streamDone) == streamDone { - // If it was already done, return. - return - } - // status and trailers can be updated here without any synchronization because the stream goroutine will - // only read it after it sees an io.EOF error from read or write and we'll write those errors - // only after updating this. - s.status = st - if len(mdata) > 0 { - s.trailer = mdata - } - if err != nil { - // This will unblock reads eventually. - s.write(recvMsg{err: err}) - } - // This will unblock write. - close(s.done) - // If headerChan isn't closed, then close it. - if atomic.SwapUint32(&s.headerDone, 1) == 0 { - close(s.headerChan) - } - cleanup := &cleanupStream{ - streamID: s.id, - onWrite: func() { - t.mu.Lock() - if t.activeStreams != nil { - delete(t.activeStreams, s.id) - } - t.mu.Unlock() - if channelz.IsOn() { - t.czmu.Lock() - if eosReceived { - t.streamsSucceeded++ - } else { - t.streamsFailed++ - } - t.czmu.Unlock() - } - }, - rst: rst, - rstCode: rstCode, - } - addBackStreamQuota := func(interface{}) bool { - t.streamQuota++ - if t.streamQuota > 0 && t.waitingStreams > 0 { - select { - case t.streamsQuotaAvailable <- struct{}{}: - default: - } - } - return true - } - t.controlBuf.executeAndPut(addBackStreamQuota, cleanup) -} - -// Close kicks off the shutdown process of the transport. This should be called -// only once on a transport. Once it is called, the transport should not be -// accessed any more. -func (t *http2Client) Close() error { - t.mu.Lock() - // Make sure we only Close once. - if t.state == closing { - t.mu.Unlock() - return nil - } - t.state = closing - streams := t.activeStreams - t.activeStreams = nil - t.mu.Unlock() - t.controlBuf.finish() - t.cancel() - err := t.conn.Close() - if channelz.IsOn() { - channelz.RemoveEntry(t.channelzID) - } - // Notify all active streams. - for _, s := range streams { - t.closeStream(s, ErrConnClosing, false, http2.ErrCodeNo, nil, nil, false) - } - if t.statsHandler != nil { - connEnd := &stats.ConnEnd{ - Client: true, - } - t.statsHandler.HandleConn(t.ctx, connEnd) - } - return err -} - -// GracefulClose sets the state to draining, which prevents new streams from -// being created and causes the transport to be closed when the last active -// stream is closed. If there are no active streams, the transport is closed -// immediately. This does nothing if the transport is already draining or -// closing. -func (t *http2Client) GracefulClose() error { - t.mu.Lock() - // Make sure we move to draining only from active. - if t.state == draining || t.state == closing { - t.mu.Unlock() - return nil - } - t.state = draining - active := len(t.activeStreams) - t.mu.Unlock() - if active == 0 { - return t.Close() - } - return nil -} - -// Write formats the data into HTTP2 data frame(s) and sends it out. The caller -// should proceed only if Write returns nil. -func (t *http2Client) Write(s *Stream, hdr []byte, data []byte, opts *Options) error { - if opts.Last { - // If it's the last message, update stream state. - if !s.compareAndSwapState(streamActive, streamWriteDone) { - return errStreamDone - } - } else if s.getState() != streamActive { - return errStreamDone - } - df := &dataFrame{ - streamID: s.id, - endStream: opts.Last, - } - if hdr != nil || data != nil { // If it's not an empty data frame. - // Add some data to grpc message header so that we can equally - // distribute bytes across frames. - emptyLen := http2MaxFrameLen - len(hdr) - if emptyLen > len(data) { - emptyLen = len(data) - } - hdr = append(hdr, data[:emptyLen]...) - data = data[emptyLen:] - df.h, df.d = hdr, data - // TODO(mmukhi): The above logic in this if can be moved to loopyWriter's data handler. - if err := s.wq.get(int32(len(hdr) + len(data))); err != nil { - return err - } - } - return t.controlBuf.put(df) -} - -func (t *http2Client) getStream(f http2.Frame) (*Stream, bool) { - t.mu.Lock() - defer t.mu.Unlock() - s, ok := t.activeStreams[f.Header().StreamID] - return s, ok -} - -// adjustWindow sends out extra window update over the initial window size -// of stream if the application is requesting data larger in size than -// the window. -func (t *http2Client) adjustWindow(s *Stream, n uint32) { - if w := s.fc.maybeAdjust(n); w > 0 { - t.controlBuf.put(&outgoingWindowUpdate{streamID: s.id, increment: w}) - } -} - -// updateWindow adjusts the inbound quota for the stream. -// Window updates will be sent out when the cumulative quota -// exceeds the corresponding threshold. -func (t *http2Client) updateWindow(s *Stream, n uint32) { - if w := s.fc.onRead(n); w > 0 { - t.controlBuf.put(&outgoingWindowUpdate{streamID: s.id, increment: w}) - } -} - -// updateFlowControl updates the incoming flow control windows -// for the transport and the stream based on the current bdp -// estimation. -func (t *http2Client) updateFlowControl(n uint32) { - t.mu.Lock() - for _, s := range t.activeStreams { - s.fc.newLimit(n) - } - t.mu.Unlock() - updateIWS := func(interface{}) bool { - t.initialWindowSize = int32(n) - return true - } - t.controlBuf.executeAndPut(updateIWS, &outgoingWindowUpdate{streamID: 0, increment: t.fc.newLimit(n)}) - t.controlBuf.put(&outgoingSettings{ - ss: []http2.Setting{ - { - ID: http2.SettingInitialWindowSize, - Val: n, - }, - }, - }) -} - -func (t *http2Client) handleData(f *http2.DataFrame) { - size := f.Header().Length - var sendBDPPing bool - if t.bdpEst != nil { - sendBDPPing = t.bdpEst.add(size) - } - // Decouple connection's flow control from application's read. - // An update on connection's flow control should not depend on - // whether user application has read the data or not. Such a - // restriction is already imposed on the stream's flow control, - // and therefore the sender will be blocked anyways. - // Decoupling the connection flow control will prevent other - // active(fast) streams from starving in presence of slow or - // inactive streams. - // - if w := t.fc.onData(size); w > 0 { - t.controlBuf.put(&outgoingWindowUpdate{ - streamID: 0, - increment: w, - }) - } - if sendBDPPing { - // Avoid excessive ping detection (e.g. in an L7 proxy) - // by sending a window update prior to the BDP ping. - - if w := t.fc.reset(); w > 0 { - t.controlBuf.put(&outgoingWindowUpdate{ - streamID: 0, - increment: w, - }) - } - - t.controlBuf.put(bdpPing) - } - // Select the right stream to dispatch. - s, ok := t.getStream(f) - if !ok { - return - } - if size > 0 { - if err := s.fc.onData(size); err != nil { - t.closeStream(s, io.EOF, true, http2.ErrCodeFlowControl, status.New(codes.Internal, err.Error()), nil, false) - return - } - if f.Header().Flags.Has(http2.FlagDataPadded) { - if w := s.fc.onRead(size - uint32(len(f.Data()))); w > 0 { - t.controlBuf.put(&outgoingWindowUpdate{s.id, w}) - } - } - // TODO(bradfitz, zhaoq): A copy is required here because there is no - // guarantee f.Data() is consumed before the arrival of next frame. - // Can this copy be eliminated? - if len(f.Data()) > 0 { - data := make([]byte, len(f.Data())) - copy(data, f.Data()) - s.write(recvMsg{data: data}) - } - } - // The server has closed the stream without sending trailers. Record that - // the read direction is closed, and set the status appropriately. - if f.FrameHeader.Flags.Has(http2.FlagDataEndStream) { - t.closeStream(s, io.EOF, false, http2.ErrCodeNo, status.New(codes.Internal, "server closed the stream without sending trailers"), nil, true) - } -} - -func (t *http2Client) handleRSTStream(f *http2.RSTStreamFrame) { - s, ok := t.getStream(f) - if !ok { - return - } - if f.ErrCode == http2.ErrCodeRefusedStream { - // The stream was unprocessed by the server. - atomic.StoreUint32(&s.unprocessed, 1) - } - statusCode, ok := http2ErrConvTab[f.ErrCode] - if !ok { - warningf("transport: http2Client.handleRSTStream found no mapped gRPC status for the received http2 error %v", f.ErrCode) - statusCode = codes.Unknown - } - t.closeStream(s, io.EOF, false, http2.ErrCodeNo, status.Newf(statusCode, "stream terminated by RST_STREAM with error code: %v", f.ErrCode), nil, false) -} - -func (t *http2Client) handleSettings(f *http2.SettingsFrame, isFirst bool) { - if f.IsAck() { - return - } - var maxStreams *uint32 - var ss []http2.Setting - f.ForeachSetting(func(s http2.Setting) error { - if s.ID == http2.SettingMaxConcurrentStreams { - maxStreams = new(uint32) - *maxStreams = s.Val - return nil - } - ss = append(ss, s) - return nil - }) - if isFirst && maxStreams == nil { - maxStreams = new(uint32) - *maxStreams = math.MaxUint32 - } - sf := &incomingSettings{ - ss: ss, - } - if maxStreams == nil { - t.controlBuf.put(sf) - return - } - updateStreamQuota := func(interface{}) bool { - delta := int64(*maxStreams) - int64(t.maxConcurrentStreams) - t.maxConcurrentStreams = *maxStreams - t.streamQuota += delta - if delta > 0 && t.waitingStreams > 0 { - close(t.streamsQuotaAvailable) // wake all of them up. - t.streamsQuotaAvailable = make(chan struct{}, 1) - } - return true - } - t.controlBuf.executeAndPut(updateStreamQuota, sf) -} - -func (t *http2Client) handlePing(f *http2.PingFrame) { - if f.IsAck() { - // Maybe it's a BDP ping. - if t.bdpEst != nil { - t.bdpEst.calculate(f.Data) - } - return - } - pingAck := &ping{ack: true} - copy(pingAck.data[:], f.Data[:]) - t.controlBuf.put(pingAck) -} - -func (t *http2Client) handleGoAway(f *http2.GoAwayFrame) { - t.mu.Lock() - if t.state == closing { - t.mu.Unlock() - return - } - if f.ErrCode == http2.ErrCodeEnhanceYourCalm { - infof("Client received GoAway with http2.ErrCodeEnhanceYourCalm.") - } - id := f.LastStreamID - if id > 0 && id%2 != 1 { - t.mu.Unlock() - t.Close() - return - } - // A client can receive multiple GoAways from the server (see - // https://github.com/grpc/grpc-go/issues/1387). The idea is that the first - // GoAway will be sent with an ID of MaxInt32 and the second GoAway will be - // sent after an RTT delay with the ID of the last stream the server will - // process. - // - // Therefore, when we get the first GoAway we don't necessarily close any - // streams. While in case of second GoAway we close all streams created after - // the GoAwayId. This way streams that were in-flight while the GoAway from - // server was being sent don't get killed. - select { - case <-t.goAway: // t.goAway has been closed (i.e.,multiple GoAways). - // If there are multiple GoAways the first one should always have an ID greater than the following ones. - if id > t.prevGoAwayID { - t.mu.Unlock() - t.Close() - return - } - default: - t.setGoAwayReason(f) - close(t.goAway) - t.state = draining - t.controlBuf.put(&incomingGoAway{}) - } - // All streams with IDs greater than the GoAwayId - // and smaller than the previous GoAway ID should be killed. - upperLimit := t.prevGoAwayID - if upperLimit == 0 { // This is the first GoAway Frame. - upperLimit = math.MaxUint32 // Kill all streams after the GoAway ID. - } - for streamID, stream := range t.activeStreams { - if streamID > id && streamID <= upperLimit { - // The stream was unprocessed by the server. - atomic.StoreUint32(&stream.unprocessed, 1) - t.closeStream(stream, errStreamDrain, false, http2.ErrCodeNo, statusGoAway, nil, false) - } - } - t.prevGoAwayID = id - active := len(t.activeStreams) - t.mu.Unlock() - if active == 0 { - t.Close() - } -} - -// setGoAwayReason sets the value of t.goAwayReason based -// on the GoAway frame received. -// It expects a lock on transport's mutext to be held by -// the caller. -func (t *http2Client) setGoAwayReason(f *http2.GoAwayFrame) { - t.goAwayReason = GoAwayNoReason - switch f.ErrCode { - case http2.ErrCodeEnhanceYourCalm: - if string(f.DebugData()) == "too_many_pings" { - t.goAwayReason = GoAwayTooManyPings - } - } -} - -func (t *http2Client) GetGoAwayReason() GoAwayReason { - t.mu.Lock() - defer t.mu.Unlock() - return t.goAwayReason -} - -func (t *http2Client) handleWindowUpdate(f *http2.WindowUpdateFrame) { - t.controlBuf.put(&incomingWindowUpdate{ - streamID: f.Header().StreamID, - increment: f.Increment, - }) -} - -// operateHeaders takes action on the decoded headers. -func (t *http2Client) operateHeaders(frame *http2.MetaHeadersFrame) { - s, ok := t.getStream(frame) - if !ok { - return - } - atomic.StoreUint32(&s.bytesReceived, 1) - var state decodeState - if err := state.decodeResponseHeader(frame); err != nil { - t.closeStream(s, err, true, http2.ErrCodeProtocol, nil, nil, false) - // Something wrong. Stops reading even when there is remaining. - return - } - - endStream := frame.StreamEnded() - var isHeader bool - defer func() { - if t.statsHandler != nil { - if isHeader { - inHeader := &stats.InHeader{ - Client: true, - WireLength: int(frame.Header().Length), - } - t.statsHandler.HandleRPC(s.ctx, inHeader) - } else { - inTrailer := &stats.InTrailer{ - Client: true, - WireLength: int(frame.Header().Length), - } - t.statsHandler.HandleRPC(s.ctx, inTrailer) - } - } - }() - // If headers haven't been received yet. - if atomic.SwapUint32(&s.headerDone, 1) == 0 { - if !endStream { - // Headers frame is not actually a trailers-only frame. - isHeader = true - // These values can be set without any synchronization because - // stream goroutine will read it only after seeing a closed - // headerChan which we'll close after setting this. - s.recvCompress = state.encoding - if len(state.mdata) > 0 { - s.header = state.mdata - } - } - close(s.headerChan) - } - if !endStream { - return - } - t.closeStream(s, io.EOF, false, http2.ErrCodeNo, state.status(), state.mdata, true) -} - -// reader runs as a separate goroutine in charge of reading data from network -// connection. -// -// TODO(zhaoq): currently one reader per transport. Investigate whether this is -// optimal. -// TODO(zhaoq): Check the validity of the incoming frame sequence. -func (t *http2Client) reader() { - defer close(t.readerDone) - // Check the validity of server preface. - frame, err := t.framer.fr.ReadFrame() - if err != nil { - t.Close() - return - } - atomic.CompareAndSwapUint32(&t.activity, 0, 1) - sf, ok := frame.(*http2.SettingsFrame) - if !ok { - t.Close() - return - } - t.onSuccess() - t.handleSettings(sf, true) - - // loop to keep reading incoming messages on this transport. - for { - frame, err := t.framer.fr.ReadFrame() - atomic.CompareAndSwapUint32(&t.activity, 0, 1) - if err != nil { - // Abort an active stream if the http2.Framer returns a - // http2.StreamError. This can happen only if the server's response - // is malformed http2. - if se, ok := err.(http2.StreamError); ok { - t.mu.Lock() - s := t.activeStreams[se.StreamID] - t.mu.Unlock() - if s != nil { - // use error detail to provide better err message - t.closeStream(s, streamErrorf(http2ErrConvTab[se.Code], "%v", t.framer.fr.ErrorDetail()), true, http2.ErrCodeProtocol, nil, nil, false) - } - continue - } else { - // Transport error. - t.Close() - return - } - } - switch frame := frame.(type) { - case *http2.MetaHeadersFrame: - t.operateHeaders(frame) - case *http2.DataFrame: - t.handleData(frame) - case *http2.RSTStreamFrame: - t.handleRSTStream(frame) - case *http2.SettingsFrame: - t.handleSettings(frame, false) - case *http2.PingFrame: - t.handlePing(frame) - case *http2.GoAwayFrame: - t.handleGoAway(frame) - case *http2.WindowUpdateFrame: - t.handleWindowUpdate(frame) - default: - errorf("transport: http2Client.reader got unhandled frame type %v.", frame) - } - } -} - -// keepalive running in a separate goroutune makes sure the connection is alive by sending pings. -func (t *http2Client) keepalive() { - p := &ping{data: [8]byte{}} - timer := time.NewTimer(t.kp.Time) - for { - select { - case <-timer.C: - if atomic.CompareAndSwapUint32(&t.activity, 1, 0) { - timer.Reset(t.kp.Time) - continue - } - // Check if keepalive should go dormant. - t.mu.Lock() - if len(t.activeStreams) < 1 && !t.kp.PermitWithoutStream { - // Make awakenKeepalive writable. - <-t.awakenKeepalive - t.mu.Unlock() - select { - case <-t.awakenKeepalive: - // If the control gets here a ping has been sent - // need to reset the timer with keepalive.Timeout. - case <-t.ctx.Done(): - return - } - } else { - t.mu.Unlock() - if channelz.IsOn() { - t.czmu.Lock() - t.kpCount++ - t.czmu.Unlock() - } - // Send ping. - t.controlBuf.put(p) - } - - // By the time control gets here a ping has been sent one way or the other. - timer.Reset(t.kp.Timeout) - select { - case <-timer.C: - if atomic.CompareAndSwapUint32(&t.activity, 1, 0) { - timer.Reset(t.kp.Time) - continue - } - t.Close() - return - case <-t.ctx.Done(): - if !timer.Stop() { - <-timer.C - } - return - } - case <-t.ctx.Done(): - if !timer.Stop() { - <-timer.C - } - return - } - } -} - -func (t *http2Client) Error() <-chan struct{} { - return t.ctx.Done() -} - -func (t *http2Client) GoAway() <-chan struct{} { - return t.goAway -} - -func (t *http2Client) ChannelzMetric() *channelz.SocketInternalMetric { - t.czmu.RLock() - s := channelz.SocketInternalMetric{ - StreamsStarted: t.streamsStarted, - StreamsSucceeded: t.streamsSucceeded, - StreamsFailed: t.streamsFailed, - MessagesSent: t.msgSent, - MessagesReceived: t.msgRecv, - KeepAlivesSent: t.kpCount, - LastLocalStreamCreatedTimestamp: t.lastStreamCreated, - LastMessageSentTimestamp: t.lastMsgSent, - LastMessageReceivedTimestamp: t.lastMsgRecv, - LocalFlowControlWindow: int64(t.fc.getSize()), - //socket options - LocalAddr: t.localAddr, - RemoteAddr: t.remoteAddr, - // Security - // RemoteName : - } - t.czmu.RUnlock() - s.RemoteFlowControlWindow = t.getOutFlowWindow() - return &s -} - -func (t *http2Client) IncrMsgSent() { - t.czmu.Lock() - t.msgSent++ - t.lastMsgSent = time.Now() - t.czmu.Unlock() -} - -func (t *http2Client) IncrMsgRecv() { - t.czmu.Lock() - t.msgRecv++ - t.lastMsgRecv = time.Now() - t.czmu.Unlock() -} - -func (t *http2Client) getOutFlowWindow() int64 { - resp := make(chan uint32, 1) - timer := time.NewTimer(time.Second) - defer timer.Stop() - t.controlBuf.put(&outFlowControlSizeRequest{resp}) - select { - case sz := <-resp: - return int64(sz) - case <-t.ctxDone: - return -1 - case <-timer.C: - return -2 - } -} diff --git a/vendor/google.golang.org/grpc/transport/http2_server.go b/vendor/google.golang.org/grpc/transport/http2_server.go deleted file mode 100644 index 8b93e222e9e..00000000000 --- a/vendor/google.golang.org/grpc/transport/http2_server.go +++ /dev/null @@ -1,1136 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package transport - -import ( - "bytes" - "errors" - "fmt" - "io" - "math" - "math/rand" - "net" - "strconv" - "sync" - "sync/atomic" - "time" - - "github.com/golang/protobuf/proto" - "golang.org/x/net/context" - "golang.org/x/net/http2" - "golang.org/x/net/http2/hpack" - - "google.golang.org/grpc/channelz" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/keepalive" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/peer" - "google.golang.org/grpc/stats" - "google.golang.org/grpc/status" - "google.golang.org/grpc/tap" -) - -// ErrIllegalHeaderWrite indicates that setting header is illegal because of -// the stream's state. -var ErrIllegalHeaderWrite = errors.New("transport: the stream is done or WriteHeader was already called") - -// http2Server implements the ServerTransport interface with HTTP2. -type http2Server struct { - ctx context.Context - ctxDone <-chan struct{} // Cache the context.Done() chan - cancel context.CancelFunc - conn net.Conn - loopy *loopyWriter - readerDone chan struct{} // sync point to enable testing. - writerDone chan struct{} // sync point to enable testing. - remoteAddr net.Addr - localAddr net.Addr - maxStreamID uint32 // max stream ID ever seen - authInfo credentials.AuthInfo // auth info about the connection - inTapHandle tap.ServerInHandle - framer *framer - // The max number of concurrent streams. - maxStreams uint32 - // controlBuf delivers all the control related tasks (e.g., window - // updates, reset streams, and various settings) to the controller. - controlBuf *controlBuffer - fc *trInFlow - stats stats.Handler - // Flag to keep track of reading activity on transport. - // 1 is true and 0 is false. - activity uint32 // Accessed atomically. - // Keepalive and max-age parameters for the server. - kp keepalive.ServerParameters - - // Keepalive enforcement policy. - kep keepalive.EnforcementPolicy - // The time instance last ping was received. - lastPingAt time.Time - // Number of times the client has violated keepalive ping policy so far. - pingStrikes uint8 - // Flag to signify that number of ping strikes should be reset to 0. - // This is set whenever data or header frames are sent. - // 1 means yes. - resetPingStrikes uint32 // Accessed atomically. - initialWindowSize int32 - bdpEst *bdpEstimator - - mu sync.Mutex // guard the following - - // drainChan is initialized when drain(...) is called the first time. - // After which the server writes out the first GoAway(with ID 2^31-1) frame. - // Then an independent goroutine will be launched to later send the second GoAway. - // During this time we don't want to write another first GoAway(with ID 2^31 -1) frame. - // Thus call to drain(...) will be a no-op if drainChan is already initialized since draining is - // already underway. - drainChan chan struct{} - state transportState - activeStreams map[uint32]*Stream - // idle is the time instant when the connection went idle. - // This is either the beginning of the connection or when the number of - // RPCs go down to 0. - // When the connection is busy, this value is set to 0. - idle time.Time - - // Fields below are for channelz metric collection. - channelzID int64 // channelz unique identification number - czmu sync.RWMutex - kpCount int64 - // The number of streams that have started, including already finished ones. - streamsStarted int64 - // The number of streams that have ended successfully by sending frame with - // EoS bit set. - streamsSucceeded int64 - streamsFailed int64 - lastStreamCreated time.Time - msgSent int64 - msgRecv int64 - lastMsgSent time.Time - lastMsgRecv time.Time -} - -// newHTTP2Server constructs a ServerTransport based on HTTP2. ConnectionError is -// returned if something goes wrong. -func newHTTP2Server(conn net.Conn, config *ServerConfig) (_ ServerTransport, err error) { - writeBufSize := defaultWriteBufSize - if config.WriteBufferSize > 0 { - writeBufSize = config.WriteBufferSize - } - readBufSize := defaultReadBufSize - if config.ReadBufferSize > 0 { - readBufSize = config.ReadBufferSize - } - framer := newFramer(conn, writeBufSize, readBufSize) - // Send initial settings as connection preface to client. - var isettings []http2.Setting - // TODO(zhaoq): Have a better way to signal "no limit" because 0 is - // permitted in the HTTP2 spec. - maxStreams := config.MaxStreams - if maxStreams == 0 { - maxStreams = math.MaxUint32 - } else { - isettings = append(isettings, http2.Setting{ - ID: http2.SettingMaxConcurrentStreams, - Val: maxStreams, - }) - } - dynamicWindow := true - iwz := int32(initialWindowSize) - if config.InitialWindowSize >= defaultWindowSize { - iwz = config.InitialWindowSize - dynamicWindow = false - } - icwz := int32(initialWindowSize) - if config.InitialConnWindowSize >= defaultWindowSize { - icwz = config.InitialConnWindowSize - dynamicWindow = false - } - if iwz != defaultWindowSize { - isettings = append(isettings, http2.Setting{ - ID: http2.SettingInitialWindowSize, - Val: uint32(iwz)}) - } - if err := framer.fr.WriteSettings(isettings...); err != nil { - return nil, connectionErrorf(false, err, "transport: %v", err) - } - // Adjust the connection flow control window if needed. - if delta := uint32(icwz - defaultWindowSize); delta > 0 { - if err := framer.fr.WriteWindowUpdate(0, delta); err != nil { - return nil, connectionErrorf(false, err, "transport: %v", err) - } - } - kp := config.KeepaliveParams - if kp.MaxConnectionIdle == 0 { - kp.MaxConnectionIdle = defaultMaxConnectionIdle - } - if kp.MaxConnectionAge == 0 { - kp.MaxConnectionAge = defaultMaxConnectionAge - } - // Add a jitter to MaxConnectionAge. - kp.MaxConnectionAge += getJitter(kp.MaxConnectionAge) - if kp.MaxConnectionAgeGrace == 0 { - kp.MaxConnectionAgeGrace = defaultMaxConnectionAgeGrace - } - if kp.Time == 0 { - kp.Time = defaultServerKeepaliveTime - } - if kp.Timeout == 0 { - kp.Timeout = defaultServerKeepaliveTimeout - } - kep := config.KeepalivePolicy - if kep.MinTime == 0 { - kep.MinTime = defaultKeepalivePolicyMinTime - } - ctx, cancel := context.WithCancel(context.Background()) - t := &http2Server{ - ctx: ctx, - cancel: cancel, - ctxDone: ctx.Done(), - conn: conn, - remoteAddr: conn.RemoteAddr(), - localAddr: conn.LocalAddr(), - authInfo: config.AuthInfo, - framer: framer, - readerDone: make(chan struct{}), - writerDone: make(chan struct{}), - maxStreams: maxStreams, - inTapHandle: config.InTapHandle, - fc: &trInFlow{limit: uint32(icwz)}, - state: reachable, - activeStreams: make(map[uint32]*Stream), - stats: config.StatsHandler, - kp: kp, - idle: time.Now(), - kep: kep, - initialWindowSize: iwz, - } - t.controlBuf = newControlBuffer(t.ctxDone) - if dynamicWindow { - t.bdpEst = &bdpEstimator{ - bdp: initialWindowSize, - updateFlowControl: t.updateFlowControl, - } - } - if t.stats != nil { - t.ctx = t.stats.TagConn(t.ctx, &stats.ConnTagInfo{ - RemoteAddr: t.remoteAddr, - LocalAddr: t.localAddr, - }) - connBegin := &stats.ConnBegin{} - t.stats.HandleConn(t.ctx, connBegin) - } - if channelz.IsOn() { - t.channelzID = channelz.RegisterNormalSocket(t, config.ChannelzParentID, "") - } - t.framer.writer.Flush() - - defer func() { - if err != nil { - t.Close() - } - }() - - // Check the validity of client preface. - preface := make([]byte, len(clientPreface)) - if _, err := io.ReadFull(t.conn, preface); err != nil { - return nil, connectionErrorf(false, err, "transport: http2Server.HandleStreams failed to receive the preface from client: %v", err) - } - if !bytes.Equal(preface, clientPreface) { - return nil, connectionErrorf(false, nil, "transport: http2Server.HandleStreams received bogus greeting from client: %q", preface) - } - - frame, err := t.framer.fr.ReadFrame() - if err == io.EOF || err == io.ErrUnexpectedEOF { - return nil, err - } - if err != nil { - return nil, connectionErrorf(false, err, "transport: http2Server.HandleStreams failed to read initial settings frame: %v", err) - } - atomic.StoreUint32(&t.activity, 1) - sf, ok := frame.(*http2.SettingsFrame) - if !ok { - return nil, connectionErrorf(false, nil, "transport: http2Server.HandleStreams saw invalid preface type %T from client", frame) - } - t.handleSettings(sf) - - go func() { - t.loopy = newLoopyWriter(serverSide, t.framer, t.controlBuf, t.bdpEst) - t.loopy.ssGoAwayHandler = t.outgoingGoAwayHandler - t.loopy.run() - t.conn.Close() - close(t.writerDone) - }() - go t.keepalive() - return t, nil -} - -// operateHeader takes action on the decoded headers. -func (t *http2Server) operateHeaders(frame *http2.MetaHeadersFrame, handle func(*Stream), traceCtx func(context.Context, string) context.Context) (close bool) { - streamID := frame.Header().StreamID - var state decodeState - for _, hf := range frame.Fields { - if err := state.processHeaderField(hf); err != nil { - if se, ok := err.(StreamError); ok { - t.controlBuf.put(&cleanupStream{ - streamID: streamID, - rst: true, - rstCode: statusCodeConvTab[se.Code], - onWrite: func() {}, - }) - } - return - } - } - - buf := newRecvBuffer() - s := &Stream{ - id: streamID, - st: t, - buf: buf, - fc: &inFlow{limit: uint32(t.initialWindowSize)}, - recvCompress: state.encoding, - method: state.method, - contentSubtype: state.contentSubtype, - } - if frame.StreamEnded() { - // s is just created by the caller. No lock needed. - s.state = streamReadDone - } - if state.timeoutSet { - s.ctx, s.cancel = context.WithTimeout(t.ctx, state.timeout) - } else { - s.ctx, s.cancel = context.WithCancel(t.ctx) - } - pr := &peer.Peer{ - Addr: t.remoteAddr, - } - // Attach Auth info if there is any. - if t.authInfo != nil { - pr.AuthInfo = t.authInfo - } - s.ctx = peer.NewContext(s.ctx, pr) - // Attach the received metadata to the context. - if len(state.mdata) > 0 { - s.ctx = metadata.NewIncomingContext(s.ctx, state.mdata) - } - if state.statsTags != nil { - s.ctx = stats.SetIncomingTags(s.ctx, state.statsTags) - } - if state.statsTrace != nil { - s.ctx = stats.SetIncomingTrace(s.ctx, state.statsTrace) - } - if t.inTapHandle != nil { - var err error - info := &tap.Info{ - FullMethodName: state.method, - } - s.ctx, err = t.inTapHandle(s.ctx, info) - if err != nil { - warningf("transport: http2Server.operateHeaders got an error from InTapHandle: %v", err) - t.controlBuf.put(&cleanupStream{ - streamID: s.id, - rst: true, - rstCode: http2.ErrCodeRefusedStream, - onWrite: func() {}, - }) - return - } - } - t.mu.Lock() - if t.state != reachable { - t.mu.Unlock() - return - } - if uint32(len(t.activeStreams)) >= t.maxStreams { - t.mu.Unlock() - t.controlBuf.put(&cleanupStream{ - streamID: streamID, - rst: true, - rstCode: http2.ErrCodeRefusedStream, - onWrite: func() {}, - }) - return - } - if streamID%2 != 1 || streamID <= t.maxStreamID { - t.mu.Unlock() - // illegal gRPC stream id. - errorf("transport: http2Server.HandleStreams received an illegal stream id: %v", streamID) - return true - } - t.maxStreamID = streamID - t.activeStreams[streamID] = s - if len(t.activeStreams) == 1 { - t.idle = time.Time{} - } - t.mu.Unlock() - if channelz.IsOn() { - t.czmu.Lock() - t.streamsStarted++ - t.lastStreamCreated = time.Now() - t.czmu.Unlock() - } - s.requestRead = func(n int) { - t.adjustWindow(s, uint32(n)) - } - s.ctx = traceCtx(s.ctx, s.method) - if t.stats != nil { - s.ctx = t.stats.TagRPC(s.ctx, &stats.RPCTagInfo{FullMethodName: s.method}) - inHeader := &stats.InHeader{ - FullMethod: s.method, - RemoteAddr: t.remoteAddr, - LocalAddr: t.localAddr, - Compression: s.recvCompress, - WireLength: int(frame.Header().Length), - } - t.stats.HandleRPC(s.ctx, inHeader) - } - s.ctxDone = s.ctx.Done() - s.wq = newWriteQuota(defaultWriteQuota, s.ctxDone) - s.trReader = &transportReader{ - reader: &recvBufferReader{ - ctx: s.ctx, - ctxDone: s.ctxDone, - recv: s.buf, - }, - windowHandler: func(n int) { - t.updateWindow(s, uint32(n)) - }, - } - handle(s) - return -} - -// HandleStreams receives incoming streams using the given handler. This is -// typically run in a separate goroutine. -// traceCtx attaches trace to ctx and returns the new context. -func (t *http2Server) HandleStreams(handle func(*Stream), traceCtx func(context.Context, string) context.Context) { - defer close(t.readerDone) - for { - frame, err := t.framer.fr.ReadFrame() - atomic.StoreUint32(&t.activity, 1) - if err != nil { - if se, ok := err.(http2.StreamError); ok { - warningf("transport: http2Server.HandleStreams encountered http2.StreamError: %v", se) - t.mu.Lock() - s := t.activeStreams[se.StreamID] - t.mu.Unlock() - if s != nil { - t.closeStream(s, true, se.Code, nil, false) - } else { - t.controlBuf.put(&cleanupStream{ - streamID: se.StreamID, - rst: true, - rstCode: se.Code, - onWrite: func() {}, - }) - } - continue - } - if err == io.EOF || err == io.ErrUnexpectedEOF { - t.Close() - return - } - warningf("transport: http2Server.HandleStreams failed to read frame: %v", err) - t.Close() - return - } - switch frame := frame.(type) { - case *http2.MetaHeadersFrame: - if t.operateHeaders(frame, handle, traceCtx) { - t.Close() - break - } - case *http2.DataFrame: - t.handleData(frame) - case *http2.RSTStreamFrame: - t.handleRSTStream(frame) - case *http2.SettingsFrame: - t.handleSettings(frame) - case *http2.PingFrame: - t.handlePing(frame) - case *http2.WindowUpdateFrame: - t.handleWindowUpdate(frame) - case *http2.GoAwayFrame: - // TODO: Handle GoAway from the client appropriately. - default: - errorf("transport: http2Server.HandleStreams found unhandled frame type %v.", frame) - } - } -} - -func (t *http2Server) getStream(f http2.Frame) (*Stream, bool) { - t.mu.Lock() - defer t.mu.Unlock() - if t.activeStreams == nil { - // The transport is closing. - return nil, false - } - s, ok := t.activeStreams[f.Header().StreamID] - if !ok { - // The stream is already done. - return nil, false - } - return s, true -} - -// adjustWindow sends out extra window update over the initial window size -// of stream if the application is requesting data larger in size than -// the window. -func (t *http2Server) adjustWindow(s *Stream, n uint32) { - if w := s.fc.maybeAdjust(n); w > 0 { - t.controlBuf.put(&outgoingWindowUpdate{streamID: s.id, increment: w}) - } - -} - -// updateWindow adjusts the inbound quota for the stream and the transport. -// Window updates will deliver to the controller for sending when -// the cumulative quota exceeds the corresponding threshold. -func (t *http2Server) updateWindow(s *Stream, n uint32) { - if w := s.fc.onRead(n); w > 0 { - t.controlBuf.put(&outgoingWindowUpdate{streamID: s.id, - increment: w, - }) - } -} - -// updateFlowControl updates the incoming flow control windows -// for the transport and the stream based on the current bdp -// estimation. -func (t *http2Server) updateFlowControl(n uint32) { - t.mu.Lock() - for _, s := range t.activeStreams { - s.fc.newLimit(n) - } - t.initialWindowSize = int32(n) - t.mu.Unlock() - t.controlBuf.put(&outgoingWindowUpdate{ - streamID: 0, - increment: t.fc.newLimit(n), - }) - t.controlBuf.put(&outgoingSettings{ - ss: []http2.Setting{ - { - ID: http2.SettingInitialWindowSize, - Val: n, - }, - }, - }) - -} - -func (t *http2Server) handleData(f *http2.DataFrame) { - size := f.Header().Length - var sendBDPPing bool - if t.bdpEst != nil { - sendBDPPing = t.bdpEst.add(size) - } - // Decouple connection's flow control from application's read. - // An update on connection's flow control should not depend on - // whether user application has read the data or not. Such a - // restriction is already imposed on the stream's flow control, - // and therefore the sender will be blocked anyways. - // Decoupling the connection flow control will prevent other - // active(fast) streams from starving in presence of slow or - // inactive streams. - if w := t.fc.onData(size); w > 0 { - t.controlBuf.put(&outgoingWindowUpdate{ - streamID: 0, - increment: w, - }) - } - if sendBDPPing { - // Avoid excessive ping detection (e.g. in an L7 proxy) - // by sending a window update prior to the BDP ping. - if w := t.fc.reset(); w > 0 { - t.controlBuf.put(&outgoingWindowUpdate{ - streamID: 0, - increment: w, - }) - } - t.controlBuf.put(bdpPing) - } - // Select the right stream to dispatch. - s, ok := t.getStream(f) - if !ok { - return - } - if size > 0 { - if err := s.fc.onData(size); err != nil { - t.closeStream(s, true, http2.ErrCodeFlowControl, nil, false) - return - } - if f.Header().Flags.Has(http2.FlagDataPadded) { - if w := s.fc.onRead(size - uint32(len(f.Data()))); w > 0 { - t.controlBuf.put(&outgoingWindowUpdate{s.id, w}) - } - } - // TODO(bradfitz, zhaoq): A copy is required here because there is no - // guarantee f.Data() is consumed before the arrival of next frame. - // Can this copy be eliminated? - if len(f.Data()) > 0 { - data := make([]byte, len(f.Data())) - copy(data, f.Data()) - s.write(recvMsg{data: data}) - } - } - if f.Header().Flags.Has(http2.FlagDataEndStream) { - // Received the end of stream from the client. - s.compareAndSwapState(streamActive, streamReadDone) - s.write(recvMsg{err: io.EOF}) - } -} - -func (t *http2Server) handleRSTStream(f *http2.RSTStreamFrame) { - s, ok := t.getStream(f) - if !ok { - return - } - t.closeStream(s, false, 0, nil, false) -} - -func (t *http2Server) handleSettings(f *http2.SettingsFrame) { - if f.IsAck() { - return - } - var ss []http2.Setting - f.ForeachSetting(func(s http2.Setting) error { - ss = append(ss, s) - return nil - }) - t.controlBuf.put(&incomingSettings{ - ss: ss, - }) -} - -const ( - maxPingStrikes = 2 - defaultPingTimeout = 2 * time.Hour -) - -func (t *http2Server) handlePing(f *http2.PingFrame) { - if f.IsAck() { - if f.Data == goAwayPing.data && t.drainChan != nil { - close(t.drainChan) - return - } - // Maybe it's a BDP ping. - if t.bdpEst != nil { - t.bdpEst.calculate(f.Data) - } - return - } - pingAck := &ping{ack: true} - copy(pingAck.data[:], f.Data[:]) - t.controlBuf.put(pingAck) - - now := time.Now() - defer func() { - t.lastPingAt = now - }() - // A reset ping strikes means that we don't need to check for policy - // violation for this ping and the pingStrikes counter should be set - // to 0. - if atomic.CompareAndSwapUint32(&t.resetPingStrikes, 1, 0) { - t.pingStrikes = 0 - return - } - t.mu.Lock() - ns := len(t.activeStreams) - t.mu.Unlock() - if ns < 1 && !t.kep.PermitWithoutStream { - // Keepalive shouldn't be active thus, this new ping should - // have come after at least defaultPingTimeout. - if t.lastPingAt.Add(defaultPingTimeout).After(now) { - t.pingStrikes++ - } - } else { - // Check if keepalive policy is respected. - if t.lastPingAt.Add(t.kep.MinTime).After(now) { - t.pingStrikes++ - } - } - - if t.pingStrikes > maxPingStrikes { - // Send goaway and close the connection. - errorf("transport: Got too many pings from the client, closing the connection.") - t.controlBuf.put(&goAway{code: http2.ErrCodeEnhanceYourCalm, debugData: []byte("too_many_pings"), closeConn: true}) - } -} - -func (t *http2Server) handleWindowUpdate(f *http2.WindowUpdateFrame) { - t.controlBuf.put(&incomingWindowUpdate{ - streamID: f.Header().StreamID, - increment: f.Increment, - }) -} - -// WriteHeader sends the header metedata md back to the client. -func (t *http2Server) WriteHeader(s *Stream, md metadata.MD) error { - if s.headerOk || s.getState() == streamDone { - return ErrIllegalHeaderWrite - } - s.headerOk = true - if md.Len() > 0 { - if s.header.Len() > 0 { - s.header = metadata.Join(s.header, md) - } else { - s.header = md - } - } - md = s.header - // TODO(mmukhi): Benchmark if the performance gets better if count the metadata and other header fields - // first and create a slice of that exact size. - headerFields := make([]hpack.HeaderField, 0, 2) // at least :status, content-type will be there if none else. - headerFields = append(headerFields, hpack.HeaderField{Name: ":status", Value: "200"}) - headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: contentType(s.contentSubtype)}) - if s.sendCompress != "" { - headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-encoding", Value: s.sendCompress}) - } - for k, vv := range md { - if isReservedHeader(k) { - // Clients don't tolerate reading restricted headers after some non restricted ones were sent. - continue - } - for _, v := range vv { - headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)}) - } - } - t.controlBuf.put(&headerFrame{ - streamID: s.id, - hf: headerFields, - endStream: false, - onWrite: func() { - atomic.StoreUint32(&t.resetPingStrikes, 1) - }, - wq: s.wq, - }) - if t.stats != nil { - // Note: WireLength is not set in outHeader. - // TODO(mmukhi): Revisit this later, if needed. - outHeader := &stats.OutHeader{} - t.stats.HandleRPC(s.Context(), outHeader) - } - return nil -} - -// WriteStatus sends stream status to the client and terminates the stream. -// There is no further I/O operations being able to perform on this stream. -// TODO(zhaoq): Now it indicates the end of entire stream. Revisit if early -// OK is adopted. -func (t *http2Server) WriteStatus(s *Stream, st *status.Status) error { - if !s.headerOk && s.header.Len() > 0 { - if err := t.WriteHeader(s, nil); err != nil { - return err - } - } else { - if s.getState() == streamDone { - return nil - } - } - // TODO(mmukhi): Benchmark if the performance gets better if count the metadata and other header fields - // first and create a slice of that exact size. - headerFields := make([]hpack.HeaderField, 0, 2) // grpc-status and grpc-message will be there if none else. - if !s.headerOk { - headerFields = append(headerFields, hpack.HeaderField{Name: ":status", Value: "200"}) - headerFields = append(headerFields, hpack.HeaderField{Name: "content-type", Value: contentType(s.contentSubtype)}) - } - headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status", Value: strconv.Itoa(int(st.Code()))}) - headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-message", Value: encodeGrpcMessage(st.Message())}) - - if p := st.Proto(); p != nil && len(p.Details) > 0 { - stBytes, err := proto.Marshal(p) - if err != nil { - // TODO: return error instead, when callers are able to handle it. - panic(err) - } - - headerFields = append(headerFields, hpack.HeaderField{Name: "grpc-status-details-bin", Value: encodeBinHeader(stBytes)}) - } - - // Attach the trailer metadata. - for k, vv := range s.trailer { - // Clients don't tolerate reading restricted headers after some non restricted ones were sent. - if isReservedHeader(k) { - continue - } - for _, v := range vv { - headerFields = append(headerFields, hpack.HeaderField{Name: k, Value: encodeMetadataHeader(k, v)}) - } - } - trailer := &headerFrame{ - streamID: s.id, - hf: headerFields, - endStream: true, - onWrite: func() { - atomic.StoreUint32(&t.resetPingStrikes, 1) - }, - } - t.closeStream(s, false, 0, trailer, true) - if t.stats != nil { - t.stats.HandleRPC(s.Context(), &stats.OutTrailer{}) - } - return nil -} - -// Write converts the data into HTTP2 data frame and sends it out. Non-nil error -// is returns if it fails (e.g., framing error, transport error). -func (t *http2Server) Write(s *Stream, hdr []byte, data []byte, opts *Options) error { - if !s.headerOk { // Headers haven't been written yet. - if err := t.WriteHeader(s, nil); err != nil { - // TODO(mmukhi, dfawley): Make sure this is the right code to return. - return streamErrorf(codes.Internal, "transport: %v", err) - } - } else { - // Writing headers checks for this condition. - if s.getState() == streamDone { - // TODO(mmukhi, dfawley): Should the server write also return io.EOF? - s.cancel() - select { - case <-t.ctx.Done(): - return ErrConnClosing - default: - } - return ContextErr(s.ctx.Err()) - } - } - // Add some data to header frame so that we can equally distribute bytes across frames. - emptyLen := http2MaxFrameLen - len(hdr) - if emptyLen > len(data) { - emptyLen = len(data) - } - hdr = append(hdr, data[:emptyLen]...) - data = data[emptyLen:] - df := &dataFrame{ - streamID: s.id, - h: hdr, - d: data, - onEachWrite: func() { - atomic.StoreUint32(&t.resetPingStrikes, 1) - }, - } - if err := s.wq.get(int32(len(hdr) + len(data))); err != nil { - select { - case <-t.ctx.Done(): - return ErrConnClosing - default: - } - return ContextErr(s.ctx.Err()) - } - return t.controlBuf.put(df) -} - -// keepalive running in a separate goroutine does the following: -// 1. Gracefully closes an idle connection after a duration of keepalive.MaxConnectionIdle. -// 2. Gracefully closes any connection after a duration of keepalive.MaxConnectionAge. -// 3. Forcibly closes a connection after an additive period of keepalive.MaxConnectionAgeGrace over keepalive.MaxConnectionAge. -// 4. Makes sure a connection is alive by sending pings with a frequency of keepalive.Time and closes a non-responsive connection -// after an additional duration of keepalive.Timeout. -func (t *http2Server) keepalive() { - p := &ping{} - var pingSent bool - maxIdle := time.NewTimer(t.kp.MaxConnectionIdle) - maxAge := time.NewTimer(t.kp.MaxConnectionAge) - keepalive := time.NewTimer(t.kp.Time) - // NOTE: All exit paths of this function should reset their - // respective timers. A failure to do so will cause the - // following clean-up to deadlock and eventually leak. - defer func() { - if !maxIdle.Stop() { - <-maxIdle.C - } - if !maxAge.Stop() { - <-maxAge.C - } - if !keepalive.Stop() { - <-keepalive.C - } - }() - for { - select { - case <-maxIdle.C: - t.mu.Lock() - idle := t.idle - if idle.IsZero() { // The connection is non-idle. - t.mu.Unlock() - maxIdle.Reset(t.kp.MaxConnectionIdle) - continue - } - val := t.kp.MaxConnectionIdle - time.Since(idle) - t.mu.Unlock() - if val <= 0 { - // The connection has been idle for a duration of keepalive.MaxConnectionIdle or more. - // Gracefully close the connection. - t.drain(http2.ErrCodeNo, []byte{}) - // Resetting the timer so that the clean-up doesn't deadlock. - maxIdle.Reset(infinity) - return - } - maxIdle.Reset(val) - case <-maxAge.C: - t.drain(http2.ErrCodeNo, []byte{}) - maxAge.Reset(t.kp.MaxConnectionAgeGrace) - select { - case <-maxAge.C: - // Close the connection after grace period. - t.Close() - // Resetting the timer so that the clean-up doesn't deadlock. - maxAge.Reset(infinity) - case <-t.ctx.Done(): - } - return - case <-keepalive.C: - if atomic.CompareAndSwapUint32(&t.activity, 1, 0) { - pingSent = false - keepalive.Reset(t.kp.Time) - continue - } - if pingSent { - t.Close() - // Resetting the timer so that the clean-up doesn't deadlock. - keepalive.Reset(infinity) - return - } - pingSent = true - if channelz.IsOn() { - t.czmu.Lock() - t.kpCount++ - t.czmu.Unlock() - } - t.controlBuf.put(p) - keepalive.Reset(t.kp.Timeout) - case <-t.ctx.Done(): - return - } - } -} - -// Close starts shutting down the http2Server transport. -// TODO(zhaoq): Now the destruction is not blocked on any pending streams. This -// could cause some resource issue. Revisit this later. -func (t *http2Server) Close() error { - t.mu.Lock() - if t.state == closing { - t.mu.Unlock() - return errors.New("transport: Close() was already called") - } - t.state = closing - streams := t.activeStreams - t.activeStreams = nil - t.mu.Unlock() - t.controlBuf.finish() - t.cancel() - err := t.conn.Close() - if channelz.IsOn() { - channelz.RemoveEntry(t.channelzID) - } - // Cancel all active streams. - for _, s := range streams { - s.cancel() - } - if t.stats != nil { - connEnd := &stats.ConnEnd{} - t.stats.HandleConn(t.ctx, connEnd) - } - return err -} - -// closeStream clears the footprint of a stream when the stream is not needed -// any more. -func (t *http2Server) closeStream(s *Stream, rst bool, rstCode http2.ErrCode, hdr *headerFrame, eosReceived bool) { - if s.swapState(streamDone) == streamDone { - // If the stream was already done, return. - return - } - // In case stream sending and receiving are invoked in separate - // goroutines (e.g., bi-directional streaming), cancel needs to be - // called to interrupt the potential blocking on other goroutines. - s.cancel() - cleanup := &cleanupStream{ - streamID: s.id, - rst: rst, - rstCode: rstCode, - onWrite: func() { - t.mu.Lock() - if t.activeStreams != nil { - delete(t.activeStreams, s.id) - if len(t.activeStreams) == 0 { - t.idle = time.Now() - } - } - t.mu.Unlock() - if channelz.IsOn() { - t.czmu.Lock() - if eosReceived { - t.streamsSucceeded++ - } else { - t.streamsFailed++ - } - t.czmu.Unlock() - } - }, - } - if hdr != nil { - hdr.cleanup = cleanup - t.controlBuf.put(hdr) - } else { - t.controlBuf.put(cleanup) - } -} - -func (t *http2Server) RemoteAddr() net.Addr { - return t.remoteAddr -} - -func (t *http2Server) Drain() { - t.drain(http2.ErrCodeNo, []byte{}) -} - -func (t *http2Server) drain(code http2.ErrCode, debugData []byte) { - t.mu.Lock() - defer t.mu.Unlock() - if t.drainChan != nil { - return - } - t.drainChan = make(chan struct{}) - t.controlBuf.put(&goAway{code: code, debugData: debugData, headsUp: true}) -} - -var goAwayPing = &ping{data: [8]byte{1, 6, 1, 8, 0, 3, 3, 9}} - -// Handles outgoing GoAway and returns true if loopy needs to put itself -// in draining mode. -func (t *http2Server) outgoingGoAwayHandler(g *goAway) (bool, error) { - t.mu.Lock() - if t.state == closing { // TODO(mmukhi): This seems unnecessary. - t.mu.Unlock() - // The transport is closing. - return false, ErrConnClosing - } - sid := t.maxStreamID - if !g.headsUp { - // Stop accepting more streams now. - t.state = draining - if len(t.activeStreams) == 0 { - g.closeConn = true - } - t.mu.Unlock() - if err := t.framer.fr.WriteGoAway(sid, g.code, g.debugData); err != nil { - return false, err - } - if g.closeConn { - // Abruptly close the connection following the GoAway (via - // loopywriter). But flush out what's inside the buffer first. - t.framer.writer.Flush() - return false, fmt.Errorf("transport: Connection closing") - } - return true, nil - } - t.mu.Unlock() - // For a graceful close, send out a GoAway with stream ID of MaxUInt32, - // Follow that with a ping and wait for the ack to come back or a timer - // to expire. During this time accept new streams since they might have - // originated before the GoAway reaches the client. - // After getting the ack or timer expiration send out another GoAway this - // time with an ID of the max stream server intends to process. - if err := t.framer.fr.WriteGoAway(math.MaxUint32, http2.ErrCodeNo, []byte{}); err != nil { - return false, err - } - if err := t.framer.fr.WritePing(false, goAwayPing.data); err != nil { - return false, err - } - go func() { - timer := time.NewTimer(time.Minute) - defer timer.Stop() - select { - case <-t.drainChan: - case <-timer.C: - case <-t.ctx.Done(): - return - } - t.controlBuf.put(&goAway{code: g.code, debugData: g.debugData}) - }() - return false, nil -} - -func (t *http2Server) ChannelzMetric() *channelz.SocketInternalMetric { - t.czmu.RLock() - s := channelz.SocketInternalMetric{ - StreamsStarted: t.streamsStarted, - StreamsSucceeded: t.streamsSucceeded, - StreamsFailed: t.streamsFailed, - MessagesSent: t.msgSent, - MessagesReceived: t.msgRecv, - KeepAlivesSent: t.kpCount, - LastRemoteStreamCreatedTimestamp: t.lastStreamCreated, - LastMessageSentTimestamp: t.lastMsgSent, - LastMessageReceivedTimestamp: t.lastMsgRecv, - LocalFlowControlWindow: int64(t.fc.getSize()), - //socket options - LocalAddr: t.localAddr, - RemoteAddr: t.remoteAddr, - // Security - // RemoteName : - } - t.czmu.RUnlock() - s.RemoteFlowControlWindow = t.getOutFlowWindow() - return &s -} - -func (t *http2Server) IncrMsgSent() { - t.czmu.Lock() - t.msgSent++ - t.lastMsgSent = time.Now() - t.czmu.Unlock() -} - -func (t *http2Server) IncrMsgRecv() { - t.czmu.Lock() - t.msgRecv++ - t.lastMsgRecv = time.Now() - t.czmu.Unlock() -} - -func (t *http2Server) getOutFlowWindow() int64 { - resp := make(chan uint32) - timer := time.NewTimer(time.Second) - defer timer.Stop() - t.controlBuf.put(&outFlowControlSizeRequest{resp}) - select { - case sz := <-resp: - return int64(sz) - case <-t.ctxDone: - return -1 - case <-timer.C: - return -2 - } -} - -var rgen = rand.New(rand.NewSource(time.Now().UnixNano())) - -func getJitter(v time.Duration) time.Duration { - if v == infinity { - return 0 - } - // Generate a jitter between +/- 10% of the value. - r := int64(v / 10) - j := rgen.Int63n(2*r) - r - return time.Duration(j) -} diff --git a/vendor/google.golang.org/grpc/transport/http_util.go b/vendor/google.golang.org/grpc/transport/http_util.go deleted file mode 100644 index a355866089c..00000000000 --- a/vendor/google.golang.org/grpc/transport/http_util.go +++ /dev/null @@ -1,574 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package transport - -import ( - "bufio" - "bytes" - "encoding/base64" - "fmt" - "net" - "net/http" - "strconv" - "strings" - "time" - - "github.com/golang/protobuf/proto" - "golang.org/x/net/http2" - "golang.org/x/net/http2/hpack" - spb "google.golang.org/genproto/googleapis/rpc/status" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - -const ( - // http2MaxFrameLen specifies the max length of a HTTP2 frame. - http2MaxFrameLen = 16384 // 16KB frame - // http://http2.github.io/http2-spec/#SettingValues - http2InitHeaderTableSize = 4096 - // http2IOBufSize specifies the buffer size for sending frames. - defaultWriteBufSize = 32 * 1024 - defaultReadBufSize = 32 * 1024 - // baseContentType is the base content-type for gRPC. This is a valid - // content-type on it's own, but can also include a content-subtype such as - // "proto" as a suffix after "+" or ";". See - // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests - // for more details. - baseContentType = "application/grpc" -) - -var ( - clientPreface = []byte(http2.ClientPreface) - http2ErrConvTab = map[http2.ErrCode]codes.Code{ - http2.ErrCodeNo: codes.Internal, - http2.ErrCodeProtocol: codes.Internal, - http2.ErrCodeInternal: codes.Internal, - http2.ErrCodeFlowControl: codes.ResourceExhausted, - http2.ErrCodeSettingsTimeout: codes.Internal, - http2.ErrCodeStreamClosed: codes.Internal, - http2.ErrCodeFrameSize: codes.Internal, - http2.ErrCodeRefusedStream: codes.Unavailable, - http2.ErrCodeCancel: codes.Canceled, - http2.ErrCodeCompression: codes.Internal, - http2.ErrCodeConnect: codes.Internal, - http2.ErrCodeEnhanceYourCalm: codes.ResourceExhausted, - http2.ErrCodeInadequateSecurity: codes.PermissionDenied, - http2.ErrCodeHTTP11Required: codes.Internal, - } - statusCodeConvTab = map[codes.Code]http2.ErrCode{ - codes.Internal: http2.ErrCodeInternal, - codes.Canceled: http2.ErrCodeCancel, - codes.Unavailable: http2.ErrCodeRefusedStream, - codes.ResourceExhausted: http2.ErrCodeEnhanceYourCalm, - codes.PermissionDenied: http2.ErrCodeInadequateSecurity, - } - httpStatusConvTab = map[int]codes.Code{ - // 400 Bad Request - INTERNAL. - http.StatusBadRequest: codes.Internal, - // 401 Unauthorized - UNAUTHENTICATED. - http.StatusUnauthorized: codes.Unauthenticated, - // 403 Forbidden - PERMISSION_DENIED. - http.StatusForbidden: codes.PermissionDenied, - // 404 Not Found - UNIMPLEMENTED. - http.StatusNotFound: codes.Unimplemented, - // 429 Too Many Requests - UNAVAILABLE. - http.StatusTooManyRequests: codes.Unavailable, - // 502 Bad Gateway - UNAVAILABLE. - http.StatusBadGateway: codes.Unavailable, - // 503 Service Unavailable - UNAVAILABLE. - http.StatusServiceUnavailable: codes.Unavailable, - // 504 Gateway timeout - UNAVAILABLE. - http.StatusGatewayTimeout: codes.Unavailable, - } -) - -// Records the states during HPACK decoding. Must be reset once the -// decoding of the entire headers are finished. -type decodeState struct { - encoding string - // statusGen caches the stream status received from the trailer the server - // sent. Client side only. Do not access directly. After all trailers are - // parsed, use the status method to retrieve the status. - statusGen *status.Status - // rawStatusCode and rawStatusMsg are set from the raw trailer fields and are not - // intended for direct access outside of parsing. - rawStatusCode *int - rawStatusMsg string - httpStatus *int - // Server side only fields. - timeoutSet bool - timeout time.Duration - method string - // key-value metadata map from the peer. - mdata map[string][]string - statsTags []byte - statsTrace []byte - contentSubtype string -} - -// isReservedHeader checks whether hdr belongs to HTTP2 headers -// reserved by gRPC protocol. Any other headers are classified as the -// user-specified metadata. -func isReservedHeader(hdr string) bool { - if hdr != "" && hdr[0] == ':' { - return true - } - switch hdr { - case "content-type", - "user-agent", - "grpc-message-type", - "grpc-encoding", - "grpc-message", - "grpc-status", - "grpc-timeout", - "grpc-status-details-bin", - "te": - return true - default: - return false - } -} - -// isWhitelistedHeader checks whether hdr should be propagated -// into metadata visible to users. -func isWhitelistedHeader(hdr string) bool { - switch hdr { - case ":authority", "user-agent": - return true - default: - return false - } -} - -// contentSubtype returns the content-subtype for the given content-type. The -// given content-type must be a valid content-type that starts with -// "application/grpc". A content-subtype will follow "application/grpc" after a -// "+" or ";". See -// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for -// more details. -// -// If contentType is not a valid content-type for gRPC, the boolean -// will be false, otherwise true. If content-type == "application/grpc", -// "application/grpc+", or "application/grpc;", the boolean will be true, -// but no content-subtype will be returned. -// -// contentType is assumed to be lowercase already. -func contentSubtype(contentType string) (string, bool) { - if contentType == baseContentType { - return "", true - } - if !strings.HasPrefix(contentType, baseContentType) { - return "", false - } - // guaranteed since != baseContentType and has baseContentType prefix - switch contentType[len(baseContentType)] { - case '+', ';': - // this will return true for "application/grpc+" or "application/grpc;" - // which the previous validContentType function tested to be valid, so we - // just say that no content-subtype is specified in this case - return contentType[len(baseContentType)+1:], true - default: - return "", false - } -} - -// contentSubtype is assumed to be lowercase -func contentType(contentSubtype string) string { - if contentSubtype == "" { - return baseContentType - } - return baseContentType + "+" + contentSubtype -} - -func (d *decodeState) status() *status.Status { - if d.statusGen == nil { - // No status-details were provided; generate status using code/msg. - d.statusGen = status.New(codes.Code(int32(*(d.rawStatusCode))), d.rawStatusMsg) - } - return d.statusGen -} - -const binHdrSuffix = "-bin" - -func encodeBinHeader(v []byte) string { - return base64.RawStdEncoding.EncodeToString(v) -} - -func decodeBinHeader(v string) ([]byte, error) { - if len(v)%4 == 0 { - // Input was padded, or padding was not necessary. - return base64.StdEncoding.DecodeString(v) - } - return base64.RawStdEncoding.DecodeString(v) -} - -func encodeMetadataHeader(k, v string) string { - if strings.HasSuffix(k, binHdrSuffix) { - return encodeBinHeader(([]byte)(v)) - } - return v -} - -func decodeMetadataHeader(k, v string) (string, error) { - if strings.HasSuffix(k, binHdrSuffix) { - b, err := decodeBinHeader(v) - return string(b), err - } - return v, nil -} - -func (d *decodeState) decodeResponseHeader(frame *http2.MetaHeadersFrame) error { - for _, hf := range frame.Fields { - if err := d.processHeaderField(hf); err != nil { - return err - } - } - - // If grpc status exists, no need to check further. - if d.rawStatusCode != nil || d.statusGen != nil { - return nil - } - - // If grpc status doesn't exist and http status doesn't exist, - // then it's a malformed header. - if d.httpStatus == nil { - return streamErrorf(codes.Internal, "malformed header: doesn't contain status(gRPC or HTTP)") - } - - if *(d.httpStatus) != http.StatusOK { - code, ok := httpStatusConvTab[*(d.httpStatus)] - if !ok { - code = codes.Unknown - } - return streamErrorf(code, http.StatusText(*(d.httpStatus))) - } - - // gRPC status doesn't exist and http status is OK. - // Set rawStatusCode to be unknown and return nil error. - // So that, if the stream has ended this Unknown status - // will be propagated to the user. - // Otherwise, it will be ignored. In which case, status from - // a later trailer, that has StreamEnded flag set, is propagated. - code := int(codes.Unknown) - d.rawStatusCode = &code - return nil - -} - -func (d *decodeState) addMetadata(k, v string) { - if d.mdata == nil { - d.mdata = make(map[string][]string) - } - d.mdata[k] = append(d.mdata[k], v) -} - -func (d *decodeState) processHeaderField(f hpack.HeaderField) error { - switch f.Name { - case "content-type": - contentSubtype, validContentType := contentSubtype(f.Value) - if !validContentType { - return streamErrorf(codes.Internal, "transport: received the unexpected content-type %q", f.Value) - } - d.contentSubtype = contentSubtype - // TODO: do we want to propagate the whole content-type in the metadata, - // or come up with a way to just propagate the content-subtype if it was set? - // ie {"content-type": "application/grpc+proto"} or {"content-subtype": "proto"} - // in the metadata? - d.addMetadata(f.Name, f.Value) - case "grpc-encoding": - d.encoding = f.Value - case "grpc-status": - code, err := strconv.Atoi(f.Value) - if err != nil { - return streamErrorf(codes.Internal, "transport: malformed grpc-status: %v", err) - } - d.rawStatusCode = &code - case "grpc-message": - d.rawStatusMsg = decodeGrpcMessage(f.Value) - case "grpc-status-details-bin": - v, err := decodeBinHeader(f.Value) - if err != nil { - return streamErrorf(codes.Internal, "transport: malformed grpc-status-details-bin: %v", err) - } - s := &spb.Status{} - if err := proto.Unmarshal(v, s); err != nil { - return streamErrorf(codes.Internal, "transport: malformed grpc-status-details-bin: %v", err) - } - d.statusGen = status.FromProto(s) - case "grpc-timeout": - d.timeoutSet = true - var err error - if d.timeout, err = decodeTimeout(f.Value); err != nil { - return streamErrorf(codes.Internal, "transport: malformed time-out: %v", err) - } - case ":path": - d.method = f.Value - case ":status": - code, err := strconv.Atoi(f.Value) - if err != nil { - return streamErrorf(codes.Internal, "transport: malformed http-status: %v", err) - } - d.httpStatus = &code - case "grpc-tags-bin": - v, err := decodeBinHeader(f.Value) - if err != nil { - return streamErrorf(codes.Internal, "transport: malformed grpc-tags-bin: %v", err) - } - d.statsTags = v - d.addMetadata(f.Name, string(v)) - case "grpc-trace-bin": - v, err := decodeBinHeader(f.Value) - if err != nil { - return streamErrorf(codes.Internal, "transport: malformed grpc-trace-bin: %v", err) - } - d.statsTrace = v - d.addMetadata(f.Name, string(v)) - default: - if isReservedHeader(f.Name) && !isWhitelistedHeader(f.Name) { - break - } - v, err := decodeMetadataHeader(f.Name, f.Value) - if err != nil { - errorf("Failed to decode metadata header (%q, %q): %v", f.Name, f.Value, err) - return nil - } - d.addMetadata(f.Name, v) - } - return nil -} - -type timeoutUnit uint8 - -const ( - hour timeoutUnit = 'H' - minute timeoutUnit = 'M' - second timeoutUnit = 'S' - millisecond timeoutUnit = 'm' - microsecond timeoutUnit = 'u' - nanosecond timeoutUnit = 'n' -) - -func timeoutUnitToDuration(u timeoutUnit) (d time.Duration, ok bool) { - switch u { - case hour: - return time.Hour, true - case minute: - return time.Minute, true - case second: - return time.Second, true - case millisecond: - return time.Millisecond, true - case microsecond: - return time.Microsecond, true - case nanosecond: - return time.Nanosecond, true - default: - } - return -} - -const maxTimeoutValue int64 = 100000000 - 1 - -// div does integer division and round-up the result. Note that this is -// equivalent to (d+r-1)/r but has less chance to overflow. -func div(d, r time.Duration) int64 { - if m := d % r; m > 0 { - return int64(d/r + 1) - } - return int64(d / r) -} - -// TODO(zhaoq): It is the simplistic and not bandwidth efficient. Improve it. -func encodeTimeout(t time.Duration) string { - if t <= 0 { - return "0n" - } - if d := div(t, time.Nanosecond); d <= maxTimeoutValue { - return strconv.FormatInt(d, 10) + "n" - } - if d := div(t, time.Microsecond); d <= maxTimeoutValue { - return strconv.FormatInt(d, 10) + "u" - } - if d := div(t, time.Millisecond); d <= maxTimeoutValue { - return strconv.FormatInt(d, 10) + "m" - } - if d := div(t, time.Second); d <= maxTimeoutValue { - return strconv.FormatInt(d, 10) + "S" - } - if d := div(t, time.Minute); d <= maxTimeoutValue { - return strconv.FormatInt(d, 10) + "M" - } - // Note that maxTimeoutValue * time.Hour > MaxInt64. - return strconv.FormatInt(div(t, time.Hour), 10) + "H" -} - -func decodeTimeout(s string) (time.Duration, error) { - size := len(s) - if size < 2 { - return 0, fmt.Errorf("transport: timeout string is too short: %q", s) - } - unit := timeoutUnit(s[size-1]) - d, ok := timeoutUnitToDuration(unit) - if !ok { - return 0, fmt.Errorf("transport: timeout unit is not recognized: %q", s) - } - t, err := strconv.ParseInt(s[:size-1], 10, 64) - if err != nil { - return 0, err - } - return d * time.Duration(t), nil -} - -const ( - spaceByte = ' ' - tildaByte = '~' - percentByte = '%' -) - -// encodeGrpcMessage is used to encode status code in header field -// "grpc-message". -// It checks to see if each individual byte in msg is an -// allowable byte, and then either percent encoding or passing it through. -// When percent encoding, the byte is converted into hexadecimal notation -// with a '%' prepended. -func encodeGrpcMessage(msg string) string { - if msg == "" { - return "" - } - lenMsg := len(msg) - for i := 0; i < lenMsg; i++ { - c := msg[i] - if !(c >= spaceByte && c < tildaByte && c != percentByte) { - return encodeGrpcMessageUnchecked(msg) - } - } - return msg -} - -func encodeGrpcMessageUnchecked(msg string) string { - var buf bytes.Buffer - lenMsg := len(msg) - for i := 0; i < lenMsg; i++ { - c := msg[i] - if c >= spaceByte && c < tildaByte && c != percentByte { - buf.WriteByte(c) - } else { - buf.WriteString(fmt.Sprintf("%%%02X", c)) - } - } - return buf.String() -} - -// decodeGrpcMessage decodes the msg encoded by encodeGrpcMessage. -func decodeGrpcMessage(msg string) string { - if msg == "" { - return "" - } - lenMsg := len(msg) - for i := 0; i < lenMsg; i++ { - if msg[i] == percentByte && i+2 < lenMsg { - return decodeGrpcMessageUnchecked(msg) - } - } - return msg -} - -func decodeGrpcMessageUnchecked(msg string) string { - var buf bytes.Buffer - lenMsg := len(msg) - for i := 0; i < lenMsg; i++ { - c := msg[i] - if c == percentByte && i+2 < lenMsg { - parsed, err := strconv.ParseUint(msg[i+1:i+3], 16, 8) - if err != nil { - buf.WriteByte(c) - } else { - buf.WriteByte(byte(parsed)) - i += 2 - } - } else { - buf.WriteByte(c) - } - } - return buf.String() -} - -type bufWriter struct { - buf []byte - offset int - batchSize int - conn net.Conn - err error - - onFlush func() -} - -func newBufWriter(conn net.Conn, batchSize int) *bufWriter { - return &bufWriter{ - buf: make([]byte, batchSize*2), - batchSize: batchSize, - conn: conn, - } -} - -func (w *bufWriter) Write(b []byte) (n int, err error) { - if w.err != nil { - return 0, w.err - } - n = copy(w.buf[w.offset:], b) - w.offset += n - if w.offset >= w.batchSize { - err = w.Flush() - } - return n, err -} - -func (w *bufWriter) Flush() error { - if w.err != nil { - return w.err - } - if w.offset == 0 { - return nil - } - if w.onFlush != nil { - w.onFlush() - } - _, w.err = w.conn.Write(w.buf[:w.offset]) - w.offset = 0 - return w.err -} - -type framer struct { - writer *bufWriter - fr *http2.Framer -} - -func newFramer(conn net.Conn, writeBufferSize, readBufferSize int) *framer { - r := bufio.NewReaderSize(conn, readBufferSize) - w := newBufWriter(conn, writeBufferSize) - f := &framer{ - writer: w, - fr: http2.NewFramer(w, r), - } - // Opt-in to Frame reuse API on framer to reduce garbage. - // Frames aren't safe to read from after a subsequent call to ReadFrame. - f.fr.SetReuseFrames() - f.fr.ReadMetaHeaders = hpack.NewDecoder(http2InitHeaderTableSize, nil) - return f -} diff --git a/vendor/google.golang.org/grpc/transport/log.go b/vendor/google.golang.org/grpc/transport/log.go deleted file mode 100644 index ac8e358c5c8..00000000000 --- a/vendor/google.golang.org/grpc/transport/log.go +++ /dev/null @@ -1,50 +0,0 @@ -/* - * - * Copyright 2017 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// This file contains wrappers for grpclog functions. -// The transport package only logs to verbose level 2 by default. - -package transport - -import "google.golang.org/grpc/grpclog" - -const logLevel = 2 - -func infof(format string, args ...interface{}) { - if grpclog.V(logLevel) { - grpclog.Infof(format, args...) - } -} - -func warningf(format string, args ...interface{}) { - if grpclog.V(logLevel) { - grpclog.Warningf(format, args...) - } -} - -func errorf(format string, args ...interface{}) { - if grpclog.V(logLevel) { - grpclog.Errorf(format, args...) - } -} - -func fatalf(format string, args ...interface{}) { - if grpclog.V(logLevel) { - grpclog.Fatalf(format, args...) - } -} diff --git a/vendor/google.golang.org/grpc/transport/transport.go b/vendor/google.golang.org/grpc/transport/transport.go deleted file mode 100644 index 2f643a3d042..00000000000 --- a/vendor/google.golang.org/grpc/transport/transport.go +++ /dev/null @@ -1,683 +0,0 @@ -/* - * - * Copyright 2014 gRPC authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -// Package transport defines and implements message oriented communication -// channel to complete various transactions (e.g., an RPC). It is meant for -// grpc-internal usage and is not intended to be imported directly by users. -package transport // externally used as import "google.golang.org/grpc/transport" - -import ( - "errors" - "fmt" - "io" - "net" - "sync" - "sync/atomic" - - "golang.org/x/net/context" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/keepalive" - "google.golang.org/grpc/metadata" - "google.golang.org/grpc/stats" - "google.golang.org/grpc/status" - "google.golang.org/grpc/tap" -) - -// recvMsg represents the received msg from the transport. All transport -// protocol specific info has been removed. -type recvMsg struct { - data []byte - // nil: received some data - // io.EOF: stream is completed. data is nil. - // other non-nil error: transport failure. data is nil. - err error -} - -// recvBuffer is an unbounded channel of recvMsg structs. -// Note recvBuffer differs from controlBuffer only in that recvBuffer -// holds a channel of only recvMsg structs instead of objects implementing "item" interface. -// recvBuffer is written to much more often than -// controlBuffer and using strict recvMsg structs helps avoid allocation in "recvBuffer.put" -type recvBuffer struct { - c chan recvMsg - mu sync.Mutex - backlog []recvMsg - err error -} - -func newRecvBuffer() *recvBuffer { - b := &recvBuffer{ - c: make(chan recvMsg, 1), - } - return b -} - -func (b *recvBuffer) put(r recvMsg) { - b.mu.Lock() - if b.err != nil { - b.mu.Unlock() - // An error had occurred earlier, don't accept more - // data or errors. - return - } - b.err = r.err - if len(b.backlog) == 0 { - select { - case b.c <- r: - b.mu.Unlock() - return - default: - } - } - b.backlog = append(b.backlog, r) - b.mu.Unlock() -} - -func (b *recvBuffer) load() { - b.mu.Lock() - if len(b.backlog) > 0 { - select { - case b.c <- b.backlog[0]: - b.backlog[0] = recvMsg{} - b.backlog = b.backlog[1:] - default: - } - } - b.mu.Unlock() -} - -// get returns the channel that receives a recvMsg in the buffer. -// -// Upon receipt of a recvMsg, the caller should call load to send another -// recvMsg onto the channel if there is any. -func (b *recvBuffer) get() <-chan recvMsg { - return b.c -} - -// -// recvBufferReader implements io.Reader interface to read the data from -// recvBuffer. -type recvBufferReader struct { - ctx context.Context - ctxDone <-chan struct{} // cache of ctx.Done() (for performance). - recv *recvBuffer - last []byte // Stores the remaining data in the previous calls. - err error -} - -// Read reads the next len(p) bytes from last. If last is drained, it tries to -// read additional data from recv. It blocks if there no additional data available -// in recv. If Read returns any non-nil error, it will continue to return that error. -func (r *recvBufferReader) Read(p []byte) (n int, err error) { - if r.err != nil { - return 0, r.err - } - n, r.err = r.read(p) - return n, r.err -} - -func (r *recvBufferReader) read(p []byte) (n int, err error) { - if r.last != nil && len(r.last) > 0 { - // Read remaining data left in last call. - copied := copy(p, r.last) - r.last = r.last[copied:] - return copied, nil - } - select { - case <-r.ctxDone: - return 0, ContextErr(r.ctx.Err()) - case m := <-r.recv.get(): - r.recv.load() - if m.err != nil { - return 0, m.err - } - copied := copy(p, m.data) - r.last = m.data[copied:] - return copied, nil - } -} - -type streamState uint32 - -const ( - streamActive streamState = iota - streamWriteDone // EndStream sent - streamReadDone // EndStream received - streamDone // the entire stream is finished. -) - -// Stream represents an RPC in the transport layer. -type Stream struct { - id uint32 - st ServerTransport // nil for client side Stream - ctx context.Context // the associated context of the stream - cancel context.CancelFunc // always nil for client side Stream - done chan struct{} // closed at the end of stream to unblock writers. On the client side. - ctxDone <-chan struct{} // same as done chan but for server side. Cache of ctx.Done() (for performance) - method string // the associated RPC method of the stream - recvCompress string - sendCompress string - buf *recvBuffer - trReader io.Reader - fc *inFlow - recvQuota uint32 - wq *writeQuota - - // Callback to state application's intentions to read data. This - // is used to adjust flow control, if needed. - requestRead func(int) - - headerChan chan struct{} // closed to indicate the end of header metadata. - headerDone uint32 // set when headerChan is closed. Used to avoid closing headerChan multiple times. - header metadata.MD // the received header metadata. - trailer metadata.MD // the key-value map of trailer metadata. - - headerOk bool // becomes true from the first header is about to send - state streamState - - status *status.Status // the status error received from the server - - bytesReceived uint32 // indicates whether any bytes have been received on this stream - unprocessed uint32 // set if the server sends a refused stream or GOAWAY including this stream - - // contentSubtype is the content-subtype for requests. - // this must be lowercase or the behavior is undefined. - contentSubtype string -} - -func (s *Stream) swapState(st streamState) streamState { - return streamState(atomic.SwapUint32((*uint32)(&s.state), uint32(st))) -} - -func (s *Stream) compareAndSwapState(oldState, newState streamState) bool { - return atomic.CompareAndSwapUint32((*uint32)(&s.state), uint32(oldState), uint32(newState)) -} - -func (s *Stream) getState() streamState { - return streamState(atomic.LoadUint32((*uint32)(&s.state))) -} - -func (s *Stream) waitOnHeader() error { - if s.headerChan == nil { - // On the server headerChan is always nil since a stream originates - // only after having received headers. - return nil - } - select { - case <-s.ctx.Done(): - return ContextErr(s.ctx.Err()) - case <-s.headerChan: - return nil - } -} - -// RecvCompress returns the compression algorithm applied to the inbound -// message. It is empty string if there is no compression applied. -func (s *Stream) RecvCompress() string { - if err := s.waitOnHeader(); err != nil { - return "" - } - return s.recvCompress -} - -// SetSendCompress sets the compression algorithm to the stream. -func (s *Stream) SetSendCompress(str string) { - s.sendCompress = str -} - -// Done returns a chanel which is closed when it receives the final status -// from the server. -func (s *Stream) Done() <-chan struct{} { - return s.done -} - -// Header acquires the key-value pairs of header metadata once it -// is available. It blocks until i) the metadata is ready or ii) there is no -// header metadata or iii) the stream is canceled/expired. -func (s *Stream) Header() (metadata.MD, error) { - err := s.waitOnHeader() - // Even if the stream is closed, header is returned if available. - select { - case <-s.headerChan: - if s.header == nil { - return nil, nil - } - return s.header.Copy(), nil - default: - } - return nil, err -} - -// Trailer returns the cached trailer metedata. Note that if it is not called -// after the entire stream is done, it could return an empty MD. Client -// side only. -// It can be safely read only after stream has ended that is either read -// or write have returned io.EOF. -func (s *Stream) Trailer() metadata.MD { - c := s.trailer.Copy() - return c -} - -// ServerTransport returns the underlying ServerTransport for the stream. -// The client side stream always returns nil. -func (s *Stream) ServerTransport() ServerTransport { - return s.st -} - -// ContentSubtype returns the content-subtype for a request. For example, a -// content-subtype of "proto" will result in a content-type of -// "application/grpc+proto". This will always be lowercase. See -// https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests for -// more details. -func (s *Stream) ContentSubtype() string { - return s.contentSubtype -} - -// Context returns the context of the stream. -func (s *Stream) Context() context.Context { - return s.ctx -} - -// Method returns the method for the stream. -func (s *Stream) Method() string { - return s.method -} - -// Status returns the status received from the server. -// Status can be read safely only after the stream has ended, -// that is, read or write has returned io.EOF. -func (s *Stream) Status() *status.Status { - return s.status -} - -// SetHeader sets the header metadata. This can be called multiple times. -// Server side only. -// This should not be called in parallel to other data writes. -func (s *Stream) SetHeader(md metadata.MD) error { - if md.Len() == 0 { - return nil - } - if s.headerOk || atomic.LoadUint32((*uint32)(&s.state)) == uint32(streamDone) { - return ErrIllegalHeaderWrite - } - s.header = metadata.Join(s.header, md) - return nil -} - -// SendHeader sends the given header metadata. The given metadata is -// combined with any metadata set by previous calls to SetHeader and -// then written to the transport stream. -func (s *Stream) SendHeader(md metadata.MD) error { - t := s.ServerTransport() - return t.WriteHeader(s, md) -} - -// SetTrailer sets the trailer metadata which will be sent with the RPC status -// by the server. This can be called multiple times. Server side only. -// This should not be called parallel to other data writes. -func (s *Stream) SetTrailer(md metadata.MD) error { - if md.Len() == 0 { - return nil - } - s.trailer = metadata.Join(s.trailer, md) - return nil -} - -func (s *Stream) write(m recvMsg) { - s.buf.put(m) -} - -// Read reads all p bytes from the wire for this stream. -func (s *Stream) Read(p []byte) (n int, err error) { - // Don't request a read if there was an error earlier - if er := s.trReader.(*transportReader).er; er != nil { - return 0, er - } - s.requestRead(len(p)) - return io.ReadFull(s.trReader, p) -} - -// tranportReader reads all the data available for this Stream from the transport and -// passes them into the decoder, which converts them into a gRPC message stream. -// The error is io.EOF when the stream is done or another non-nil error if -// the stream broke. -type transportReader struct { - reader io.Reader - // The handler to control the window update procedure for both this - // particular stream and the associated transport. - windowHandler func(int) - er error -} - -func (t *transportReader) Read(p []byte) (n int, err error) { - n, err = t.reader.Read(p) - if err != nil { - t.er = err - return - } - t.windowHandler(n) - return -} - -// BytesReceived indicates whether any bytes have been received on this stream. -func (s *Stream) BytesReceived() bool { - return atomic.LoadUint32(&s.bytesReceived) == 1 -} - -// Unprocessed indicates whether the server did not process this stream -- -// i.e. it sent a refused stream or GOAWAY including this stream ID. -func (s *Stream) Unprocessed() bool { - return atomic.LoadUint32(&s.unprocessed) == 1 -} - -// GoString is implemented by Stream so context.String() won't -// race when printing %#v. -func (s *Stream) GoString() string { - return fmt.Sprintf("", s, s.method) -} - -// state of transport -type transportState int - -const ( - reachable transportState = iota - closing - draining -) - -// ServerConfig consists of all the configurations to establish a server transport. -type ServerConfig struct { - MaxStreams uint32 - AuthInfo credentials.AuthInfo - InTapHandle tap.ServerInHandle - StatsHandler stats.Handler - KeepaliveParams keepalive.ServerParameters - KeepalivePolicy keepalive.EnforcementPolicy - InitialWindowSize int32 - InitialConnWindowSize int32 - WriteBufferSize int - ReadBufferSize int - ChannelzParentID int64 -} - -// NewServerTransport creates a ServerTransport with conn or non-nil error -// if it fails. -func NewServerTransport(protocol string, conn net.Conn, config *ServerConfig) (ServerTransport, error) { - return newHTTP2Server(conn, config) -} - -// ConnectOptions covers all relevant options for communicating with the server. -type ConnectOptions struct { - // UserAgent is the application user agent. - UserAgent string - // Authority is the :authority pseudo-header to use. This field has no effect if - // TransportCredentials is set. - Authority string - // Dialer specifies how to dial a network address. - Dialer func(context.Context, string) (net.Conn, error) - // FailOnNonTempDialError specifies if gRPC fails on non-temporary dial errors. - FailOnNonTempDialError bool - // PerRPCCredentials stores the PerRPCCredentials required to issue RPCs. - PerRPCCredentials []credentials.PerRPCCredentials - // TransportCredentials stores the Authenticator required to setup a client connection. - TransportCredentials credentials.TransportCredentials - // KeepaliveParams stores the keepalive parameters. - KeepaliveParams keepalive.ClientParameters - // StatsHandler stores the handler for stats. - StatsHandler stats.Handler - // InitialWindowSize sets the initial window size for a stream. - InitialWindowSize int32 - // InitialConnWindowSize sets the initial window size for a connection. - InitialConnWindowSize int32 - // WriteBufferSize sets the size of write buffer which in turn determines how much data can be batched before it's written on the wire. - WriteBufferSize int - // ReadBufferSize sets the size of read buffer, which in turn determines how much data can be read at most for one read syscall. - ReadBufferSize int - // ChannelzParentID sets the addrConn id which initiate the creation of this client transport. - ChannelzParentID int64 -} - -// TargetInfo contains the information of the target such as network address and metadata. -type TargetInfo struct { - Addr string - Metadata interface{} - Authority string -} - -// NewClientTransport establishes the transport with the required ConnectOptions -// and returns it to the caller. -func NewClientTransport(connectCtx, ctx context.Context, target TargetInfo, opts ConnectOptions, onSuccess func()) (ClientTransport, error) { - return newHTTP2Client(connectCtx, ctx, target, opts, onSuccess) -} - -// Options provides additional hints and information for message -// transmission. -type Options struct { - // Last indicates whether this write is the last piece for - // this stream. - Last bool - - // Delay is a hint to the transport implementation for whether - // the data could be buffered for a batching write. The - // transport implementation may ignore the hint. - Delay bool -} - -// CallHdr carries the information of a particular RPC. -type CallHdr struct { - // Host specifies the peer's host. - Host string - - // Method specifies the operation to perform. - Method string - - // SendCompress specifies the compression algorithm applied on - // outbound message. - SendCompress string - - // Creds specifies credentials.PerRPCCredentials for a call. - Creds credentials.PerRPCCredentials - - // Flush indicates whether a new stream command should be sent - // to the peer without waiting for the first data. This is - // only a hint. - // If it's true, the transport may modify the flush decision - // for performance purposes. - // If it's false, new stream will never be flushed. - Flush bool - - // ContentSubtype specifies the content-subtype for a request. For example, a - // content-subtype of "proto" will result in a content-type of - // "application/grpc+proto". The value of ContentSubtype must be all - // lowercase, otherwise the behavior is undefined. See - // https://github.com/grpc/grpc/blob/master/doc/PROTOCOL-HTTP2.md#requests - // for more details. - ContentSubtype string -} - -// ClientTransport is the common interface for all gRPC client-side transport -// implementations. -type ClientTransport interface { - // Close tears down this transport. Once it returns, the transport - // should not be accessed any more. The caller must make sure this - // is called only once. - Close() error - - // GracefulClose starts to tear down the transport. It stops accepting - // new RPCs and wait the completion of the pending RPCs. - GracefulClose() error - - // Write sends the data for the given stream. A nil stream indicates - // the write is to be performed on the transport as a whole. - Write(s *Stream, hdr []byte, data []byte, opts *Options) error - - // NewStream creates a Stream for an RPC. - NewStream(ctx context.Context, callHdr *CallHdr) (*Stream, error) - - // CloseStream clears the footprint of a stream when the stream is - // not needed any more. The err indicates the error incurred when - // CloseStream is called. Must be called when a stream is finished - // unless the associated transport is closing. - CloseStream(stream *Stream, err error) - - // Error returns a channel that is closed when some I/O error - // happens. Typically the caller should have a goroutine to monitor - // this in order to take action (e.g., close the current transport - // and create a new one) in error case. It should not return nil - // once the transport is initiated. - Error() <-chan struct{} - - // GoAway returns a channel that is closed when ClientTransport - // receives the draining signal from the server (e.g., GOAWAY frame in - // HTTP/2). - GoAway() <-chan struct{} - - // GetGoAwayReason returns the reason why GoAway frame was received. - GetGoAwayReason() GoAwayReason - - // IncrMsgSent increments the number of message sent through this transport. - IncrMsgSent() - - // IncrMsgRecv increments the number of message received through this transport. - IncrMsgRecv() -} - -// ServerTransport is the common interface for all gRPC server-side transport -// implementations. -// -// Methods may be called concurrently from multiple goroutines, but -// Write methods for a given Stream will be called serially. -type ServerTransport interface { - // HandleStreams receives incoming streams using the given handler. - HandleStreams(func(*Stream), func(context.Context, string) context.Context) - - // WriteHeader sends the header metadata for the given stream. - // WriteHeader may not be called on all streams. - WriteHeader(s *Stream, md metadata.MD) error - - // Write sends the data for the given stream. - // Write may not be called on all streams. - Write(s *Stream, hdr []byte, data []byte, opts *Options) error - - // WriteStatus sends the status of a stream to the client. WriteStatus is - // the final call made on a stream and always occurs. - WriteStatus(s *Stream, st *status.Status) error - - // Close tears down the transport. Once it is called, the transport - // should not be accessed any more. All the pending streams and their - // handlers will be terminated asynchronously. - Close() error - - // RemoteAddr returns the remote network address. - RemoteAddr() net.Addr - - // Drain notifies the client this ServerTransport stops accepting new RPCs. - Drain() - - // IncrMsgSent increments the number of message sent through this transport. - IncrMsgSent() - - // IncrMsgRecv increments the number of message received through this transport. - IncrMsgRecv() -} - -// streamErrorf creates an StreamError with the specified error code and description. -func streamErrorf(c codes.Code, format string, a ...interface{}) StreamError { - return StreamError{ - Code: c, - Desc: fmt.Sprintf(format, a...), - } -} - -// connectionErrorf creates an ConnectionError with the specified error description. -func connectionErrorf(temp bool, e error, format string, a ...interface{}) ConnectionError { - return ConnectionError{ - Desc: fmt.Sprintf(format, a...), - temp: temp, - err: e, - } -} - -// ConnectionError is an error that results in the termination of the -// entire connection and the retry of all the active streams. -type ConnectionError struct { - Desc string - temp bool - err error -} - -func (e ConnectionError) Error() string { - return fmt.Sprintf("connection error: desc = %q", e.Desc) -} - -// Temporary indicates if this connection error is temporary or fatal. -func (e ConnectionError) Temporary() bool { - return e.temp -} - -// Origin returns the original error of this connection error. -func (e ConnectionError) Origin() error { - // Never return nil error here. - // If the original error is nil, return itself. - if e.err == nil { - return e - } - return e.err -} - -var ( - // ErrConnClosing indicates that the transport is closing. - ErrConnClosing = connectionErrorf(true, nil, "transport is closing") - // errStreamDrain indicates that the stream is rejected because the - // connection is draining. This could be caused by goaway or balancer - // removing the address. - errStreamDrain = streamErrorf(codes.Unavailable, "the connection is draining") - // errStreamDone is returned from write at the client side to indiacte application - // layer of an error. - errStreamDone = errors.New("the stream is done") - // StatusGoAway indicates that the server sent a GOAWAY that included this - // stream's ID in unprocessed RPCs. - statusGoAway = status.New(codes.Unavailable, "the stream is rejected because server is draining the connection") -) - -// TODO: See if we can replace StreamError with status package errors. - -// StreamError is an error that only affects one stream within a connection. -type StreamError struct { - Code codes.Code - Desc string -} - -func (e StreamError) Error() string { - return fmt.Sprintf("stream error: code = %s desc = %q", e.Code, e.Desc) -} - -// GoAwayReason contains the reason for the GoAway frame received. -type GoAwayReason uint8 - -const ( - // GoAwayInvalid indicates that no GoAway frame is received. - GoAwayInvalid GoAwayReason = 0 - // GoAwayNoReason is the default value when GoAway frame is received. - GoAwayNoReason GoAwayReason = 1 - // GoAwayTooManyPings indicates that a GoAway frame with - // ErrCodeEnhanceYourCalm was received and that the debug data said - // "too_many_pings". - GoAwayTooManyPings GoAwayReason = 2 -) diff --git a/vendor/gopkg.in/go-playground/webhooks.v3/LICENSE b/vendor/gopkg.in/go-playground/webhooks.v3/LICENSE deleted file mode 100644 index 6a2ae9aa4da..00000000000 --- a/vendor/gopkg.in/go-playground/webhooks.v3/LICENSE +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Dean Karn - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/vendor/gopkg.in/go-playground/webhooks.v3/github/github.go b/vendor/gopkg.in/go-playground/webhooks.v3/github/github.go deleted file mode 100644 index 2b5c9330e62..00000000000 --- a/vendor/gopkg.in/go-playground/webhooks.v3/github/github.go +++ /dev/null @@ -1,292 +0,0 @@ -package github - -import ( - "crypto/hmac" - "crypto/sha1" - "encoding/hex" - "encoding/json" - "fmt" - "io/ioutil" - "net/http" - - "gopkg.in/go-playground/webhooks.v3" -) - -// Webhook instance contains all methods needed to process events -type Webhook struct { - provider webhooks.Provider - secret string - eventFuncs map[Event]webhooks.ProcessPayloadFunc -} - -// Config defines the configuration to create a new GitHub Webhook instance -type Config struct { - Secret string -} - -// Event defines a GitHub hook event type -type Event string - -// GitHub hook types -const ( - CommitCommentEvent Event = "commit_comment" - CreateEvent Event = "create" - DeleteEvent Event = "delete" - DeploymentEvent Event = "deployment" - DeploymentStatusEvent Event = "deployment_status" - ForkEvent Event = "fork" - GollumEvent Event = "gollum" - InstallationEvent Event = "installation" - IntegrationInstallationEvent Event = "integration_installation" - IssueCommentEvent Event = "issue_comment" - IssuesEvent Event = "issues" - LabelEvent Event = "label" - MemberEvent Event = "member" - MembershipEvent Event = "membership" - MilestoneEvent Event = "milestone" - OrganizationEvent Event = "organization" - OrgBlockEvent Event = "org_block" - PageBuildEvent Event = "page_build" - PingEvent Event = "ping" - ProjectCardEvent Event = "project_card" - ProjectColumnEvent Event = "project_column" - ProjectEvent Event = "project" - PublicEvent Event = "public" - PullRequestEvent Event = "pull_request" - PullRequestReviewEvent Event = "pull_request_review" - PullRequestReviewCommentEvent Event = "pull_request_review_comment" - PushEvent Event = "push" - ReleaseEvent Event = "release" - RepositoryEvent Event = "repository" - StatusEvent Event = "status" - TeamEvent Event = "team" - TeamAddEvent Event = "team_add" - WatchEvent Event = "watch" -) - -// EventSubtype defines a GitHub Hook Event subtype -type EventSubtype string - -// GitHub hook event subtypes -const ( - NoSubtype EventSubtype = "" - BranchSubtype EventSubtype = "branch" - TagSubtype EventSubtype = "tag" - PullSubtype EventSubtype = "pull" - IssueSubtype EventSubtype = "issues" -) - -// New creates and returns a WebHook instance denoted by the Provider type -func New(config *Config) *Webhook { - return &Webhook{ - provider: webhooks.GitHub, - secret: config.Secret, - eventFuncs: map[Event]webhooks.ProcessPayloadFunc{}, - } -} - -// Provider returns the current hooks provider ID -func (hook Webhook) Provider() webhooks.Provider { - return hook.provider -} - -// RegisterEvents registers the function to call when the specified event(s) are encountered -func (hook Webhook) RegisterEvents(fn webhooks.ProcessPayloadFunc, events ...Event) { - - for _, event := range events { - hook.eventFuncs[event] = fn - } -} - -// ParsePayload parses and verifies the payload and fires off the mapped function, if it exists. -func (hook Webhook) ParsePayload(w http.ResponseWriter, r *http.Request) { - webhooks.DefaultLog.Info("Parsing Payload...") - - event := r.Header.Get("X-GitHub-Event") - if len(event) == 0 { - webhooks.DefaultLog.Error("Missing X-GitHub-Event Header") - http.Error(w, "400 Bad Request - Missing X-GitHub-Event Header", http.StatusBadRequest) - return - } - webhooks.DefaultLog.Debug(fmt.Sprintf("X-GitHub-Event:%s", event)) - - gitHubEvent := Event(event) - - fn, ok := hook.eventFuncs[gitHubEvent] - // if no event registered - if !ok { - webhooks.DefaultLog.Info(fmt.Sprintf("Webhook Event %s not registered, it is recommended to setup only events in github that will be registered in the webhook to avoid unnecessary traffic and reduce potential attack vectors.", event)) - return - } - - payload, err := ioutil.ReadAll(r.Body) - if err != nil || len(payload) == 0 { - webhooks.DefaultLog.Error("Issue reading Payload") - http.Error(w, "Issue reading Payload", http.StatusInternalServerError) - return - } - webhooks.DefaultLog.Debug(fmt.Sprintf("Payload:%s", string(payload))) - - // If we have a Secret set, we should check the MAC - if len(hook.secret) > 0 { - webhooks.DefaultLog.Info("Checking secret") - signature := r.Header.Get("X-Hub-Signature") - if len(signature) == 0 { - webhooks.DefaultLog.Error("Missing X-Hub-Signature required for HMAC verification") - http.Error(w, "403 Forbidden - Missing X-Hub-Signature required for HMAC verification", http.StatusForbidden) - return - } - webhooks.DefaultLog.Debug(fmt.Sprintf("X-Hub-Signature:%s", signature)) - - mac := hmac.New(sha1.New, []byte(hook.secret)) - mac.Write(payload) - - expectedMAC := hex.EncodeToString(mac.Sum(nil)) - - if !hmac.Equal([]byte(signature[5:]), []byte(expectedMAC)) { - webhooks.DefaultLog.Error("HMAC verification failed") - http.Error(w, "403 Forbidden - HMAC verification failed", http.StatusForbidden) - return - } - } - - // Make headers available to ProcessPayloadFunc as a webhooks type - hd := webhooks.Header(r.Header) - - switch gitHubEvent { - case CommitCommentEvent: - var cc CommitCommentPayload - json.Unmarshal([]byte(payload), &cc) - hook.runProcessPayloadFunc(fn, cc, hd) - case CreateEvent: - var c CreatePayload - json.Unmarshal([]byte(payload), &c) - hook.runProcessPayloadFunc(fn, c, hd) - case DeleteEvent: - var d DeletePayload - json.Unmarshal([]byte(payload), &d) - hook.runProcessPayloadFunc(fn, d, hd) - case DeploymentEvent: - var d DeploymentPayload - json.Unmarshal([]byte(payload), &d) - hook.runProcessPayloadFunc(fn, d, hd) - case DeploymentStatusEvent: - var d DeploymentStatusPayload - json.Unmarshal([]byte(payload), &d) - hook.runProcessPayloadFunc(fn, d, hd) - case ForkEvent: - var f ForkPayload - json.Unmarshal([]byte(payload), &f) - hook.runProcessPayloadFunc(fn, f, hd) - case GollumEvent: - var g GollumPayload - json.Unmarshal([]byte(payload), &g) - hook.runProcessPayloadFunc(fn, g, hd) - case InstallationEvent, IntegrationInstallationEvent: - var i InstallationPayload - json.Unmarshal([]byte(payload), &i) - hook.runProcessPayloadFunc(fn, i, hd) - case IssueCommentEvent: - var i IssueCommentPayload - json.Unmarshal([]byte(payload), &i) - hook.runProcessPayloadFunc(fn, i, hd) - case IssuesEvent: - var i IssuesPayload - json.Unmarshal([]byte(payload), &i) - hook.runProcessPayloadFunc(fn, i, hd) - case LabelEvent: - var l LabelPayload - json.Unmarshal([]byte(payload), &l) - hook.runProcessPayloadFunc(fn, l, hd) - case MemberEvent: - var m MemberPayload - json.Unmarshal([]byte(payload), &m) - hook.runProcessPayloadFunc(fn, m, hd) - case MembershipEvent: - var m MembershipPayload - json.Unmarshal([]byte(payload), &m) - hook.runProcessPayloadFunc(fn, m, hd) - case MilestoneEvent: - var m MilestonePayload - json.Unmarshal([]byte(payload), &m) - hook.runProcessPayloadFunc(fn, m, hd) - case OrganizationEvent: - var o OrganizationPayload - json.Unmarshal([]byte(payload), &o) - hook.runProcessPayloadFunc(fn, o, hd) - case OrgBlockEvent: - var o OrgBlockPayload - json.Unmarshal([]byte(payload), &o) - hook.runProcessPayloadFunc(fn, o, hd) - case PageBuildEvent: - var p PageBuildPayload - json.Unmarshal([]byte(payload), &p) - hook.runProcessPayloadFunc(fn, p, hd) - case PingEvent: - var p PingPayload - json.Unmarshal([]byte(payload), &p) - hook.runProcessPayloadFunc(fn, p, hd) - case ProjectCardEvent: - var p ProjectCardPayload - json.Unmarshal([]byte(payload), &p) - hook.runProcessPayloadFunc(fn, p, hd) - case ProjectColumnEvent: - var p ProjectColumnPayload - json.Unmarshal([]byte(payload), &p) - hook.runProcessPayloadFunc(fn, p, hd) - case ProjectEvent: - var p ProjectPayload - json.Unmarshal([]byte(payload), &p) - hook.runProcessPayloadFunc(fn, p, hd) - case PublicEvent: - var p PublicPayload - json.Unmarshal([]byte(payload), &p) - hook.runProcessPayloadFunc(fn, p, hd) - case PullRequestEvent: - var p PullRequestPayload - json.Unmarshal([]byte(payload), &p) - hook.runProcessPayloadFunc(fn, p, hd) - case PullRequestReviewEvent: - var p PullRequestReviewPayload - json.Unmarshal([]byte(payload), &p) - hook.runProcessPayloadFunc(fn, p, hd) - case PullRequestReviewCommentEvent: - var p PullRequestReviewCommentPayload - json.Unmarshal([]byte(payload), &p) - hook.runProcessPayloadFunc(fn, p, hd) - case PushEvent: - var p PushPayload - json.Unmarshal([]byte(payload), &p) - hook.runProcessPayloadFunc(fn, p, hd) - case ReleaseEvent: - var r ReleasePayload - json.Unmarshal([]byte(payload), &r) - hook.runProcessPayloadFunc(fn, r, hd) - case RepositoryEvent: - var r RepositoryPayload - json.Unmarshal([]byte(payload), &r) - hook.runProcessPayloadFunc(fn, r, hd) - case StatusEvent: - var s StatusPayload - json.Unmarshal([]byte(payload), &s) - hook.runProcessPayloadFunc(fn, s, hd) - case TeamEvent: - var t TeamPayload - json.Unmarshal([]byte(payload), &t) - hook.runProcessPayloadFunc(fn, t, hd) - case TeamAddEvent: - var t TeamAddPayload - json.Unmarshal([]byte(payload), &t) - hook.runProcessPayloadFunc(fn, t, hd) - case WatchEvent: - var w WatchPayload - json.Unmarshal([]byte(payload), &w) - hook.runProcessPayloadFunc(fn, w, hd) - } -} - -func (hook Webhook) runProcessPayloadFunc(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) { - go func(fn webhooks.ProcessPayloadFunc, results interface{}, header webhooks.Header) { - fn(results, header) - }(fn, results, header) -} diff --git a/vendor/gopkg.in/go-playground/webhooks.v3/github/payload.go b/vendor/gopkg.in/go-playground/webhooks.v3/github/payload.go deleted file mode 100644 index 330c44916a4..00000000000 --- a/vendor/gopkg.in/go-playground/webhooks.v3/github/payload.go +++ /dev/null @@ -1,5087 +0,0 @@ -package github - -import "time" - -// CommitCommentPayload contains the information for GitHub's commit_comment hook event -type CommitCommentPayload struct { - Action string `json:"action"` - Comment struct { - URL string `json:"url"` - HTMLURL string `json:"html_url"` - ID int64 `json:"id"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Position *int64 `json:"position"` - Line *int64 `json:"line"` - Path *string `json:"path"` - CommitID string `json:"commit_id"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Body string `json:"body"` - AuthorAssociation string `json:"author_association"` - } `json:"comment"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// CreatePayload contains the information for GitHub's create hook event -type CreatePayload struct { - Ref string `json:"ref"` - RefType string `json:"ref_type"` - MasterBranch string `json:"master_branch"` - Description string `json:"description"` - PusherType string `json:"pusher_type"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// DeletePayload contains the information for GitHub's delete hook event -type DeletePayload struct { - Ref string `json:"ref"` - RefType string `json:"ref_type"` - PusherType string `json:"pusher_type"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// DeploymentPayload contains the information for GitHub's deployment hook -type DeploymentPayload struct { - Deployment struct { - URL string `json:"url"` - ID int64 `json:"id"` - Sha string `json:"sha"` - Ref string `json:"ref"` - Task string `json:"task"` - Payload struct { - } `json:"payload"` - Environment string `json:"environment"` - Description *string `json:"description"` - Creator struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"creator"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - StatusesURL string `json:"statuses_url"` - RepositoryURL string `json:"repository_url"` - } `json:"deployment"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// DeploymentStatusPayload contains the information for GitHub's deployment_status hook event -type DeploymentStatusPayload struct { - DeploymentStatus struct { - URL string `json:"url"` - ID int64 `json:"id"` - State string `json:"state"` - Creator struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"creator"` - Description *string `json:"description"` - TargetURL *string `json:"target_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - DeploymentURL string `json:"deployment_url"` - RepositoryURL string `json:"repository_url"` - } `json:"deployment_status"` - Deployment struct { - URL string `json:"url"` - ID int64 `json:"id"` - Sha string `json:"sha"` - Ref string `json:"ref"` - Task string `json:"task"` - Payload struct { - } `json:"payload"` - Environment string `json:"environment"` - Description *string `json:"description"` - Creator struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"creator"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - StatusesURL string `json:"statuses_url"` - RepositoryURL string `json:"repository_url"` - } `json:"deployment"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// ForkPayload contains the information for GitHub's fork hook event -type ForkPayload struct { - Forkee struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - Public bool `json:"public"` - } `json:"forkee"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// GollumPayload contains the information for GitHub's gollum hook event -type GollumPayload struct { - Pages []struct { - PageName string `json:"page_name"` - Title string `json:"title"` - Summary *string `json:"summary"` - Action string `json:"action"` - Sha string `json:"sha"` - HTMLURL string `json:"html_url"` - } `json:"pages"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// InstallationPayload contains the information for GitHub's installation and integration_installation hook events -type InstallationPayload struct { - Action string `json:"action"` - Installation struct { - ID int64 `json:"id"` - Account struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"account"` - RepositorySelection string `json:"repository_selection"` - AccessTokensURL string `json:"access_tokens_url"` - RepositoriesURL string `json:"repositories_url"` - HTMLURL string `json:"html_url"` - AppID int `json:"app_id"` - TargetID int `json:"target_id"` - TargetType string `json:"target_type"` - Permissions struct { - Issues string `json:"issues"` - Metadata string `json:"metadata"` - PullRequests string `json:"pull_requests"` - RepositoryProjects string `json:"repository_projects"` - } `json:"permissions"` - Events []string `json:"events"` - CreatedAt int64 `json:"created_at"` - UpdatedAt int64 `json:"updated_at"` - SingleFileName *string `json:"single_file_name"` - } `json:"installation"` - Repositories []struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - } `json:"repositories"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// IssueCommentPayload contains the information for GitHub's issue_comment hook event -type IssueCommentPayload struct { - Action string `json:"action"` - Issue struct { - URL string `json:"url"` - LabelsURL string `json:"labels_url"` - CommentsURL string `json:"comments_url"` - EventsURL string `json:"events_url"` - HTMLURL string `json:"html_url"` - ID int64 `json:"id"` - Number int64 `json:"number"` - Title string `json:"title"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Labels []struct { - URL string `json:"url"` - Name string `json:"name"` - Color string `json:"color"` - } `json:"labels"` - State string `json:"state"` - Locked bool `json:"locked"` - Assignee *Assignee `json:"assignee"` - Milestone *Milestone `json:"milestone"` - Comments int64 `json:"comments"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - ClosedAt *time.Time `json:"closed_at"` - Body string `json:"body"` - } `json:"issue"` - Comment struct { - URL string `json:"url"` - HTMLURL string `json:"html_url"` - IssueURL string `json:"issue_url"` - ID int64 `json:"id"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Body string `json:"body"` - AuthorAssociation string `json:"author_association"` - } `json:"comment"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// IssuesPayload contains the information for GitHub's issues hook event -type IssuesPayload struct { - Action string `json:"action"` - Issue struct { - URL string `json:"url"` - LabelsURL string `json:"labels_url"` - CommentsURL string `json:"comments_url"` - EventsURL string `json:"events_url"` - HTMLURL string `json:"html_url"` - ID int64 `json:"id"` - Number int64 `json:"number"` - Title string `json:"title"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Labels []struct { - ID int64 `json:"id"` - URL string `json:"url"` - Name string `json:"name"` - Color string `json:"color"` - Default bool `json:"default"` - } `json:"labels"` - State string `json:"state"` - Locked bool `json:"locked"` - Assignee *Assignee `json:"assignee"` - Milestone *Milestone `json:"milestone"` - Comments int64 `json:"comments"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - ClosedAt *time.Time `json:"closed_at"` - Body string `json:"body"` - } `json:"issue"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// LabelPayload contains the information for GitHub's label hook event -type LabelPayload struct { - Action string `json:"action"` - Label struct { - URL string `json:"url"` - Name string `json:"name"` - Color string `json:"color"` - } `json:"label"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description *string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - DeploymentsURL string `json:"deployments_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Organization struct { - Login string `json:"login"` - ID int64 `json:"id"` - URL string `json:"url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - HooksURL string `json:"hooks_url"` - IssuesURL string `json:"issues_url"` - MembersURL string `json:"members_url"` - PublicMembersURL string `json:"public_members_url"` - AvatarURL string `json:"avatar_url"` - Description string `json:"description"` - } `json:"organization"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// MemberPayload contains the information for GitHub's member hook event -type MemberPayload struct { - Action string `json:"action"` - Member struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"member"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// MembershipPayload contains the information for GitHub's membership hook event -type MembershipPayload struct { - Action string `json:"action"` - Scope string `json:"scope"` - Member struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"member"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` - Team struct { - Name string `json:"name"` - ID int64 `json:"id"` - Slug string `json:"slug"` - Permission string `json:"permission"` - URL string `json:"url"` - MembersURL string `json:"members_url"` - RepositoriesURL string `json:"repositories_url"` - } `json:"team"` - Organization struct { - Login string `json:"login"` - ID int64 `json:"id"` - URL string `json:"url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - MembersURL string `json:"members_url"` - PublicMembersURL string `json:"public_members_url"` - AvatarURL string `json:"avatar_url"` - } `json:"organization"` -} - -// MilestonePayload contains the information for GitHub's milestone hook event -type MilestonePayload struct { - Action string `json:"action"` - Milestone struct { - URL string `json:"url"` - HTMLURL string `json:"html_url"` - LabelsURL string `json:"labels_url"` - ID int64 `json:"id"` - Number int64 `json:"number"` - Title string `json:"title"` - Description *string `json:"description"` - Creator struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"creator"` - OpenIssues int64 `json:"open_issues"` - ClosedIssues int64 `json:"closed_issues"` - State string `json:"state"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - DueOn *time.Time `json:"due_on"` - ClosedAt *time.Time `json:"closed_at"` - } `json:"milestone"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description *string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - DeploymentsURL string `json:"deployments_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Organization struct { - Login string `json:"login"` - ID int64 `json:"id"` - URL string `json:"url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - HooksURL string `json:"hooks_url"` - IssuesURL string `json:"issues_url"` - MembersURL string `json:"members_url"` - PublicMembersURL string `json:"public_members_url"` - AvatarURL string `json:"avatar_url"` - Description string `json:"description"` - } `json:"organization"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// OrganizationPayload contains the information for GitHub's organization hook event -type OrganizationPayload struct { - Action string `json:"action"` - Invitation struct { - ID int64 `json:"id"` - Login string `json:"login"` - Email *string `json:"email"` - Role string `json:"role"` - } `json:"invitation"` - Membership struct { - URL string `json:"url"` - State string `json:"state"` - Role string `json:"role"` - OrganizationURL string `json:"organization_url"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - } `json:"membership"` - Organization struct { - Login string `json:"login"` - ID int64 `json:"id"` - URL string `json:"url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - HooksURL string `json:"hooks_url"` - IssuesURL string `json:"issues_url"` - MembersURL string `json:"members_url"` - PublicMembersURL string `json:"public_members_url"` - AvatarURL string `json:"avatar_url"` - Description string `json:"description"` - } `json:"organization"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// OrgBlockPayload contains the information for GitHub's org_block hook event -type OrgBlockPayload struct { - Action string `json:"action"` - BlockedUser struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"blocked_user"` - Organization struct { - Login string `json:"login"` - ID int64 `json:"id"` - URL string `json:"url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - HooksURL string `json:"hooks_url"` - IssuesURL string `json:"issues_url"` - MembersURL string `json:"members_url"` - PublicMembersURL string `json:"public_members_url"` - AvatarURL string `json:"avatar_url"` - Description string `json:"description"` - } `json:"organization"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// PageBuildPayload contains the information for GitHub's page_build hook event -type PageBuildPayload struct { - ID int64 `json:"id"` - Build struct { - URL string `json:"url"` - Status string `json:"status"` - Error struct { - Message *string `json:"message"` - } `json:"error"` - Pusher struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"pusher"` - Commit string `json:"commit"` - Duration int64 `json:"duration"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - } `json:"build"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// PingPayload contains the information for GitHub's ping hook event -type PingPayload struct { - HookID int `json:"hook_id"` - Hook struct { - Type string `json:"type"` - ID int64 `json:"id"` - Name string `json:"name"` - Active bool `json:"active"` - Events []string `json:"events"` - AppID int `json:"app_id"` - Config struct { - ContentType string `json:"content_type"` - InsecureSSL int `json:"insecure_ssl"` - Secret string `json:"secret"` - URL string `json:"url"` - } `json:"config"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - } `json:"hook"` -} - -// ProjectCardPayload contains the information for GitHub's project_payload hook event -type ProjectCardPayload struct { - Action string `json:"action"` - ProjectCard struct { - URL string `json:"url"` - ColumnURL string `json:"column_url"` - ColumnID int64 `json:"column_id"` - ID int64 `json:"id"` - Note *string `json:"note"` - Creator struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"creator"` - CreatedAt int64 `json:"created_at"` - UpdatedAt int64 `json:"updated_at"` - ContentURL string `json:"content_url"` - } `json:"project_card"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Organization struct { - Login string `json:"login"` - ID int64 `json:"id"` - URL string `json:"url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - MembersURL string `json:"members_url"` - PublicMembersURL string `json:"public_members_url"` - AvatarURL string `json:"avatar_url"` - } `json:"organization"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// ProjectColumnPayload contains the information for GitHub's project_column hook event -type ProjectColumnPayload struct { - Action string `json:"action"` - ProjectColumn struct { - URL string `json:"url"` - ProjectURL string `json:"project_url"` - CardsURL string `json:"cards_url"` - ID int64 `json:"id"` - Name string `json:"name"` - CreatedAt int64 `json:"created_at"` - UpdatedAt int64 `json:"updated_at"` - } `json:"project_column"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Organization struct { - Login string `json:"login"` - ID int64 `json:"id"` - URL string `json:"url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - MembersURL string `json:"members_url"` - PublicMembersURL string `json:"public_members_url"` - AvatarURL string `json:"avatar_url"` - } `json:"organization"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// ProjectPayload contains the information for GitHub's project hook event -type ProjectPayload struct { - Action string `json:"action"` - Project struct { - OwnerURL string `json:"owner_url"` - URL string `json:"url"` - ColumnsURL string `json:"columns_url"` - ID int64 `json:"id"` - Name string `json:"name"` - Body string `json:"body"` - Number int64 `json:"number"` - State string `json:"state"` - Creator struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"creator"` - CreatedAt int64 `json:"created_at"` - UpdatedAt int64 `json:"updated_at"` - } `json:"project"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Organization struct { - Login string `json:"login"` - ID int64 `json:"id"` - URL string `json:"url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - MembersURL string `json:"members_url"` - PublicMembersURL string `json:"public_members_url"` - AvatarURL string `json:"avatar_url"` - } `json:"organization"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// PublicPayload contains the information for GitHub's public hook event -type PublicPayload struct { - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// PullRequestPayload contains the information for GitHub's pull_request hook event -type PullRequestPayload struct { - Action string `json:"action"` - Number int64 `json:"number"` - PullRequest struct { - URL string `json:"url"` - ID int64 `json:"id"` - HTMLURL string `json:"html_url"` - DiffURL string `json:"diff_url"` - PatchURL string `json:"patch_url"` - IssueURL string `json:"issue_url"` - Number int64 `json:"number"` - State string `json:"state"` - Locked bool `json:"locked"` - Title string `json:"title"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Body string `json:"body"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - ClosedAt *time.Time `json:"closed_at"` - MergedAt *time.Time `json:"merged_at"` - MergeCommitSha *string `json:"merge_commit_sha"` - Assignee *Assignee `json:"assignee"` - Milestone *Milestone `json:"milestone"` - CommitsURL string `json:"commits_url"` - ReviewCommentsURL string `json:"review_comments_url"` - ReviewCommentURL string `json:"review_comment_url"` - CommentsURL string `json:"comments_url"` - StatusesURL string `json:"statuses_url"` - RequestedReviewers []struct { - Login string `json:"login"` - ID int `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"requested_reviewers,omitempty"` - Labels []struct { - ID int64 `json:"id"` - URL string `json:"url"` - Name string `json:"name"` - Color string `json:"color"` - Default bool `json:"default"` - } `json:"labels"` - Head struct { - Label string `json:"label"` - Ref string `json:"ref"` - Sha string `json:"sha"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Repo struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repo"` - } `json:"head"` - Base struct { - Label string `json:"label"` - Ref string `json:"ref"` - Sha string `json:"sha"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Repo struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repo"` - } `json:"base"` - Links struct { - Self struct { - Href string `json:"href"` - } `json:"self"` - HTML struct { - Href string `json:"href"` - } `json:"html"` - Issue struct { - Href string `json:"href"` - } `json:"issue"` - Comments struct { - Href string `json:"href"` - } `json:"comments"` - ReviewComments struct { - Href string `json:"href"` - } `json:"review_comments"` - ReviewComment struct { - Href string `json:"href"` - } `json:"review_comment"` - Commits struct { - Href string `json:"href"` - } `json:"commits"` - Statuses struct { - Href string `json:"href"` - } `json:"statuses"` - } `json:"_links"` - Merged bool `json:"merged"` - Mergeable *bool `json:"mergeable"` - MergeableState string `json:"mergeable_state"` - MergedBy *MergedBy `json:"merged_by"` - Comments int64 `json:"comments"` - ReviewComments int64 `json:"review_comments"` - Commits int64 `json:"commits"` - Additions int64 `json:"additions"` - Deletions int64 `json:"deletions"` - ChangedFiles int64 `json:"changed_files"` - } `json:"pull_request"` - Label struct { - ID int64 `json:"id"` - URL string `json:"url"` - Name string `json:"name"` - Color string `json:"color"` - Default bool `json:"default"` - } `json:"label"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` - Installation struct { - ID int64 `json:"id"` - } `json:"installation"` -} - -// PullRequestReviewPayload contains the information for GitHub's pull_request_review hook event -type PullRequestReviewPayload struct { - Action string `json:"action"` - Review struct { - ID int64 `json:"id"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Body string `json:"body"` - SubmittedAt time.Time `json:"submitted_at"` - State string `json:"state"` - HTMLURL string `json:"html_url"` - PullRequestURL string `json:"pull_request_url"` - Links struct { - HTML struct { - Href string `json:"href"` - } `json:"html"` - PullRequest struct { - Href string `json:"href"` - } `json:"pull_request"` - } `json:"_links"` - } `json:"review"` - PullRequest struct { - URL string `json:"url"` - ID int64 `json:"id"` - HTMLURL string `json:"html_url"` - DiffURL string `json:"diff_url"` - PatchURL string `json:"patch_url"` - IssueURL string `json:"issue_url"` - Number int64 `json:"number"` - State string `json:"state"` - Locked bool `json:"locked"` - Title string `json:"title"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Body string `json:"body"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - ClosedAt *time.Time `json:"closed_at"` - MergedAt *time.Time `json:"merged_at"` - MergeCommitSha string `json:"merge_commit_sha"` - Assignee *Assignee `json:"assignee"` - Assignees []Assignee `json:"assignees"` - Milestone *Milestone `json:"milestone"` - CommitsURL string `json:"commits_url"` - ReviewCommentsURL string `json:"review_comments_url"` - ReviewCommentURL string `json:"review_comment_url"` - CommentsURL string `json:"comments_url"` - StatusesURL string `json:"statuses_url"` - Head struct { - Label string `json:"label"` - Ref string `json:"ref"` - Sha string `json:"sha"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Repo struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description *string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - DeploymentsURL string `json:"deployments_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repo"` - } `json:"head"` - Base struct { - Label string `json:"label"` - Ref string `json:"ref"` - Sha string `json:"sha"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Repo struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - DeploymentsURL string `json:"deployments_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repo"` - } `json:"base"` - Links struct { - Self struct { - Href string `json:"href"` - } `json:"self"` - HTML struct { - Href string `json:"href"` - } `json:"html"` - Issue struct { - Href string `json:"href"` - } `json:"issue"` - Comments struct { - Href string `json:"href"` - } `json:"comments"` - ReviewComments struct { - Href string `json:"href"` - } `json:"review_comments"` - ReviewComment struct { - Href string `json:"href"` - } `json:"review_comment"` - Commits struct { - Href string `json:"href"` - } `json:"commits"` - Statuses struct { - Href string `json:"href"` - } `json:"statuses"` - } `json:"_links"` - } `json:"pull_request"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - DeploymentsURL string `json:"deployments_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// PullRequestReviewCommentPayload contains the information for GitHub's pull_request_review_comments hook event -type PullRequestReviewCommentPayload struct { - Action string `json:"action"` - Comment struct { - URL string `json:"url"` - ID int64 `json:"id"` - DiffHunk string `json:"diff_hunk"` - Path string `json:"path"` - Position int64 `json:"position"` - OriginalPosition int64 `json:"original_position"` - CommitID string `json:"commit_id"` - OriginalCommitID string `json:"original_commit_id"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Body string `json:"body"` - AuthorAssociation string `json:"author_association"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - HTMLURL string `json:"html_url"` - PullRequestURL string `json:"pull_request_url"` - Links struct { - Self struct { - Href string `json:"href"` - } `json:"self"` - HTML struct { - Href string `json:"href"` - } `json:"html"` - PullRequest struct { - Href string `json:"href"` - } `json:"pull_request"` - } `json:"_links"` - } `json:"comment"` - PullRequest struct { - URL string `json:"url"` - ID int64 `json:"id"` - HTMLURL string `json:"html_url"` - DiffURL string `json:"diff_url"` - PatchURL string `json:"patch_url"` - IssueURL string `json:"issue_url"` - Number int64 `json:"number"` - State string `json:"state"` - Locked bool `json:"locked"` - Title string `json:"title"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Body string `json:"body"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - ClosedAt *time.Time `json:"closed_at"` - MergedAt *time.Time `json:"merged_at"` - MergeCommitSha string `json:"merge_commit_sha"` - Assignee *Assignee `json:"assignee"` - Milestone *Milestone `json:"milestone"` - CommitsURL string `json:"commits_url"` - ReviewCommentsURL string `json:"review_comments_url"` - ReviewCommentURL string `json:"review_comment_url"` - CommentsURL string `json:"comments_url"` - StatusesURL string `json:"statuses_url"` - Head struct { - Label string `json:"label"` - Ref string `json:"ref"` - Sha string `json:"sha"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Repo struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repo"` - } `json:"head"` - Base struct { - Label string `json:"label"` - Ref string `json:"ref"` - Sha string `json:"sha"` - User struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"user"` - Repo struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repo"` - } `json:"base"` - Links struct { - Self struct { - Href string `json:"href"` - } `json:"self"` - HTML struct { - Href string `json:"href"` - } `json:"html"` - Issue struct { - Href string `json:"href"` - } `json:"issue"` - Comments struct { - Href string `json:"href"` - } `json:"comments"` - ReviewComments struct { - Href string `json:"href"` - } `json:"review_comments"` - ReviewComment struct { - Href string `json:"href"` - } `json:"review_comment"` - Commits struct { - Href string `json:"href"` - } `json:"commits"` - Statuses struct { - Href string `json:"href"` - } `json:"statuses"` - } `json:"_links"` - } `json:"pull_request"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// PushPayload contains the information for GitHub's push hook event -type PushPayload struct { - Ref string `json:"ref"` - Before string `json:"before"` - After string `json:"after"` - Created bool `json:"created"` - Deleted bool `json:"deleted"` - Forced bool `json:"forced"` - BaseRef *string `json:"base_ref"` - Compare string `json:"compare"` - Commits []struct { - Sha string `json:"sha"` - ID string `json:"id"` - TreeID string `json:"tree_id"` - Distinct bool `json:"distinct"` - Message string `json:"message"` - Timestamp string `json:"timestamp"` - URL string `json:"url"` - Author struct { - Name string `json:"name"` - Email string `json:"email"` - Username string `json:"username"` - } `json:"author"` - Committer struct { - Name string `json:"name"` - Email string `json:"email"` - Username string `json:"username"` - } `json:"committer"` - Added []string `json:"added"` - Removed []string `json:"removed"` - Modified []string `json:"modified"` - } `json:"commits"` - HeadCommit struct { - ID string `json:"id"` - TreeID string `json:"tree_id"` - Distinct bool `json:"distinct"` - Message string `json:"message"` - Timestamp string `json:"timestamp"` - URL string `json:"url"` - Author struct { - Name string `json:"name"` - Email string `json:"email"` - Username string `json:"username"` - } `json:"author"` - Committer struct { - Name string `json:"name"` - Email string `json:"email"` - Username string `json:"username"` - } `json:"committer"` - Added []string `json:"added"` - Removed []string `json:"removed"` - Modified []string `json:"modified"` - } `json:"head_commit"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Name string `json:"name"` - Email string `json:"email"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt int64 `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt int64 `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - Stargazers int64 `json:"stargazers"` - MasterBranch string `json:"master_branch"` - } `json:"repository"` - Pusher struct { - Name string `json:"name"` - Email string `json:"email"` - } `json:"pusher"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// ReleasePayload contains the information for GitHub's release hook event -type ReleasePayload struct { - Action string `json:"action"` - Release struct { - URL string `json:"url"` - AssetsURL string `json:"assets_url"` - UploadURL string `json:"upload_url"` - HTMLURL string `json:"html_url"` - ID int64 `json:"id"` - TagName string `json:"tag_name"` - TargetCommitish string `json:"target_commitish"` - Name *string `json:"name"` - Draft bool `json:"draft"` - Author struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"author"` - Prerelease bool `json:"prerelease"` - CreatedAt time.Time `json:"created_at"` - PublishedAt time.Time `json:"published_at"` - Assets []Asset `json:"assets"` - TarballURL string `json:"tarball_url"` - ZipballURL string `json:"zipball_url"` - Body *string `json:"body"` - } `json:"release"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// RepositoryPayload contains the information for GitHub's repository hook event -type RepositoryPayload struct { - Action string `json:"action"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Organization struct { - Login string `json:"login"` - ID int64 `json:"id"` - URL string `json:"url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - MembersURL string `json:"members_url"` - PublicMembersURL string `json:"public_members_url"` - AvatarURL string `json:"avatar_url"` - } `json:"organization"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// StatusPayload contains the information for GitHub's status hook event -type StatusPayload struct { - ID int64 `json:"id"` - Sha string `json:"sha"` - Name string `json:"name"` - TargetURL *string `json:"target_url"` - Context string `json:"context"` - Description *string `json:"description"` - State string `json:"state"` - Commit struct { - Sha string `json:"sha"` - Commit struct { - Author struct { - Name string `json:"name"` - Email string `json:"email"` - Date time.Time `json:"date"` - } `json:"author"` - Committer struct { - Name string `json:"name"` - Email string `json:"email"` - Date time.Time `json:"date"` - } `json:"committer"` - Message string `json:"message"` - Tree struct { - Sha string `json:"sha"` - URL string `json:"url"` - } `json:"tree"` - URL string `json:"url"` - CommentCount int64 `json:"comment_count"` - } `json:"commit"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - CommentsURL string `json:"comments_url"` - Author struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"author"` - Committer struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"committer"` - Parents []Parent `json:"parents"` - } `json:"commit"` - Branches []struct { - Name string `json:"name"` - Commit struct { - Sha string `json:"sha"` - URL string `json:"url"` - } `json:"commit"` - } `json:"branches"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// TeamPayload contains the information for GitHub's team hook event -type TeamPayload struct { - Action string `json:"action"` - Team struct { - Name string `json:"name"` - ID int64 `json:"id"` - Slug string `json:"slug"` - Description string `json:"description"` - Privacy string `json:"privacy"` - URL string `json:"url"` - MembersURL string `json:"members_url"` - RepositoriesURL string `json:"repositories_url"` - Permission string `json:"permission"` - } `json:"team"` - Organization struct { - Login string `json:"login"` - ID int64 `json:"id"` - URL string `json:"url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - HooksURL string `json:"hooks_url"` - IssuesURL string `json:"issues_url"` - MembersURL string `json:"members_url"` - PublicMembersURL string `json:"public_members_url"` - AvatarURL string `json:"avatar_url"` - Description string `json:"description"` - } `json:"organization"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// TeamAddPayload contains the information for GitHub's team_add hook event -type TeamAddPayload struct { - Team struct { - Name string `json:"name"` - ID int64 `json:"id"` - Slug string `json:"slug"` - Description string `json:"description"` - Permission string `json:"permission"` - URL string `json:"url"` - MembersURL string `json:"members_url"` - RepositoriesURL string `json:"repositories_url"` - } `json:"team"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Organization struct { - Login string `json:"login"` - ID int64 `json:"id"` - URL string `json:"url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - MembersURL string `json:"members_url"` - PublicMembersURL string `json:"public_members_url"` - AvatarURL string `json:"avatar_url"` - Description *string `json:"description"` - } `json:"organization"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// WatchPayload contains the information for GitHub's watch hook event -type WatchPayload struct { - Action string `json:"action"` - Repository struct { - ID int64 `json:"id"` - Name string `json:"name"` - FullName string `json:"full_name"` - Owner struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"owner"` - Private bool `json:"private"` - HTMLURL string `json:"html_url"` - Description string `json:"description"` - Fork bool `json:"fork"` - URL string `json:"url"` - ForksURL string `json:"forks_url"` - KeysURL string `json:"keys_url"` - CollaboratorsURL string `json:"collaborators_url"` - TeamsURL string `json:"teams_url"` - HooksURL string `json:"hooks_url"` - IssueEventsURL string `json:"issue_events_url"` - EventsURL string `json:"events_url"` - AssigneesURL string `json:"assignees_url"` - BranchesURL string `json:"branches_url"` - TagsURL string `json:"tags_url"` - BlobsURL string `json:"blobs_url"` - GitTagsURL string `json:"git_tags_url"` - GitRefsURL string `json:"git_refs_url"` - TreesURL string `json:"trees_url"` - StatusesURL string `json:"statuses_url"` - LanguagesURL string `json:"languages_url"` - StargazersURL string `json:"stargazers_url"` - ContributorsURL string `json:"contributors_url"` - SubscribersURL string `json:"subscribers_url"` - SubscriptionURL string `json:"subscription_url"` - CommitsURL string `json:"commits_url"` - GitCommitsURL string `json:"git_commits_url"` - CommentsURL string `json:"comments_url"` - IssueCommentURL string `json:"issue_comment_url"` - ContentsURL string `json:"contents_url"` - CompareURL string `json:"compare_url"` - MergesURL string `json:"merges_url"` - ArchiveURL string `json:"archive_url"` - DownloadsURL string `json:"downloads_url"` - IssuesURL string `json:"issues_url"` - PullsURL string `json:"pulls_url"` - MilestonesURL string `json:"milestones_url"` - NotificationsURL string `json:"notifications_url"` - LabelsURL string `json:"labels_url"` - ReleasesURL string `json:"releases_url"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - PushedAt time.Time `json:"pushed_at"` - GitURL string `json:"git_url"` - SSHURL string `json:"ssh_url"` - CloneURL string `json:"clone_url"` - SvnURL string `json:"svn_url"` - Homepage *string `json:"homepage"` - Size int64 `json:"size"` - StargazersCount int64 `json:"stargazers_count"` - WatchersCount int64 `json:"watchers_count"` - Language *string `json:"language"` - HasIssues bool `json:"has_issues"` - HasDownloads bool `json:"has_downloads"` - HasWiki bool `json:"has_wiki"` - HasPages bool `json:"has_pages"` - ForksCount int64 `json:"forks_count"` - MirrorURL *string `json:"mirror_url"` - OpenIssuesCount int64 `json:"open_issues_count"` - Forks int64 `json:"forks"` - OpenIssues int64 `json:"open_issues"` - Watchers int64 `json:"watchers"` - DefaultBranch string `json:"default_branch"` - } `json:"repository"` - Sender struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"sender"` -} - -// Assignee contains GitHub's assignee information -type Assignee struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` -} - -// Milestone contains GitHub's milestone information -type Milestone struct { - URL string `json:"url"` - HTMLURL string `json:"html_url"` - LabelsURL string `json:"labels_url"` - ID int64 `json:"id"` - Number int64 `json:"number"` - State string `json:"state"` - Title string `json:"title"` - Description string `json:"description"` - Creator struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"creator"` - OpenIssues int64 `json:"open_issues"` - ClosedIssues int64 `json:"closed_issues"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - ClosedAt time.Time `json:"closed_at"` - DueOn time.Time `json:"due_on"` -} - -// MergedBy contains GitHub's merged-by information -type MergedBy struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` -} - -// Asset contains GitHub's asset information -type Asset struct { - URL string `json:"url"` - BrowserDownloadURL string `json:"browser_download_url"` - ID int64 `json:"id"` - Name string `json:"name"` - Label string `json:"label"` - State string `json:"state"` - ContentType string `json:"content_type"` - Size int64 `json:"size"` - DownloadCount int64 `json:"download_count"` - CreatedAt time.Time `json:"created_at"` - UpdatedAt time.Time `json:"updated_at"` - Uploader struct { - Login string `json:"login"` - ID int64 `json:"id"` - AvatarURL string `json:"avatar_url"` - GravatarID string `json:"gravatar_id"` - URL string `json:"url"` - HTMLURL string `json:"html_url"` - FollowersURL string `json:"followers_url"` - FollowingURL string `json:"following_url"` - GistsURL string `json:"gists_url"` - StarredURL string `json:"starred_url"` - SubscriptionsURL string `json:"subscriptions_url"` - OrganizationsURL string `json:"organizations_url"` - ReposURL string `json:"repos_url"` - EventsURL string `json:"events_url"` - ReceivedEventsURL string `json:"received_events_url"` - Type string `json:"type"` - SiteAdmin bool `json:"site_admin"` - } `json:"uploader"` -} - -// Parent contains GitHub's parent information -type Parent struct { - URL string `json:"url"` - Sha string `json:"sha"` -} diff --git a/vendor/gopkg.in/go-playground/webhooks.v3/logger.go b/vendor/gopkg.in/go-playground/webhooks.v3/logger.go deleted file mode 100644 index 0d3d588e0b6..00000000000 --- a/vendor/gopkg.in/go-playground/webhooks.v3/logger.go +++ /dev/null @@ -1,44 +0,0 @@ -package webhooks - -import "log" - -// DefaultLog contains the default logger for webhooks, and prints only info and error messages by default -// for debugs override DefaultLog or see NewLogger for creating one without debugs. -var DefaultLog Logger = new(logger) - -// Logger allows for customizable logging -type Logger interface { - // Info prints basic information. - Info(string) - // Error prints error information. - Error(string) - // Debug prints information usefull for debugging. - Debug(string) -} - -// NewLogger returns a new logger for use. -func NewLogger(debug bool) Logger { - return &logger{PrintDebugs: debug} -} - -type logger struct { - PrintDebugs bool -} - -// Info prints basic information. -func (l *logger) Info(msg string) { - log.Println("INFO:", msg) -} - -// v prints error information. -func (l *logger) Error(msg string) { - log.Println("ERROR:", msg) -} - -// Debug prints information usefull for debugging. -func (l *logger) Debug(msg string) { - if !l.PrintDebugs { - return - } - log.Println("DEBUG:", msg) -} diff --git a/vendor/gopkg.in/go-playground/webhooks.v3/webhooks.go b/vendor/gopkg.in/go-playground/webhooks.v3/webhooks.go deleted file mode 100644 index 4ab78b1bfef..00000000000 --- a/vendor/gopkg.in/go-playground/webhooks.v3/webhooks.go +++ /dev/null @@ -1,124 +0,0 @@ -package webhooks - -import ( - "fmt" - "net/http" -) - -// Header provides http.Header to minimize imports -type Header http.Header - -// Provider defines the type of webhook -type Provider int - -func (p Provider) String() string { - switch p { - case GitHub: - return "GitHub" - case Bitbucket: - return "Bitbucket" - case GitLab: - return "GitLab" - case Gogs: - return "Gogs" - default: - return "Unknown" - } -} - -// webhooks available providers -const ( - GitHub Provider = iota - Bitbucket - GitLab - Gogs -) - -// Webhook interface defines a webhook to receive events -type Webhook interface { - Provider() Provider - ParsePayload(w http.ResponseWriter, r *http.Request) -} - -type server struct { - hook Webhook - path string - includePathCheck bool -} - -// ProcessPayloadFunc is a common function for payload return values -type ProcessPayloadFunc func(payload interface{}, header Header) - -// Handler returns the webhook http.Handler for use in your own Mux implementation -func Handler(hook Webhook) http.Handler { - return &server{ - hook: hook, - } -} - -// Run runs a server -func Run(hook Webhook, addr string, path string) error { - srv := &server{ - hook: hook, - path: path, - includePathCheck: true, - } - s := &http.Server{Addr: addr, Handler: srv} - - DefaultLog.Info(fmt.Sprintf("Listening on addr: %s path: %s", addr, path)) - return s.ListenAndServe() -} - -// RunServer runs a custom server. -func RunServer(s *http.Server, hook Webhook, path string) error { - - srv := &server{ - hook: hook, - path: path, - includePathCheck: true, - } - - s.Handler = srv - DefaultLog.Info(fmt.Sprintf("Listening on addr: %s path: %s", s.Addr, path)) - return s.ListenAndServe() -} - -// RunTLSServer runs a custom server with TLS configuration. -// NOTE: http.Server Handler will be overridden by this library, just set it to nil. -// Setting the Certificates can be done in the http.Server.TLSConfig.Certificates -// see example here: https://github.com/go-playground/webhooks/blob/v2/webhooks_test.go#L178 -func RunTLSServer(s *http.Server, hook Webhook, path string) error { - - srv := &server{ - hook: hook, - path: path, - includePathCheck: true, - } - - s.Handler = srv - DefaultLog.Info(fmt.Sprintf("Listening on addr: %s path: %s", s.Addr, path)) - return s.ListenAndServeTLS("", "") -} - -// ServeHTTP is the Handler for every posted WebHook Event -func (s *server) ServeHTTP(w http.ResponseWriter, r *http.Request) { - defer r.Body.Close() - DefaultLog.Info("Webhook received") - - if r.Method != "POST" { - DefaultLog.Error(fmt.Sprintf("405 Method not allowed, attempt made using Method: %s", r.Method)) - http.Error(w, "405 Method not allowed", http.StatusMethodNotAllowed) - return - } - - DefaultLog.Debug(fmt.Sprintf("Include path check: %t", s.includePathCheck)) - if s.includePathCheck { - if r.URL.Path != s.path { - DefaultLog.Error(fmt.Sprintf("404 Not found, POST made using path: %s, but expected %s", r.URL.Path, s.path)) - http.Error(w, "404 Not found", http.StatusNotFound) - return - } - } - - s.hook.ParsePayload(w, r) -} From 52fc360be7d455e621ca259b86ca34a1d7381c93 Mon Sep 17 00:00:00 2001 From: Sabari Kumar Murugesan Date: Wed, 7 Nov 2018 10:27:36 -0800 Subject: [PATCH 25/54] Deleting subscription should delete channel subscribers (#586) --- .../eventing/subscription/reconcile.go | 85 +++++++++---- .../eventing/subscription/reconcile_test.go | 119 +++++++++++++++--- 2 files changed, 165 insertions(+), 39 deletions(-) diff --git a/pkg/controller/eventing/subscription/reconcile.go b/pkg/controller/eventing/subscription/reconcile.go index 7ba8bcba534..211d5628102 100644 --- a/pkg/controller/eventing/subscription/reconcile.go +++ b/pkg/controller/eventing/subscription/reconcile.go @@ -34,11 +34,16 @@ import ( "k8s.io/apimachinery/pkg/api/meta" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/dynamic" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" ) +const ( + finalizerName = controllerAgentName +) + // Reconcile compares the actual state with the desired, and attempts to // converge the two. It then updates the Status block of the Subscription resource // with the current status of the resource. @@ -57,17 +62,10 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err return reconcile.Result{}, err } - original := subscription.DeepCopy() - // Reconcile this copy of the Subscription and then write back any status // updates regardless of whether the reconcile error out. err = r.reconcile(subscription) - if equality.Semantic.DeepEqual(original.Status, subscription.Status) { - // If we didn't change anything then don't call updateStatus. - // This is important because the copy we loaded from the informer's - // cache may be stale and we don't want to overwrite a prior update - // to status with this stale state. - } else if _, updateStatusErr := r.updateStatus(subscription); updateStatusErr != nil { + if _, updateStatusErr := r.updateStatus(subscription.DeepCopy()); updateStatusErr != nil { glog.Warningf("Failed to update subscription status: %v", updateStatusErr) return reconcile.Result{}, updateStatusErr } @@ -88,6 +86,20 @@ func (r *reconciler) reconcile(subscription *v1alpha1.Subscription) error { deletionTimestamp := accessor.GetDeletionTimestamp() glog.Infof("DeletionTimestamp: %v", deletionTimestamp) + if subscription.DeletionTimestamp != nil { + // If the subscription is Ready, then we have to remove it + // from the channel's subscriber list. + if subscription.Status.IsReady() { + err := r.syncPhysicalChannel(subscription, true) + if err != nil { + glog.Warningf("Failed to sync physical from Channel : %s", err) + return err + } + } + removeFinalizer(subscription) + return nil + } + // Verify that `channel` exists. _, err = r.fetchObjectReference(subscription.Namespace, &subscription.Spec.Channel) if err != nil { @@ -129,13 +141,14 @@ func (r *reconciler) reconcile(subscription *v1alpha1.Subscription) error { // Ok, now that we have the Channel and at least one of the Call/Result, let's reconcile // the Channel with this information. - err = r.syncPhysicalChannel(subscription) + err = r.syncPhysicalChannel(subscription, false) if err != nil { glog.Warningf("Failed to sync physical Channel : %s", err) return err } // Everything went well, set the fact that subscriptions have been modified subscription.Status.MarkChannelReady() + addFinalizer(subscription) return nil } @@ -154,15 +167,28 @@ func (r *reconciler) updateStatus(subscription *v1alpha1.Subscription) (*v1alpha if err != nil { return nil, err } - newSubscription.Status = subscription.Status - // Until #38113 is merged, we must use Update instead of UpdateStatus to - // update the Status block of the Subscription resource. UpdateStatus will not - // allow changes to the Spec of the resource, which is ideal for ensuring - // nothing other than resource status has been updated. - if err = r.client.Update(context.TODO(), newSubscription); err != nil { - return nil, err + updated := false + if !equality.Semantic.DeepEqual(newSubscription.Finalizers, subscription.Finalizers) { + newSubscription.SetFinalizers(subscription.ObjectMeta.Finalizers) + updated = true } + + if !equality.Semantic.DeepEqual(newSubscription.Status, subscription.Status) { + newSubscription.Status = subscription.Status + updated = true + } + + if updated { + // Until #38113 is merged, we must use Update instead of UpdateStatus to + // update the Status block of the Subscription resource. UpdateStatus will not + // allow changes to the Spec of the resource, which is ideal for ensuring + // nothing other than resource status has been updated. + if err = r.client.Update(context.TODO(), newSubscription); err != nil { + return nil, err + } + } + return newSubscription, nil } @@ -249,7 +275,7 @@ func domainToURL(domain string) string { return u.String() } -func (r *reconciler) syncPhysicalChannel(sub *v1alpha1.Subscription) error { +func (r *reconciler) syncPhysicalChannel(sub *v1alpha1.Subscription, isDeleted bool) error { glog.Infof("Reconciling Physical From Channel: %+v", sub) subs, err := r.listAllSubscriptionsWithPhysicalChannel(sub) @@ -258,6 +284,12 @@ func (r *reconciler) syncPhysicalChannel(sub *v1alpha1.Subscription) error { return err } + if !isDeleted { + // The sub we are currently reconciling has not yet written any updated status, so when listing + // it won't show any updates to the Status.PhysicalSubscription. We know that we are listing + // for subscriptions with the same PhysicalSubscription.From, so just add this one manually. + subs = append(subs, *sub) + } subscribable := r.createSubscribable(subs) return r.patchPhysicalFrom(sub.Namespace, sub.Spec.Channel, subscribable) @@ -266,11 +298,6 @@ func (r *reconciler) syncPhysicalChannel(sub *v1alpha1.Subscription) error { func (r *reconciler) listAllSubscriptionsWithPhysicalChannel(sub *v1alpha1.Subscription) ([]v1alpha1.Subscription, error) { subs := make([]v1alpha1.Subscription, 0) - // The sub we are currently reconciling has not yet written any updated status, so when listing - // it won't show any updates to the Status.PhysicalSubscription. We know that we are listing - // for subscriptions with the same PhysicalSubscription.From, so just add this one manually. - subs = append(subs, *sub) - opts := &client.ListOptions{ // TODO this is here because the fake client needs it. Remove this when it's no longer // needed. @@ -290,7 +317,7 @@ func (r *reconciler) listAllSubscriptionsWithPhysicalChannel(sub *v1alpha1.Subsc } for _, s := range sl.Items { if sub.UID == s.UID { - // This is the sub that is being reconciled. It has already been added to the list. + // This is the sub that is being reconciled. Skip it. continue } if equality.Semantic.DeepEqual(sub.Spec.Channel, s.Spec.Channel) { @@ -375,3 +402,15 @@ func (r *reconciler) CreateResourceInterface(namespace string, ref *corev1.Objec return rc.Namespace(namespace), nil } + +func addFinalizer(sub *v1alpha1.Subscription) { + finalizers := sets.NewString(sub.Finalizers...) + finalizers.Insert(finalizerName) + sub.Finalizers = finalizers.List() +} + +func removeFinalizer(sub *v1alpha1.Subscription) { + finalizers := sets.NewString(sub.Finalizers...) + finalizers.Delete(finalizerName) + sub.Finalizers = finalizers.List() +} diff --git a/pkg/controller/eventing/subscription/reconcile_test.go b/pkg/controller/eventing/subscription/reconcile_test.go index 7259a40476c..8102590a92e 100644 --- a/pkg/controller/eventing/subscription/reconcile_test.go +++ b/pkg/controller/eventing/subscription/reconcile_test.go @@ -36,25 +36,29 @@ import ( var ( trueVal = true + + // deletionTime is used when objects are marked as deleted. Rfc3339Copy() + // truncates to seconds to match the loss of precision during serialization. + deletionTime = metav1.Now().Rfc3339Copy() ) const ( - fromChannelName = "fromchannel" - resultChannelName = "resultchannel" - sourceName = "source" - routeName = "subscriberroute" - channelKind = "Channel" - routeKind = "Route" - sourceKind = "Source" - subscriptionKind = "Subscription" - targetDNS = "myfunction.mynamespace.svc.cluster.local" - sinkableDNS = "myresultchannel.mynamespace.svc.cluster.local" - eventType = "myeventtype" - subscriptionName = "testsubscription" - testNS = "testnamespace" - k8sServiceName = "testk8sservice" - k8sServiceDNS = "testk8sservice.testnamespace.svc.cluster.local" - otherAddressableDNS = "other-sinkable-channel.mynamespace.svc.cluster.local" + fromChannelName = "fromchannel" + resultChannelName = "resultchannel" + sourceName = "source" + routeName = "subscriberroute" + channelKind = "Channel" + routeKind = "Route" + sourceKind = "Source" + subscriptionKind = "Subscription" + targetDNS = "myfunction.mynamespace.svc.cluster.local" + sinkableDNS = "myresultchannel.mynamespace.svc.cluster.local" + eventType = "myeventtype" + subscriptionName = "testsubscription" + testNS = "testnamespace" + k8sServiceName = "testk8sservice" + k8sServiceDNS = "testk8sservice.testnamespace.svc.cluster.local" + otherAddressableDNS = "other-sinkable-channel.mynamespace.svc.cluster.local" ) func init() { @@ -696,6 +700,60 @@ var testCases = []controllertesting.TestCase{ }, }, }, + { + Name: "delete subscription with from channel: subscribers modified", + InitialState: []runtime.Object{ + getNewDeletedSubscriptionWithChannelReady(), + }, + // TODO: JSON patch is not working on the fake, see + // https://github.com/kubernetes/client-go/issues/478. Marking this as expecting a specific + // failure for now, until upstream is fixed. + WantResult: reconcile.Result{}, + WantErrMsg: "invalid JSON document", + WantAbsent: []runtime.Object{ + // TODO: JSON patch is not working on the fake, see + // https://github.com/kubernetes/client-go/issues/478. The entire test is really to + // verify the following, but can't be done because the call to Patch fails (it assumes + // a Strategic Merge Patch, whereas we are doing a JSON Patch). so for now, comment it + // out. + //getNewDeletedSubscriptionWithChannelReady(), + }, + WantPresent: []runtime.Object{ + // TODO: JSON patch is not working on the fake, see + // https://github.com/kubernetes/client-go/issues/478. The entire test is really to + // verify the following, but can't be done because the call to Patch fails (it assumes + // a Strategic Merge Patch, whereas we are doing a JSON Patch). so for now, comment it + // out. + //getChannelWithOtherSubscription(), + }, + Objects: []runtime.Object{ + // Source channel + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), + "kind": channelKind, + "metadata": map[string]interface{}{ + "namespace": testNS, + "name": fromChannelName, + }, + "spec": map[string]interface{}{ + "channelable": map[string]interface{}{ + "subscribers": []interface{}{ + map[string]interface{}{ + "subscriberURI": targetDNS, + "replyURI": sinkableDNS, + }, + map[string]interface{}{ + "replyURI": otherAddressableDNS, + }, + }, + }, + }, + }, + }, + }, + Scheme: scheme.Scheme, + }, } func TestAllCases(t *testing.T) { @@ -919,6 +977,16 @@ func getSubscriptionWithDifferentChannel() *eventingv1alpha1.Subscription { return s } +func getNewDeletedSubscriptionWithChannelReady() *eventingv1alpha1.Subscription { + s := getNewSubscriptionWithUnknownConditions() + s.Status.MarkReferencesResolved() + s.Status.PhysicalSubscription.SubscriberURI = domainToURL(targetDNS) + s.Status.PhysicalSubscription.ReplyURI = domainToURL(sinkableDNS) + s.Status.MarkChannelReady() + s.ObjectMeta.DeletionTimestamp = &deletionTime + return s +} + func channelType() metav1.TypeMeta { return metav1.TypeMeta{ APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), @@ -983,6 +1051,25 @@ func getChannelWithMultipleSubscriptions() *eventingv1alpha1.Channel { } } +func getChannelWithOtherSubscription() *eventingv1alpha1.Channel { + return &eventingv1alpha1.Channel{ + TypeMeta: metav1.TypeMeta{ + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: channelKind, + }, + ObjectMeta: om(testNS, fromChannelName), + Spec: eventingv1alpha1.ChannelSpec{ + Subscribable: &eventingduck.Subscribable{ + Subscribers: []eventingduck.ChannelSubscriberSpec{ + { + ReplyURI: otherAddressableDNS, + }, + }, + }, + }, + } +} + func om(namespace, name string) metav1.ObjectMeta { return metav1.ObjectMeta{ Namespace: namespace, From 35a38726bcb529f412aee7a602226cc322330344 Mon Sep 17 00:00:00 2001 From: Ville Aikas Date: Wed, 7 Nov 2018 11:50:37 -0800 Subject: [PATCH 26/54] Exclude test and vendor from coverage. now matches serving (#588) --- .gitattributes | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitattributes b/.gitattributes index ec5482eee06..ac2cf10fb78 100644 --- a/.gitattributes +++ b/.gitattributes @@ -9,3 +9,5 @@ # coverage. If a path is marked as linguist-generated already, it will be implicitly excluded and # there is no need to add the coverage-excluded attribute #/pkg/controller/testing/** coverage-excluded=true +/vendor/** coverage-excluded=true +/test/** coverage-excluded=true From 9fe2b04986c2374629028c6d1f921813457461c7 Mon Sep 17 00:00:00 2001 From: Adriano Cunha <35786489+adrcunha@users.noreply.github.com> Date: Wed, 7 Nov 2018 12:41:36 -0800 Subject: [PATCH 27/54] Update the release script (#584) * Update the release script The major change is the ability to publish a new release to GitHub automatically, just like serving or build do. Bonus: update documentation about `//hack`, and add documentation about `release.sh`. * Fix handling of repo KO_DOCKER_REPO is the canonical reference. * Fix { * Address PR feedback on doc --- Gopkg.lock | 4 +- hack/README.md | 8 +-- hack/release.md | 72 +++++++++++++++++++ hack/release.sh | 29 +++++--- .../knative/test-infra/scripts/dummy.go | 26 +++++++ .../knative/test-infra/scripts/e2e-tests.sh | 13 ++-- .../knative/test-infra/scripts/library.sh | 29 +++++--- .../knative/test-infra/scripts/release.sh | 40 ++++++----- 8 files changed, 174 insertions(+), 47 deletions(-) create mode 100644 hack/release.md create mode 100644 vendor/github.com/knative/test-infra/scripts/dummy.go diff --git a/Gopkg.lock b/Gopkg.lock index b8f23990b79..4b42c229402 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -288,11 +288,11 @@ [[projects]] branch = "master" - digest = "1:47acbf79b15512c1c70d952c4c094c91c106ed1d48341e0345b900e7bcecf973" + digest = "1:4d0724515d22c3964ef7bf671c989c45eefb81d71be09c1d8ab74acbdbb2ca94" name = "github.com/knative/test-infra" packages = ["."] pruneopts = "T" - revision = "8d72ddd96801f397298519adf1138fee8de1d51e" + revision = "f710a703baf3ac7e5e9005693fb1c8d61c4eccbb" [[projects]] digest = "1:56dbf15e091bf7926cb33a57cb6bdfc658fc6d3498d2f76f10a97ce7856f1fde" diff --git a/hack/README.md b/hack/README.md index a1eb457e45d..eff43a7c50f 100644 --- a/hack/README.md +++ b/hack/README.md @@ -3,7 +3,7 @@ This directory contains several scripts useful in the development process of Knative Eventing. * `boilerplate/add-boilerplate.sh` Adds license boilerplate to *txt* or *go* files in a directory, recursively. -* `release.sh` Builds (and optionally tags and publishes) a new release of Knative Eventing. -* `update-codegen.sh` Update auto-generated client libraries. -* `update-deps.sh` Update Go dependencies. -* `verify-codegen.sh` Verify that auto-generated client libraries are up-to-date. +* `release.sh` Creates a new [release](release.md) of Knative Eventing. +* `update-codegen.sh` Updates auto-generated client libraries. +* `update-deps.sh` Updates Go dependencies. +* `verify-codegen.sh` Verifies that auto-generated client libraries are up-to-date. diff --git a/hack/release.md b/hack/release.md new file mode 100644 index 00000000000..68417894428 --- /dev/null +++ b/hack/release.md @@ -0,0 +1,72 @@ +# Creating a new Knative Eventing release + +The `release.sh` script automates the creation of Knative Eventing releases, +either nightly or versioned ones. + +By default, the script creates a nightly release but does not publish anywhere. + +## Common flags for cutting releases + +The following flags affect the behavior of the script, no matter the type of +the release. + +* `--skip-tests` Do not run tests before building the release. Otherwise, +build, unit and end-to-end tests are run and they all must pass for the +release to be built. +* `--tag-release`, `--notag-release` Tag (or not) the generated images +with either `vYYYYMMDD-` (for nightly releases) or +`vX.Y.Z` for versioned releases. These are docker tags. *For versioned +releases, a tag is always added.* +* `--publish`, `--nopublish` Whether the generated images should be published +to a GCR, and the generated manifests written to a GCS bucket or not. If yes, +the destination GCR is defined by the environment variable +`$EVENTING_RELEASE_GCR` (defaults to `gcr.io/knative-releases`) and +the destination GCS bucket is defined by the environment variable +`$EVENTING_RELEASE_GCS` (defaults to `knative-releases/eventing`). If no, the +images will be pushed to the `ko.local` registry, and the manifests written to +the local disk only (in the repository root directory). + +## Creating nightly releases + +Nightly releases are built against the current git tree. The behavior of the +script is defined by the common flags. You must have write access to the GCR +and GCS bucket the release will be pushed to, unless `--nopublish` is used. + +Examples: + +```bash +# Create and publish a nightly, tagged release. +./hack/release.sh --publish --tag-release + +# Create release, but don't test, publish or tag it. +./hack/release.sh --skip-tests --nopublish --notag-release +``` + +## Creating versioned releases + +*Note: only Knative admins can create versioned releases.* + +To specify a versioned release to be cut, you must use the `--version` flag. +Versioned releases are usually built against a branch in the Knative Eventing +repository, specified by the `--branch` flag. + +* `--version` Defines the version of the release, and must be in the form +`X.Y.Z`, where X, Y and Z are numbers. +* `--branch` Defines the branch in Knative Eventing repository from which the +release will be built. If not passed, the `master` branch at HEAD will be used. +This branch must be created before the script is executed, and must be in the +form `release-X.Y`, where X and Y must match the numbers used in the version +passed in the `--version` flag. This flag has no effect unless `--version` is +also passed. +* `--release-notes` Points to a markdown file containing a description of the +release. This is optional but highly recommended. It has no effect unless +`--version` is also passed. + +If this is the first time you're cutting a versioned release, you'll be prompted +for your GitHub username, password, and possibly 2-factor authentication +challenge before the release is published. + +The release will be published in the *Releases* page of the Knative Eventing +repository, with the title *Knative Eventing release vX.Y.Z* and the given +release notes. It will also be tagged *vX.Y.Z* (both on GitHub and as a git +annotated tag). diff --git a/hack/release.sh b/hack/release.sh index 33699fc0b92..7b9c1fe7e75 100755 --- a/hack/release.sh +++ b/hack/release.sh @@ -24,17 +24,24 @@ readonly EVENTING_RELEASE_GCR # Yaml files to generate, and the source config dir for them. declare -A COMPONENTS -COMPONENTS["eventing.yaml"]="config" -COMPONENTS["in-memory-channel.yaml"]="config/provisioners/in-memory-channel" +COMPONENTS=( + ["eventing.yaml"]="config" + ["in-memory-channel.yaml"]="config/provisioners/in-memory-channel" +) readonly COMPONENTS declare -A RELEASES -RELEASES["release.yaml"]="eventing.yaml;in-memory-channel.yaml" +RELEASES=( + ["release.yaml"]="eventing.yaml in-memory-channel.yaml" +) readonly RELEASES +# Set the repository +export KO_DOCKER_REPO=${EVENTING_RELEASE_GCR} + # Script entry point. -parse_flags $@ +initialize $@ set -o errexit set -o pipefail @@ -43,11 +50,8 @@ run_validation_tests ./test/presubmit-tests.sh banner "Building the release" -# Set the repository -export KO_DOCKER_REPO=${EVENTING_RELEASE_GCR} - +echo "- Destination GCR: ${KO_DOCKER_REPO}" if (( PUBLISH_RELEASE )); then - echo "- Destination GCR: ${EVENTING_RELEASE_GCR}" echo "- Destination GCS: ${EVENTING_RELEASE_GCS}" fi @@ -59,19 +63,20 @@ for yaml in "${!COMPONENTS[@]}"; do config="${COMPONENTS[${yaml}]}" echo "Building Knative Eventing - ${config}" ko resolve ${KO_FLAGS} -f ${config}/ > ${yaml} - tag_images_in_yaml ${yaml} ${EVENTING_RELEASE_GCR} ${TAG} + tag_images_in_yaml ${yaml} ${KO_DOCKER_REPO} ${TAG} all_yamls+=(${yaml}) done # Assemble the release for yaml in "${!RELEASES[@]}"; do echo "Assembling Knative Eventing - ${yaml}" - echo -n "" > ${yaml} - for component in $(echo ${RELEASES[${yaml}]} | tr ";" "\n"); do + touch ${yaml} + for component in ${RELEASES[${yaml}]}; do echo "---" >> ${yaml} echo "# ${component}" >> ${yaml} cat ${component} >> ${yaml} done + tag_images_in_yaml ${yaml} ${KO_DOCKER_REPO} ${TAG} all_yamls+=(${yaml}) done @@ -88,4 +93,6 @@ for yaml in ${all_yamls[@]}; do publish_yaml ${yaml} ${EVENTING_RELEASE_GCS} ${TAG} done +branch_release "Knative Eventing" "${all_yamls[@]}" + echo "New release published successfully" diff --git a/vendor/github.com/knative/test-infra/scripts/dummy.go b/vendor/github.com/knative/test-infra/scripts/dummy.go new file mode 100644 index 00000000000..e6cc380fd7b --- /dev/null +++ b/vendor/github.com/knative/test-infra/scripts/dummy.go @@ -0,0 +1,26 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://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 scripts + +import ( + "fmt" +) + +func main() { + fmt.Println("This is a dummy go file so `go dep` can be used with knative/test-infra/scripts") + fmt.Println("This file can be safely removed if one day this directory contains real, useful go code") +} diff --git a/vendor/github.com/knative/test-infra/scripts/e2e-tests.sh b/vendor/github.com/knative/test-infra/scripts/e2e-tests.sh index ae88e1b12e0..e276b7ba117 100755 --- a/vendor/github.com/knative/test-infra/scripts/e2e-tests.sh +++ b/vendor/github.com/knative/test-infra/scripts/e2e-tests.sh @@ -72,12 +72,14 @@ function fail_test() { exit 1 } -# Run the given E2E tests (must be tagged as such). +# Run the given E2E tests. Assume tests are tagged e2e, unless `-tags=XXX` is passed. # Parameters: $1..$n - any go test flags, then directories containing the tests to run. function go_test_e2e() { - local options="" - (( EMIT_METRICS )) && options="-emitmetrics" - report_go_test -v -tags=e2e -count=1 $@ ${options} + local test_options="" + local go_options="" + (( EMIT_METRICS )) && test_options="-emitmetrics" + [[ ! " $@" == *" -tags="* ]] && go_options="-tags=e2e" + report_go_test -v -count=1 ${go_options} $@ ${test_options} } # Download the k8s binaries required by kubetest. @@ -184,7 +186,8 @@ function create_test_cluster() { # Don't fail test for kubetest, as it might incorrectly report test failure # if teardown fails (for details, see success() below) set +o errexit - kubetest "${CLUSTER_CREATION_ARGS[@]}" \ + run_go_tool k8s.io/test-infra/kubetest \ + kubetest "${CLUSTER_CREATION_ARGS[@]}" \ --up \ --down \ --extract local \ diff --git a/vendor/github.com/knative/test-infra/scripts/library.sh b/vendor/github.com/knative/test-infra/scripts/library.sh index 084c9d354a7..95acac29a58 100755 --- a/vendor/github.com/knative/test-infra/scripts/library.sh +++ b/vendor/github.com/knative/test-infra/scripts/library.sh @@ -23,6 +23,7 @@ readonly SERVING_GKE_VERSION=latest readonly SERVING_GKE_IMAGE=cos # Public images and yaml files. +readonly KNATIVE_ISTIO_CRD_YAML=https://storage.googleapis.com/knative-releases/serving/latest/istio-crds.yaml readonly KNATIVE_ISTIO_YAML=https://storage.googleapis.com/knative-releases/serving/latest/istio.yaml readonly KNATIVE_SERVING_RELEASE=https://storage.googleapis.com/knative-releases/serving/latest/release.yaml readonly KNATIVE_BUILD_RELEASE=https://storage.googleapis.com/knative-releases/build/latest/release.yaml @@ -177,7 +178,7 @@ function wait_until_routable() { return 1 } -# Returns the name of the pod of the given app. +# Returns the name of the first pod of the given app. # Parameters: $1 - app name. # $2 - namespace (optional). function get_app_pod() { @@ -186,6 +187,15 @@ function get_app_pod() { kubectl get pods ${namespace} --selector=app=$1 --output=jsonpath="{.items[0].metadata.name}" } +# Returns the name of all pods of the given app. +# Parameters: $1 - app name. +# $2 - namespace (optional). +function get_app_pods() { + local namespace="" + [[ -n $2 ]] && namespace="-n $2" + kubectl get pods ${namespace} --selector=app=$1 --output=jsonpath="{.items[*].metadata.name}" +} + # Sets the given user as cluster admin. # Parameters: $1 - user # $2 - cluster name @@ -335,6 +345,7 @@ function report_go_test() { function start_latest_knative_serving() { header "Starting Knative Serving" subheader "Installing Istio" + kubectl apply -f ${KNATIVE_ISTIO_CRD_YAML} || return 1 kubectl apply -f ${KNATIVE_ISTIO_YAML} || return 1 wait_until_pods_running istio-system || return 1 kubectl label namespace default istio-injection=enabled || return 1 @@ -356,16 +367,18 @@ function start_latest_knative_build() { } # Run a go tool, installing it first if necessary. -# Parameters: $1 - tool to run. -# $2 - tool package for go get. +# Parameters: $1 - tool package/dir for go get/install. +# $2 - tool to run. # $3..$n - parameters passed to the tool. function run_go_tool() { - local tool=$1 + local tool=$2 if [[ -z "$(which ${tool})" ]]; then - go install $2 + local action=get + [[ $1 =~ ^[\./].* ]] && action=install + go ${action} $1 fi shift 2 - ${tool} $@ + ${tool} "$@" } # Run dep-collector to update licenses. @@ -375,7 +388,7 @@ function update_licenses() { cd ${REPO_ROOT_DIR} || return 1 local dst=$1 shift - run_go_tool dep-collector ./vendor/github.com/knative/test-infra/tools/dep-collector $@ > ./${dst} + run_go_tool ./vendor/github.com/knative/test-infra/tools/dep-collector dep-collector $@ > ./${dst} } # Run dep-collector to check for forbidden liceses. @@ -384,7 +397,7 @@ function check_licenses() { # Fetch the google/licenseclassifier for its license db go get -u github.com/google/licenseclassifier # Check that we don't have any forbidden licenses in our images. - run_go_tool dep-collector ./vendor/github.com/knative/test-infra/tools/dep-collector -check $@ + run_go_tool ./vendor/github.com/knative/test-infra/tools/dep-collector dep-collector -check $@ } # Run the given linter on the given files, checking it exists first. diff --git a/vendor/github.com/knative/test-infra/scripts/release.sh b/vendor/github.com/knative/test-infra/scripts/release.sh index 3ddf02922be..3e41bdda9d1 100755 --- a/vendor/github.com/knative/test-infra/scripts/release.sh +++ b/vendor/github.com/knative/test-infra/scripts/release.sh @@ -68,7 +68,7 @@ function parse_flags() { RELEASE_VERSION="" RELEASE_NOTES="" RELEASE_BRANCH="" - KO_FLAGS="-P -L" + KO_FLAGS="-P" cd ${REPO_ROOT_DIR} while [[ $# -ne 0 ]]; do local parameter=$1 @@ -76,16 +76,8 @@ function parse_flags() { --skip-tests) SKIP_TESTS=1 ;; --tag-release) TAG_RELEASE=1 ;; --notag-release) TAG_RELEASE=0 ;; - --publish) - PUBLISH_RELEASE=1 - # Remove -L from ko flags - KO_FLAGS="${KO_FLAGS/-L}" - ;; - --nopublish) - PUBLISH_RELEASE=0 - # Add -L to ko flags - KO_FLAGS="-L ${KO_FLAGS}" - ;; + --publish) PUBLISH_RELEASE=1 ;; + --nopublish) PUBLISH_RELEASE=0 ;; --version) shift [[ $# -ge 1 ]] || abort "missing version after --version" @@ -109,6 +101,12 @@ function parse_flags() { shift done + # Update KO_DOCKER_REPO and KO_FLAGS if we're not publishing. + if (( ! PUBLISH_RELEASE )); then + KO_DOCKER_REPO="ko.local" + KO_FLAGS="-L ${KO_FLAGS}" + fi + if (( TAG_RELEASE )); then # Currently we're not considering the tags in refs/tags namespace. commit=$(git describe --always --dirty) @@ -138,7 +136,10 @@ function run_validation_tests() { if (( ! SKIP_TESTS )); then banner "Running release validation tests" # Run tests. - $1 + if ! $1; then + banner "Release validation tests failed, aborting" + exit 1 + fi fi } @@ -147,7 +148,7 @@ function initialize() { parse_flags $@ # Checkout specific branch, if necessary if (( BRANCH_RELEASE )); then - git checkout --track upstream/${RELEASE_BRANCH} || abort "cannot checkout branch ${RELEASE_BRANCH}" + git checkout upstream/${RELEASE_BRANCH} || abort "cannot checkout branch ${RELEASE_BRANCH}" fi } @@ -157,18 +158,23 @@ function initialize() { function branch_release() { (( BRANCH_RELEASE )) || return 0 local title="$1 release ${TAG}" - local yamls="$2" - local attach="--attach=${yamls// / --attach=}" + local attachments=() local description="$(mktemp)" + local attachments_dir="$(mktemp -d)" + # Copy each YAML to a separate dir + for yaml in $2; do + cp ${yaml} ${attachments_dir}/ + attachments+=("--attach=${yaml}#$(basename ${yaml})") + done echo -e "${title}\n" > ${description} if [[ -n "${RELEASE_NOTES}" ]]; then cat ${RELEASE_NOTES} >> ${description} fi git tag -a ${TAG} -m "${title}" git push $(git remote get-url upstream) tag ${TAG} - run_go_tool hub github.com/github/hub release create \ + run_go_tool github.com/github/hub hub release create \ --prerelease \ - ${attach} \ + ${attachments[@]} \ --file=${description} \ --commitish=${RELEASE_BRANCH} \ ${TAG} From 9617713e8d11c40b1b42103ffa7d2e31770cb491 Mon Sep 17 00:00:00 2001 From: Sabari Kumar Murugesan Date: Wed, 7 Nov 2018 14:00:36 -0800 Subject: [PATCH 28/54] Add helper methods from in-memory provisioner to utils (#560) * Add channel utils * Add Provisioner util and fix comments * Add unit tests * Fix logging * Fix after pr 562 * renaming * Fix import ordering and test --- .../eventing/inmemory/channel/reconcile.go | 204 ++---------------- .../inmemory/channel/reconcile_test.go | 7 +- .../clusterchannelprovisioner/reconcile.go | 91 +------- .../reconcile_test.go | 9 +- pkg/provisioners/channel_util.go | 191 ++++++++++++++++ pkg/provisioners/channel_util_test.go | 198 +++++++++++++++++ pkg/provisioners/provisioner_util.go | 95 ++++++++ pkg/provisioners/provisioner_util_test.go | 114 ++++++++++ 8 files changed, 634 insertions(+), 275 deletions(-) create mode 100644 pkg/provisioners/channel_util.go create mode 100644 pkg/provisioners/channel_util_test.go create mode 100644 pkg/provisioners/provisioner_util.go create mode 100644 pkg/provisioners/provisioner_util_test.go diff --git a/pkg/controller/eventing/inmemory/channel/reconcile.go b/pkg/controller/eventing/inmemory/channel/reconcile.go index 405312dbcc9..b8f56034219 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile.go @@ -19,30 +19,25 @@ package channel import ( "context" - eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - "github.com/knative/eventing/pkg/controller" - ccpcontroller "github.com/knative/eventing/pkg/controller/eventing/inmemory/clusterchannelprovisioner" - "github.com/knative/eventing/pkg/sidecar/configmap" - "github.com/knative/eventing/pkg/sidecar/fanout" - "github.com/knative/eventing/pkg/sidecar/multichannelfanout" - "github.com/knative/eventing/pkg/system" - istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "github.com/knative/eventing/pkg/controller" + ccpcontroller "github.com/knative/eventing/pkg/controller/eventing/inmemory/clusterchannelprovisioner" + util "github.com/knative/eventing/pkg/provisioners" + "github.com/knative/eventing/pkg/sidecar/configmap" + "github.com/knative/eventing/pkg/sidecar/fanout" + "github.com/knative/eventing/pkg/sidecar/multichannelfanout" ) const ( - portName = "http" - portNumber = 80 finalizerName = controllerAgentName ) @@ -100,7 +95,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err // regardless of the error. } - if updateStatusErr := r.updateChannel(ctx, c); updateStatusErr != nil { + if updateStatusErr := util.UpdateChannel(ctx, r.client, c); updateStatusErr != nil { logger.Info("Error updating Channel Status", zap.Error(updateStatusErr)) return reconcile.Result{}, updateStatusErr } @@ -136,200 +131,39 @@ func (r *reconciler) reconcile(ctx context.Context, c *eventingv1alpha1.Channel) if c.DeletionTimestamp != nil { // K8s garbage collection will delete the K8s service and VirtualService for this channel. // We use a finalizer to ensure the channel config has been synced. - r.removeFinalizer(c) + util.RemoveFinalizer(c, finalizerName) return nil } - r.addFinalizer(c) + util.AddFinalizer(c, finalizerName) - if svc, err := r.createK8sService(ctx, c); err != nil { + svc, err := util.CreateK8sService(ctx, r.client, c) + if err != nil { logger.Info("Error creating the Channel's K8s Service", zap.Error(err)) return err - } else { - c.Status.SetAddress(controller.ServiceHostName(svc.Name, svc.Namespace)) - } - - if err := r.createVirtualService(ctx, c); err != nil { - logger.Info("Error creating the Virtual Service for the Channel", zap.Error(err)) - return err - } - - c.Status.MarkProvisioned() - return nil -} - -func (r *reconciler) addFinalizer(c *eventingv1alpha1.Channel) { - finalizers := sets.NewString(c.Finalizers...) - finalizers.Insert(finalizerName) - c.Finalizers = finalizers.List() -} - -func (r *reconciler) removeFinalizer(c *eventingv1alpha1.Channel) { - finalizers := sets.NewString(c.Finalizers...) - finalizers.Delete(finalizerName) - c.Finalizers = finalizers.List() -} - -func (r *reconciler) getK8sService(ctx context.Context, c *eventingv1alpha1.Channel) (*corev1.Service, error) { - svcKey := types.NamespacedName{ - Namespace: c.Namespace, - Name: controller.ChannelServiceName(c.Name), - } - svc := &corev1.Service{} - err := r.client.Get(ctx, svcKey, svc) - return svc, err -} - -func (r *reconciler) createK8sService(ctx context.Context, c *eventingv1alpha1.Channel) (*corev1.Service, error) { - svc, err := r.getK8sService(ctx, c) - - if errors.IsNotFound(err) { - svc = newK8sService(c) - err = r.client.Create(ctx, svc) - } - - // If an error occurred in either Get or Create, we need to reconcile again. - if err != nil { - return nil, err } // Check if this Channel is the owner of the K8s service. if !metav1.IsControlledBy(svc, c) { - r.logger.Warn("Channel's K8s Service is not owned by the Channel", zap.Any("channel", c), zap.Any("service", svc)) + logger.Warn("Channel's K8s Service is not owned by the Channel", zap.Any("channel", c), zap.Any("service", svc)) } - return svc, nil -} -func (r *reconciler) getVirtualService(ctx context.Context, c *eventingv1alpha1.Channel) (*istiov1alpha3.VirtualService, error) { - vsk := client.ObjectKey{ - Namespace: c.Namespace, - Name: controller.ChannelVirtualServiceName(c.ObjectMeta.Name), - } - vs := &istiov1alpha3.VirtualService{} - err := r.client.Get(ctx, vsk, vs) - return vs, err -} - -func (r *reconciler) createVirtualService(ctx context.Context, c *eventingv1alpha1.Channel) error { - virtualService, err := r.getVirtualService(ctx, c) + c.Status.SetAddress(controller.ServiceHostName(svc.Name, svc.Namespace)) - // If the resource doesn't exist, we'll create it - if errors.IsNotFound(err) { - virtualService = newVirtualService(c) - err = r.client.Create(ctx, virtualService) - } + virtualService, err := util.CreateVirtualService(ctx, r.client, c) - // If an error occurs during Get/Create, we'll requeue the item so we can - // attempt processing again later. This could have been caused by a - // temporary network failure, or any other transient reason. if err != nil { + logger.Info("Error creating the Virtual Service for the Channel", zap.Error(err)) return err } // If the Virtual Service is not controlled by this Channel, we should log a warning, but don't // consider it an error. if !metav1.IsControlledBy(virtualService, c) { - r.logger.Warn("VirtualService not owned by Channel", zap.Any("channel", c), zap.Any("virtualService", virtualService)) - } - return nil -} - -// newK8sService creates a new Service for a Channel resource. It also sets the appropriate -// OwnerReferences on the resource so handleObject can discover the Channel resource that 'owns' it. -// As well as being garbage collected when the Channel is deleted. -func newK8sService(c *eventingv1alpha1.Channel) *corev1.Service { - labels := map[string]string{ - "channel": c.Name, - "provisioner": c.Spec.Provisioner.Name, - } - return &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: controller.ChannelServiceName(c.ObjectMeta.Name), - Namespace: c.Namespace, - Labels: labels, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(c, schema.GroupVersionKind{ - Group: eventingv1alpha1.SchemeGroupVersion.Group, - Version: eventingv1alpha1.SchemeGroupVersion.Version, - Kind: "Channel", - }), - }, - }, - Spec: corev1.ServiceSpec{ - Ports: []corev1.ServicePort{ - { - Name: portName, - Port: portNumber, - }, - }, - }, - } -} - -// newVirtualService creates a new VirtualService for a Channel resource. It also sets the -// appropriate OwnerReferences on the resource so handleObject can discover the Channel resource -// that 'owns' it. As well as being garbage collected when the Channel is deleted. -func newVirtualService(channel *eventingv1alpha1.Channel) *istiov1alpha3.VirtualService { - labels := map[string]string{ - "channel": channel.Name, - "provisioner": channel.Spec.Provisioner.Name, - } - destinationHost := controller.ServiceHostName(controller.ClusterBusDispatcherServiceName(channel.Spec.Provisioner.Name), system.Namespace) - return &istiov1alpha3.VirtualService{ - ObjectMeta: metav1.ObjectMeta{ - Name: controller.ChannelVirtualServiceName(channel.Name), - Namespace: channel.Namespace, - Labels: labels, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(channel, schema.GroupVersionKind{ - Group: eventingv1alpha1.SchemeGroupVersion.Group, - Version: eventingv1alpha1.SchemeGroupVersion.Version, - Kind: "Channel", - }), - }, - }, - Spec: istiov1alpha3.VirtualServiceSpec{ - Hosts: []string{ - controller.ServiceHostName(controller.ChannelServiceName(channel.Name), channel.Namespace), - controller.ChannelHostName(channel.Name, channel.Namespace), - }, - Http: []istiov1alpha3.HTTPRoute{{ - Rewrite: &istiov1alpha3.HTTPRewrite{ - Authority: controller.ChannelHostName(channel.Name, channel.Namespace), - }, - Route: []istiov1alpha3.DestinationWeight{{ - Destination: istiov1alpha3.Destination{ - Host: destinationHost, - Port: istiov1alpha3.PortSelector{ - Number: portNumber, - }, - }}, - }}, - }, - }, + logger.Warn("VirtualService not owned by Channel", zap.Any("channel", c), zap.Any("virtualService", virtualService)) } -} -func (r *reconciler) updateChannel(ctx context.Context, u *eventingv1alpha1.Channel) error { - o := &eventingv1alpha1.Channel{} - if err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o); err != nil { - r.logger.Info("Error getting Channel for status update", zap.Error(err), zap.Any("updatedChannel", u)) - return err - } - - updated := false - if !equality.Semantic.DeepEqual(o.Finalizers, u.Finalizers) { - updated = true - o.SetFinalizers(u.Finalizers) - } - if !equality.Semantic.DeepEqual(o.Status, u.Status) { - updated = true - o.Status = u.Status - } - - if updated { - return r.client.Update(ctx, o) - } + c.Status.MarkProvisioned() return nil } diff --git a/pkg/controller/eventing/inmemory/channel/reconcile_test.go b/pkg/controller/eventing/inmemory/channel/reconcile_test.go index 16199c19bc5..7542a4df80e 100644 --- a/pkg/controller/eventing/inmemory/channel/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/channel/reconcile_test.go @@ -27,6 +27,7 @@ import ( eventingduck "github.com/knative/eventing/pkg/apis/duck/v1alpha1" eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" controllertesting "github.com/knative/eventing/pkg/controller/testing" + util "github.com/knative/eventing/pkg/provisioners" "github.com/knative/eventing/pkg/sidecar/configmap" "github.com/knative/eventing/pkg/sidecar/fanout" "github.com/knative/eventing/pkg/sidecar/multichannelfanout" @@ -611,8 +612,8 @@ func makeK8sService() *corev1.Service { Spec: corev1.ServiceSpec{ Ports: []corev1.ServicePort{ { - Name: portName, - Port: portNumber, + Name: util.PortName, + Port: util.PortNumber, }, }, }, @@ -662,7 +663,7 @@ func makeVirtualService() *istiov1alpha3.VirtualService { Destination: istiov1alpha3.Destination{ Host: "in-memory-channel-clusterbus.knative-eventing.svc.cluster.local", Port: istiov1alpha3.PortSelector{ - Number: portNumber, + Number: util.PortNumber, }, }}, }}, diff --git a/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile.go b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile.go index 10a8dac8600..eb7420627bd 100644 --- a/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile.go +++ b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile.go @@ -19,20 +19,16 @@ package clusterchannelprovisioner import ( "context" - eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - "github.com/knative/eventing/pkg/controller" - "github.com/knative/eventing/pkg/system" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/types" - "k8s.io/apimachinery/pkg/util/intstr" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + util "github.com/knative/eventing/pkg/provisioners" ) const ( @@ -95,7 +91,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err // regardless of the error. } - if updateStatusErr := r.updateClusterChannelProvisionerStatus(ctx, ccp); updateStatusErr != nil { + if updateStatusErr := util.UpdateClusterChannelProvisionerStatus(ctx, r.client, ccp); updateStatusErr != nil { logger.Info("Error updating ClusterChannelProvisioner Status", zap.Error(updateStatusErr)) return reconcile.Result{}, updateStatusErr } @@ -131,89 +127,18 @@ func (r *reconciler) reconcile(ctx context.Context, ccp *eventingv1alpha1.Cluste return nil } - if err := r.createDispatcherService(ctx, ccp); err != nil { - logger.Info("Error creating the ClusterChannelProvisioner's K8s Service", zap.Error(err)) - return err - } - - ccp.Status.MarkReady() - return nil -} - -func (r *reconciler) createDispatcherService(ctx context.Context, ccp *eventingv1alpha1.ClusterChannelProvisioner) error { - svcName := controller.ClusterBusDispatcherServiceName(ccp.Name) - svcKey := types.NamespacedName{ - Namespace: system.Namespace, - Name: svcName, - } - svc := &corev1.Service{} - err := r.client.Get(ctx, svcKey, svc) - - if errors.IsNotFound(err) { - svc = newDispatcherService(ccp) - err = r.client.Create(ctx, svc) - } + svc, err := util.CreateDispatcherService(ctx, r.client, ccp) - // If an error occurred in either Get or Create, we need to reconcile again. if err != nil { + logger.Info("Error creating the ClusterChannelProvisioner's K8s Service", zap.Error(err)) return err } // Check if this ClusterChannelProvisioner is the owner of the K8s service. if !metav1.IsControlledBy(svc, ccp) { - r.logger.Warn("ClusterChannelProvisioner's K8s Service is not owned by the ClusterChannelProvisioner", zap.Any("clusterChannelProvisioner", ccp), zap.Any("service", svc)) + logger.Warn("ClusterChannelProvisioner's K8s Service is not owned by the ClusterChannelProvisioner", zap.Any("clusterChannelProvisioner", ccp), zap.Any("service", svc)) } - return nil -} -func (r *reconciler) updateClusterChannelProvisionerStatus(ctx context.Context, u *eventingv1alpha1.ClusterChannelProvisioner) error { - o := &eventingv1alpha1.ClusterChannelProvisioner{} - if err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o); err != nil { - r.logger.Info("Error getting ClusterChannelProvisioner for status update", zap.Error(err), zap.Any("updatedClusterChannelProvisioner", u)) - return err - } - - if !equality.Semantic.DeepEqual(o.Status, u.Status) { - o.Status = u.Status - return r.client.Update(ctx, o) - } + ccp.Status.MarkReady() return nil } - -// newDispatcherService creates a new Service for a ClusterBus resource. It also sets -// the appropriate OwnerReferences on the resource so handleObject can discover -// the ClusterBus resource that 'owns' it. -func newDispatcherService(ccp *eventingv1alpha1.ClusterChannelProvisioner) *corev1.Service { - labels := dispatcherLabels(ccp.Name) - return &corev1.Service{ - ObjectMeta: metav1.ObjectMeta{ - Name: controller.ClusterBusDispatcherServiceName(ccp.Name), - Namespace: system.Namespace, - Labels: labels, - OwnerReferences: []metav1.OwnerReference{ - *metav1.NewControllerRef(ccp, schema.GroupVersionKind{ - Group: eventingv1alpha1.SchemeGroupVersion.Group, - Version: eventingv1alpha1.SchemeGroupVersion.Version, - Kind: "ClusterChannelProvisioner", - }), - }, - }, - Spec: corev1.ServiceSpec{ - Selector: labels, - Ports: []corev1.ServicePort{ - { - Name: "http", - Port: 80, - TargetPort: intstr.FromInt(8080), - }, - }, - }, - } -} - -func dispatcherLabels(ccpName string) map[string]string { - return map[string]string{ - "clusterChannelProvisioner": ccpName, - "role": "dispatcher", - } -} diff --git a/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go index b4f9945cc2f..49f0f709c8f 100644 --- a/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go @@ -22,8 +22,6 @@ import ( "fmt" "testing" - eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - "github.com/knative/eventing/pkg/system" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" "go.uber.org/zap" corev1 "k8s.io/api/core/v1" @@ -35,7 +33,10 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/fake" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" controllertesting "github.com/knative/eventing/pkg/controller/testing" + util "github.com/knative/eventing/pkg/provisioners" + "github.com/knative/eventing/pkg/system" ) const ( @@ -290,10 +291,10 @@ func makeK8sService() *corev1.Service { BlockOwnerDeletion: &truePointer, }, }, - Labels: dispatcherLabels(Name), + Labels: util.DispatcherLabels(Name), }, Spec: corev1.ServiceSpec{ - Selector: dispatcherLabels(Name), + Selector: util.DispatcherLabels(Name), Ports: []corev1.ServicePort{ { Name: "http", diff --git a/pkg/provisioners/channel_util.go b/pkg/provisioners/channel_util.go new file mode 100644 index 00000000000..165ed49cea4 --- /dev/null +++ b/pkg/provisioners/channel_util.go @@ -0,0 +1,191 @@ +package provisioners + +import ( + "context" + + istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" + 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/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/sets" + runtimeClient "sigs.k8s.io/controller-runtime/pkg/client" + + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "github.com/knative/eventing/pkg/controller" + "github.com/knative/eventing/pkg/system" + "k8s.io/apimachinery/pkg/api/equality" +) + +const ( + PortName = "http" + PortNumber = 80 +) + +func AddFinalizer(c *eventingv1alpha1.Channel, finalizerName string) { + finalizers := sets.NewString(c.Finalizers...) + finalizers.Insert(finalizerName) + c.Finalizers = finalizers.List() +} + +func RemoveFinalizer(c *eventingv1alpha1.Channel, finalizerName string) { + finalizers := sets.NewString(c.Finalizers...) + finalizers.Delete(finalizerName) + c.Finalizers = finalizers.List() +} + +func getK8sService(ctx context.Context, client runtimeClient.Client, c *eventingv1alpha1.Channel) (*corev1.Service, error) { + svcKey := types.NamespacedName{ + Namespace: c.Namespace, + Name: controller.ChannelServiceName(c.Name), + } + svc := &corev1.Service{} + err := client.Get(ctx, svcKey, svc) + return svc, err +} + +func CreateK8sService(ctx context.Context, client runtimeClient.Client, c *eventingv1alpha1.Channel) (*corev1.Service, error) { + svc, err := getK8sService(ctx, client, c) + + if errors.IsNotFound(err) { + svc = newK8sService(c) + err = client.Create(ctx, svc) + } + + // If an error occurred in either Get or Create, we need to reconcile again. + if err != nil { + return nil, err + } + + return svc, nil +} + +func getVirtualService(ctx context.Context, client runtimeClient.Client, c *eventingv1alpha1.Channel) (*istiov1alpha3.VirtualService, error) { + vsk := runtimeClient.ObjectKey{ + Namespace: c.Namespace, + Name: controller.ChannelVirtualServiceName(c.ObjectMeta.Name), + } + vs := &istiov1alpha3.VirtualService{} + err := client.Get(ctx, vsk, vs) + return vs, err +} + +func CreateVirtualService(ctx context.Context, client runtimeClient.Client, c *eventingv1alpha1.Channel) (*istiov1alpha3.VirtualService, error) { + virtualService, err := getVirtualService(ctx, client, c) + + // If the resource doesn't exist, we'll create it + if errors.IsNotFound(err) { + virtualService = newVirtualService(c) + err = client.Create(ctx, virtualService) + } + + // If an error occurs during Get/Create, we'll requeue the item so we can + // attempt processing again later. This could have been caused by a + // temporary network failure, or any other transient reason. + if err != nil { + return nil, err + } + + return virtualService, nil +} + +func UpdateChannel(ctx context.Context, client runtimeClient.Client, u *eventingv1alpha1.Channel) error { + channel := &eventingv1alpha1.Channel{} + err := client.Get(ctx, runtimeClient.ObjectKey{Namespace: u.Namespace, Name: u.Name}, channel) + if err != nil { + return err + } + + updated := false + if !equality.Semantic.DeepEqual(channel.Finalizers, u.Finalizers) { + channel.SetFinalizers(u.ObjectMeta.Finalizers) + updated = true + } + + if !equality.Semantic.DeepEqual(channel.Status, u.Status) { + channel.Status = u.Status + updated = true + } + + if updated { + return client.Update(ctx, channel) + } + return nil +} + +// newK8sService creates a new Service for a Channel resource. It also sets the appropriate +// OwnerReferences on the resource so handleObject can discover the Channel resource that 'owns' it. +// As well as being garbage collected when the Channel is deleted. +func newK8sService(c *eventingv1alpha1.Channel) *corev1.Service { + labels := map[string]string{ + "channel": c.Name, + "provisioner": c.Spec.Provisioner.Name, + } + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: controller.ChannelServiceName(c.ObjectMeta.Name), + Namespace: c.Namespace, + Labels: labels, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(c, schema.GroupVersionKind{ + Group: eventingv1alpha1.SchemeGroupVersion.Group, + Version: eventingv1alpha1.SchemeGroupVersion.Version, + Kind: "Channel", + }), + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: PortName, + Port: PortNumber, + }, + }, + }, + } +} + +// newVirtualService creates a new VirtualService for a Channel resource. It also sets the +// appropriate OwnerReferences on the resource so handleObject can discover the Channel resource +// that 'owns' it. As well as being garbage collected when the Channel is deleted. +func newVirtualService(channel *eventingv1alpha1.Channel) *istiov1alpha3.VirtualService { + labels := map[string]string{ + "channel": channel.Name, + "provisioner": channel.Spec.Provisioner.Name, + } + destinationHost := controller.ServiceHostName(controller.ClusterBusDispatcherServiceName(channel.Spec.Provisioner.Name), system.Namespace) + return &istiov1alpha3.VirtualService{ + ObjectMeta: metav1.ObjectMeta{ + Name: controller.ChannelVirtualServiceName(channel.Name), + Namespace: channel.Namespace, + Labels: labels, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(channel, schema.GroupVersionKind{ + Group: eventingv1alpha1.SchemeGroupVersion.Group, + Version: eventingv1alpha1.SchemeGroupVersion.Version, + Kind: "Channel", + }), + }, + }, + Spec: istiov1alpha3.VirtualServiceSpec{ + Hosts: []string{ + controller.ServiceHostName(controller.ChannelServiceName(channel.Name), channel.Namespace), + controller.ChannelHostName(channel.Name, channel.Namespace), + }, + Http: []istiov1alpha3.HTTPRoute{{ + Rewrite: &istiov1alpha3.HTTPRewrite{ + Authority: controller.ChannelHostName(channel.Name, channel.Namespace), + }, + Route: []istiov1alpha3.DestinationWeight{{ + Destination: istiov1alpha3.Destination{ + Host: destinationHost, + Port: istiov1alpha3.PortSelector{ + Number: PortNumber, + }, + }}, + }}, + }, + }, + } +} diff --git a/pkg/provisioners/channel_util_test.go b/pkg/provisioners/channel_util_test.go new file mode 100644 index 00000000000..0503ee778ec --- /dev/null +++ b/pkg/provisioners/channel_util_test.go @@ -0,0 +1,198 @@ +package provisioners + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/knative/pkg/apis" + istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/kubernetes/scheme" + runtimeClient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" +) + +const ( + channelName = "test-channel" + testNS = "test-namespace" +) + +var ( + truePointer = true +) + +func init() { + // Add types to scheme. + istiov1alpha3.AddToScheme(scheme.Scheme) + eventingv1alpha1.AddToScheme(scheme.Scheme) +} + +func TestCreateK8sService(t *testing.T) { + want := makeK8sService() + client := fake.NewFakeClient() + got, _ := CreateK8sService(context.TODO(), client, getNewChannel()) + + ignore := cmpopts.IgnoreTypes(apis.VolatileTime{}) + if diff := cmp.Diff(want, got, ignore); diff != "" { + t.Errorf("Service (-want, +got) = %v", diff) + } +} + +func TestCreateK8sService_Existing(t *testing.T) { + want := makeK8sService() + client := fake.NewFakeClient(want) + got, _ := CreateK8sService(context.TODO(), client, getNewChannel()) + + ignore := cmpopts.IgnoreTypes(apis.VolatileTime{}) + if diff := cmp.Diff(want, got, ignore); diff != "" { + t.Errorf("Service (-want, +got) = %v", diff) + } +} + +func TestCreateVirtualService(t *testing.T) { + want := makeVirtualService() + client := fake.NewFakeClient() + got, _ := CreateVirtualService(context.TODO(), client, getNewChannel()) + + ignore := cmpopts.IgnoreTypes(apis.VolatileTime{}) + if diff := cmp.Diff(want, got, ignore); diff != "" { + t.Errorf("VirtualService (-want, +got) = %v", diff) + } +} + +func TestCreateVirtualService_Existing(t *testing.T) { + want := makeVirtualService() + client := fake.NewFakeClient(want) + got, _ := CreateVirtualService(context.TODO(), client, getNewChannel()) + + ignore := cmpopts.IgnoreTypes(apis.VolatileTime{}) + if diff := cmp.Diff(want, got, ignore); diff != "" { + t.Errorf("VirtualService (-want, +got) = %v", diff) + } +} + +func TestUpdateChannel(t *testing.T) { + oldChannel := getNewChannel() + client := fake.NewFakeClient(oldChannel) + + want := getNewChannel() + AddFinalizer(want, "test-finalizer") + want.Status.SetAddress("test-domain") + UpdateChannel(context.TODO(), client, want) + + got := &eventingv1alpha1.Channel{} + client.Get(context.TODO(), runtimeClient.ObjectKey{Namespace: testNS, Name: channelName}, got) + + ignore := cmpopts.IgnoreTypes(apis.VolatileTime{}) + if diff := cmp.Diff(want, got, ignore); diff != "" { + t.Errorf("Channel (-want, +got) = %v", diff) + } +} + +func getNewChannel() *eventingv1alpha1.Channel { + channel := &eventingv1alpha1.Channel{ + TypeMeta: channelType(), + ObjectMeta: om(testNS, channelName), + Spec: eventingv1alpha1.ChannelSpec{ + Provisioner: &corev1.ObjectReference{ + Name: clusterChannelProvisionerName, + Kind: "ClusterChannelProvisioner", + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + }, + }, + } + // selflink is not filled in when we create the object, so clear it + channel.ObjectMeta.SelfLink = "" + return channel +} + +func channelType() metav1.TypeMeta { + return metav1.TypeMeta{ + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "Channel", + } +} +func om(namespace, name string) metav1.ObjectMeta { + return metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + SelfLink: fmt.Sprintf("/apis/eventing/v1alpha1/namespaces/%s/object/%s", namespace, name), + } +} + +func makeK8sService() *corev1.Service { + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-channel", channelName), + Namespace: testNS, + Labels: map[string]string{ + "channel": channelName, + "provisioner": clusterChannelProvisionerName, + }, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "Channel", + Name: channelName, + Controller: &truePointer, + BlockOwnerDeletion: &truePointer, + }, + }, + }, + Spec: corev1.ServiceSpec{ + Ports: []corev1.ServicePort{ + { + Name: PortName, + Port: PortNumber, + }, + }, + }, + } +} + +func makeVirtualService() *istiov1alpha3.VirtualService { + return &istiov1alpha3.VirtualService{ + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-channel", channelName), + Namespace: testNS, + Labels: map[string]string{ + "channel": channelName, + "provisioner": clusterChannelProvisionerName, + }, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "Channel", + Name: channelName, + Controller: &truePointer, + BlockOwnerDeletion: &truePointer, + }, + }, + }, + Spec: istiov1alpha3.VirtualServiceSpec{ + Hosts: []string{ + fmt.Sprintf("%s-channel.%s.svc.cluster.local", channelName, testNS), + fmt.Sprintf("%s.%s.channels.cluster.local", channelName, testNS), + }, + Http: []istiov1alpha3.HTTPRoute{{ + Rewrite: &istiov1alpha3.HTTPRewrite{ + Authority: fmt.Sprintf("%s.%s.channels.cluster.local", channelName, testNS), + }, + Route: []istiov1alpha3.DestinationWeight{{ + Destination: istiov1alpha3.Destination{ + Host: fmt.Sprintf("%s-clusterbus.knative-eventing.svc.cluster.local", clusterChannelProvisionerName), + Port: istiov1alpha3.PortSelector{ + Number: PortNumber, + }, + }}, + }}, + }, + }, + } +} diff --git a/pkg/provisioners/provisioner_util.go b/pkg/provisioners/provisioner_util.go new file mode 100644 index 00000000000..787a1c58155 --- /dev/null +++ b/pkg/provisioners/provisioner_util.go @@ -0,0 +1,95 @@ +package provisioners + +import ( + "context" + + "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/apimachinery/pkg/util/intstr" + runtimeClient "sigs.k8s.io/controller-runtime/pkg/client" + + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "github.com/knative/eventing/pkg/controller" + "github.com/knative/eventing/pkg/system" + "github.com/knative/pkg/logging" +) + +func CreateDispatcherService(ctx context.Context, client runtimeClient.Client, ccp *eventingv1alpha1.ClusterChannelProvisioner) (*corev1.Service, error) { + svcName := controller.ClusterBusDispatcherServiceName(ccp.Name) + svcKey := types.NamespacedName{ + Namespace: system.Namespace, + Name: svcName, + } + svc := &corev1.Service{} + err := client.Get(ctx, svcKey, svc) + + if errors.IsNotFound(err) { + svc = newDispatcherService(ccp) + err = client.Create(ctx, svc) + } + + // If an error occurred in either Get or Create, we need to reconcile again. + if err != nil { + return nil, err + } + + return svc, nil +} + +func UpdateClusterChannelProvisionerStatus(ctx context.Context, client runtimeClient.Client, u *eventingv1alpha1.ClusterChannelProvisioner) error { + o := &eventingv1alpha1.ClusterChannelProvisioner{} + if err := client.Get(ctx, runtimeClient.ObjectKey{Namespace: u.Namespace, Name: u.Name}, o); err != nil { + logger := logging.FromContext(ctx) + logger.Info("Error getting ClusterChannelProvisioner for status update", zap.Error(err), zap.Any("updatedClusterChannelProvisioner", u)) + return err + } + + if !equality.Semantic.DeepEqual(o.Status, u.Status) { + o.Status = u.Status + return client.Update(ctx, o) + } + return nil +} + +// newDispatcherService creates a new Service for a ClusterChannelProvisioner resource. It also sets +// the appropriate OwnerReferences on the resource so handleObject can discover +// the ClusterChannelProvisioner resource that 'owns' it. +func newDispatcherService(ccp *eventingv1alpha1.ClusterChannelProvisioner) *corev1.Service { + labels := DispatcherLabels(ccp.Name) + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: controller.ClusterBusDispatcherServiceName(ccp.Name), + Namespace: system.Namespace, + Labels: labels, + OwnerReferences: []metav1.OwnerReference{ + *metav1.NewControllerRef(ccp, schema.GroupVersionKind{ + Group: eventingv1alpha1.SchemeGroupVersion.Group, + Version: eventingv1alpha1.SchemeGroupVersion.Version, + Kind: "ClusterChannelProvisioner", + }), + }, + }, + Spec: corev1.ServiceSpec{ + Selector: labels, + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + }, + } +} + +func DispatcherLabels(ccpName string) map[string]string { + return map[string]string{ + "clusterChannelProvisioner": ccpName, + "role": "dispatcher", + } +} diff --git a/pkg/provisioners/provisioner_util_test.go b/pkg/provisioners/provisioner_util_test.go new file mode 100644 index 00000000000..b93e7b780be --- /dev/null +++ b/pkg/provisioners/provisioner_util_test.go @@ -0,0 +1,114 @@ +package provisioners + +import ( + "context" + "fmt" + "testing" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" + "github.com/knative/pkg/apis" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/intstr" + runtimeClient "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/client/fake" + + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "github.com/knative/eventing/pkg/system" +) + +const ( + clusterChannelProvisionerName = "kafka" +) + +func TestCreateDispatcherService(t *testing.T) { + want := makeDispatcherService() + client := fake.NewFakeClient() + got, _ := CreateDispatcherService(context.TODO(), client, getNewClusterChannelProvisioner()) + + ignore := cmpopts.IgnoreTypes(apis.VolatileTime{}) + if diff := cmp.Diff(want, got, ignore); diff != "" { + t.Errorf("Service (-want, +got) = %v", diff) + } +} + +func TestCreateDispatcherService_Existing(t *testing.T) { + want := makeDispatcherService() + client := fake.NewFakeClient(want) + got, _ := CreateDispatcherService(context.TODO(), client, getNewClusterChannelProvisioner()) + + ignore := cmpopts.IgnoreTypes(apis.VolatileTime{}) + if diff := cmp.Diff(want, got, ignore); diff != "" { + t.Errorf("Service (-want, +got) = %v", diff) + } +} + +func TestUpdateClusterChannelProvisioner(t *testing.T) { + ccp := getNewClusterChannelProvisioner() + client := fake.NewFakeClient(ccp) + + // Update more than just Status + ccp.Status.MarkReady() + ccp.ObjectMeta.Annotations = map[string]string{"test-annotation": "testing"} + UpdateClusterChannelProvisionerStatus(context.TODO(), client, ccp) + + got := &eventingv1alpha1.ClusterChannelProvisioner{} + client.Get(context.TODO(), runtimeClient.ObjectKey{Namespace: testNS, Name: clusterChannelProvisionerName}, got) + + // Only status should be updated + want := getNewClusterChannelProvisioner() + want.Status.MarkReady() + + ignore := cmpopts.IgnoreTypes(apis.VolatileTime{}) + if diff := cmp.Diff(want, got, ignore); diff != "" { + t.Errorf("ClusterChannelProvisioner (-want, +got) = %v", diff) + } +} + +func getNewClusterChannelProvisioner() *eventingv1alpha1.ClusterChannelProvisioner { + clusterChannelProvisioner := &eventingv1alpha1.ClusterChannelProvisioner{ + TypeMeta: ClusterChannelProvisionerType(), + ObjectMeta: om(testNS, clusterChannelProvisionerName), + Spec: eventingv1alpha1.ClusterChannelProvisionerSpec{}, + } + // selflink is not filled in when we create the object, so clear it + clusterChannelProvisioner.ObjectMeta.SelfLink = "" + return clusterChannelProvisioner +} + +func ClusterChannelProvisionerType() metav1.TypeMeta { + return metav1.TypeMeta{ + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "ClusterChannelProvisioner", + } +} + +func makeDispatcherService() *corev1.Service { + return &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: system.Namespace, + Name: fmt.Sprintf("%s-clusterbus", clusterChannelProvisionerName), + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "ClusterChannelProvisioner", + Name: clusterChannelProvisionerName, + Controller: &truePointer, + BlockOwnerDeletion: &truePointer, + }, + }, + Labels: DispatcherLabels(clusterChannelProvisionerName), + }, + Spec: corev1.ServiceSpec{ + Selector: DispatcherLabels(clusterChannelProvisionerName), + Ports: []corev1.ServicePort{ + { + Name: "http", + Port: 80, + TargetPort: intstr.FromInt(8080), + }, + }, + }, + } +} From 176adafc2dc18c086c00086a140d5c587a4ce21c Mon Sep 17 00:00:00 2001 From: Grant Rodgers Date: Wed, 7 Nov 2018 17:07:36 -0800 Subject: [PATCH 29/54] Tests for simple packages (#592) * Test duck types * Fix api package doc lint * Add tests for *Name functions Functions that are no longer used were deleted. * Remove unused functions --- pkg/apis/duck/v1alpha1/doc.go | 1 + .../duck/v1alpha1/subscribable_types_test.go | 83 +++++++++++++++++++ pkg/apis/eventing/v1alpha1/doc.go | 3 +- pkg/controller/names.go | 20 ----- pkg/controller/names_test.go | 67 +++++++++++++++ pkg/controller/owner_references.go | 49 ----------- 6 files changed, 153 insertions(+), 70 deletions(-) create mode 100644 pkg/apis/duck/v1alpha1/subscribable_types_test.go create mode 100644 pkg/controller/names_test.go delete mode 100644 pkg/controller/owner_references.go diff --git a/pkg/apis/duck/v1alpha1/doc.go b/pkg/apis/duck/v1alpha1/doc.go index 0dcbcff0129..3e5d1e3bc5a 100644 --- a/pkg/apis/duck/v1alpha1/doc.go +++ b/pkg/apis/duck/v1alpha1/doc.go @@ -15,6 +15,7 @@ limitations under the License. // backward compatibility by support multiple concurrent versions // of the same resource +// Package v1alpha1 is the v1alpha1 version of the API. // +k8s:deepcopy-gen=package // +groupName=duck.knative.dev package v1alpha1 diff --git a/pkg/apis/duck/v1alpha1/subscribable_types_test.go b/pkg/apis/duck/v1alpha1/subscribable_types_test.go new file mode 100644 index 00000000000..c14bbc70d92 --- /dev/null +++ b/pkg/apis/duck/v1alpha1/subscribable_types_test.go @@ -0,0 +1,83 @@ +/* + * Copyright 2018 The Knative Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package v1alpha1 + +import ( + "testing" + + "github.com/google/go-cmp/cmp" + corev1 "k8s.io/api/core/v1" +) + +func TestGetFullType(t *testing.T) { + s := &Subscribable{} + switch s.GetFullType().(type) { + case *Channel: + // expected + default: + t.Errorf("expected GetFullType to return *Channel, got %T", s.GetFullType()) + } +} + +func TestGetListType(t *testing.T) { + c := &Channel{} + switch c.GetListType().(type) { + case *ChannelList: + // expected + default: + t.Errorf("expected GetFullType to return *ChannelList, got %T", c.GetListType()) + } +} + +func TestPopulate(t *testing.T) { + got := &Channel{} + + want := &Channel{ + Spec: ChannelSpec{ + Subscribable: &Subscribable{ + Subscribers: []ChannelSubscriberSpec{{ + Ref: &corev1.ObjectReference{ + APIVersion: "eventing.knative.dev/v1alpha1", + Kind: "Subscription", + Name: "subscription1", + Namespace: "default", + UID: "2f9b5e8e-deb6-11e8-9f32-f2801f1b9fd1", + }, + SubscriberURI: "call1", + ReplyURI: "sink2", + }, { + Ref: &corev1.ObjectReference{ + APIVersion: "eventing.knative.dev/v1alpha1", + Kind: "Subscription", + Name: "subscription2", + Namespace: "default", + UID: "34c5aec8-deb6-11e8-9f32-f2801f1b9fd1", + }, + SubscriberURI: "call2", + ReplyURI: "sink2", + }}, + }, + }, + } + + got.Populate() + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("Unexpected difference (-want, +got): %v", diff) + } + +} diff --git a/pkg/apis/eventing/v1alpha1/doc.go b/pkg/apis/eventing/v1alpha1/doc.go index b2c114bfe8a..e080fe737d2 100644 --- a/pkg/apis/eventing/v1alpha1/doc.go +++ b/pkg/apis/eventing/v1alpha1/doc.go @@ -10,7 +10,8 @@ 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. */ -// +k8s:deepcopy-gen=package + // Package v1alpha1 is the v1alpha1 version of the API. +// +k8s:deepcopy-gen=package // +groupName=eventing.knative.dev package v1alpha1 diff --git a/pkg/controller/names.go b/pkg/controller/names.go index 852481adc73..6026013e34b 100644 --- a/pkg/controller/names.go +++ b/pkg/controller/names.go @@ -18,26 +18,6 @@ package controller import "fmt" -func BusProvisionerDeploymentName(busName, namespace string) string { - return fmt.Sprintf("%s-%s-bus-provisioner", busName, namespace) -} - -func BusDispatcherDeploymentName(busName, namespace string) string { - return fmt.Sprintf("%s-%s-bus-dispatcher", busName, namespace) -} - -func BusDispatcherServiceName(busName, namespace string) string { - return fmt.Sprintf("%s-%s-bus", busName, namespace) -} - -func ClusterBusProvisionerDeploymentName(clusterBusName string) string { - return fmt.Sprintf("%s-clusterbus-provisioner", clusterBusName) -} - -func ClusterBusDispatcherDeploymentName(clusterBusName string) string { - return fmt.Sprintf("%s-clusterbus-dispatcher", clusterBusName) -} - func ClusterBusDispatcherServiceName(clusterBusName string) string { return fmt.Sprintf("%s-clusterbus", clusterBusName) } diff --git a/pkg/controller/names_test.go b/pkg/controller/names_test.go new file mode 100644 index 00000000000..ca0d3933a25 --- /dev/null +++ b/pkg/controller/names_test.go @@ -0,0 +1,67 @@ +/* + * Copyright 2018 The Knative Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package controller + +import ( + "testing" +) + +func TestNames(t *testing.T) { + testCases := []struct { + Name string + F func() string + Want string + }{{ + Name: "ClusterBusDispatcherServiceName", + F: func() string { + return ClusterBusDispatcherServiceName("foo") + }, + Want: "foo-clusterbus", + }, { + Name: "ChannelVirtualServiceName", + F: func() string { + return ChannelVirtualServiceName("foo") + }, + Want: "foo-channel", + }, { + Name: "ChannelServiceName", + F: func() string { + return ChannelServiceName("foo") + }, + Want: "foo-channel", + }, { + Name: "ChannelHostName", + F: func() string { + return ChannelHostName("foo", "namespace") + }, + Want: "foo.namespace.channels.cluster.local", + }, { + Name: "ServiceHostName", + F: func() string { + return ServiceHostName("foo", "namespace") + }, + Want: "foo.namespace.svc.cluster.local", + }} + + for _, tc := range testCases { + t.Run(tc.Name, func(t *testing.T) { + if got := tc.F(); got != tc.Want { + t.Errorf("want %v, got %v", tc.Want, got) + } + }) + } +} diff --git a/pkg/controller/owner_references.go b/pkg/controller/owner_references.go deleted file mode 100644 index 46cda13987c..00000000000 --- a/pkg/controller/owner_references.go +++ /dev/null @@ -1,49 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package controller - -import ( - "fmt" - - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime/schema" - - eventingv1alpha "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" -) - -func kind(obj metav1.Object) schema.GroupVersionKind { - switch obj.(type) { - - // Eventing - case *eventingv1alpha.Channel: - return eventingv1alpha.SchemeGroupVersion.WithKind("Channel") - case *eventingv1alpha.ClusterChannelProvisioner: - return eventingv1alpha.SchemeGroupVersion.WithKind("ClusterChannelProvisioner") - case *eventingv1alpha.Subscription: - return eventingv1alpha.SchemeGroupVersion.WithKind("Subscription") - - default: - panic(fmt.Sprintf("Unsupported object type %T", obj)) - } -} - -// NewControllerRef creates an OwnerReference pointing to the given Resource. -func NewControllerRef(obj metav1.Object, blockOwnerDeletion bool) *metav1.OwnerReference { - ref := metav1.NewControllerRef(obj, kind(obj)) - ref.BlockOwnerDeletion = &blockOwnerDeletion - return ref -} From 88125976c770c6287bb26ee1809df2dbf3f65dc6 Mon Sep 17 00:00:00 2001 From: Ville Aikas Date: Thu, 8 Nov 2018 12:07:44 -0700 Subject: [PATCH 30/54] add more unit tests to subscription/reconcile.go (#593) * add more unit tests * use anonymous struct as per pr comment --- .../eventing/subscription/reconcile_test.go | 90 +++++++++++++++++++ 1 file changed, 90 insertions(+) diff --git a/pkg/controller/eventing/subscription/reconcile_test.go b/pkg/controller/eventing/subscription/reconcile_test.go index 8102590a92e..79f56de8f99 100644 --- a/pkg/controller/eventing/subscription/reconcile_test.go +++ b/pkg/controller/eventing/subscription/reconcile_test.go @@ -28,6 +28,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/util/sets" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" "k8s.io/client-go/tools/record" @@ -464,6 +465,33 @@ var testCases = []controllertesting.TestCase{ }, }, }, + }, { + Name: "new subscription to non-existent K8s Service: fails with no service found", + InitialState: []runtime.Object{ + getNewSubscriptionToK8sService(), + }, + WantResult: reconcile.Result{}, + WantPresent: []runtime.Object{ + getNewSubscriptionToK8sServiceWithUnknownConditions(), + }, + WantErrMsg: "services \"testk8sservice\" not found", + Scheme: scheme.Scheme, + Objects: []runtime.Object{ + // Source channel + &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": eventingv1alpha1.SchemeGroupVersion.String(), + "kind": channelKind, + "metadata": map[string]interface{}{ + "namespace": testNS, + "name": fromChannelName, + }, + "spec": map[string]interface{}{ + "subscribable": map[string]interface{}{}, + }, + }, + }, + }, }, { Name: "new subscription to K8s Service: adds status, all targets resolved, subscribers modified", InitialState: []runtime.Object{ @@ -775,6 +803,62 @@ func TestAllCases(t *testing.T) { } } +func TestFinalizers(t *testing.T) { + var testcases = []struct { + name string + original sets.String + add bool + want sets.String + }{ + { + name: "empty, add", + original: sets.NewString(), + add: true, + want: sets.NewString(finalizerName), + }, { + name: "empty, delete", + original: sets.NewString(), + add: false, + want: sets.NewString(), + }, { + name: "existing, delete", + original: sets.NewString(finalizerName), + add: false, + want: sets.NewString(), + }, { + name: "existing, add", + original: sets.NewString(finalizerName), + add: true, + want: sets.NewString(finalizerName), + }, { + name: "existing two, delete", + original: sets.NewString(finalizerName, "someother"), + add: false, + want: sets.NewString("someother"), + }, { + name: "existing two, no change", + original: sets.NewString(finalizerName, "someother"), + add: true, + want: sets.NewString(finalizerName, "someother"), + }, + } + + for _, tc := range testcases { + original := &eventingv1alpha1.Subscription{} + original.Finalizers = tc.original.List() + if tc.add { + addFinalizer(original) + } else { + removeFinalizer(original) + } + has := sets.NewString(original.Finalizers...) + diff := has.Difference(tc.want) + if diff.Len() > 0 { + t.Errorf("%q failed, diff: %+v", tc.name, diff) + } + } +} + func getNewFromChannel() *eventingv1alpha1.Channel { return getNewChannel(fromChannelName) } @@ -873,6 +957,12 @@ func getNewSubscriptionToK8sService() *eventingv1alpha1.Subscription { return sub } +func getNewSubscriptionToK8sServiceWithUnknownConditions() *eventingv1alpha1.Subscription { + sub := getNewSubscriptionToK8sService() + sub.Status.InitializeConditions() + return sub +} + func getNewSubscriptionWithFromChannel() *eventingv1alpha1.Subscription { subscription := &eventingv1alpha1.Subscription{ TypeMeta: subscriptionType(), From 2b99e13b5b43ae998d4c0593073d999d9a3bc0db Mon Sep 17 00:00:00 2001 From: Ville Aikas Date: Thu, 8 Nov 2018 16:38:44 -0700 Subject: [PATCH 31/54] Add tests for eventing/v1alpha1/register.go (#595) * Add tests for eventing/v1alpha1/register.go * address pr feedback --- pkg/apis/eventing/v1alpha1/register_test.go | 69 +++++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 pkg/apis/eventing/v1alpha1/register_test.go diff --git a/pkg/apis/eventing/v1alpha1/register_test.go b/pkg/apis/eventing/v1alpha1/register_test.go new file mode 100644 index 00000000000..0f7133addc1 --- /dev/null +++ b/pkg/apis/eventing/v1alpha1/register_test.go @@ -0,0 +1,69 @@ +/* +Copyright 2018 The Knative Authors +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + http://www.apache.org/licenses/LICENSE-2.0 +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package v1alpha1 + +import ( + "github.com/google/go-cmp/cmp" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "testing" +) + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func TestResource(t *testing.T) { + want := schema.GroupResource{ + Group: "eventing.knative.dev", + Resource: "foo", + } + + got := Resource("foo") + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("unexpected resource (-want, +got) = %v", diff) + } +} + +// Kind takes an unqualified resource and returns a Group qualified GroupKind +func TestKind(t *testing.T) { + want := schema.GroupKind{ + Group: "eventing.knative.dev", + Kind: "kind", + } + + got := Kind("kind") + + if diff := cmp.Diff(want, got); diff != "" { + t.Errorf("unexpected resource (-want, +got) = %v", diff) + } +} + +// TestKnownTypes makes sure that expected types get added. +func TestKnownTypes(t *testing.T) { + scheme := runtime.NewScheme() + addKnownTypes(scheme) + types := scheme.KnownTypes(SchemeGroupVersion) + + for _, name := range []string{ + "Channel", + "ChannelList", + "ClusterChannelProvisioner", + "ClusterChannelProvisionerList", + "Subscription", + "SubscriptionList", + } { + if _, ok := types[name]; !ok { + t.Errorf("Did not find %q as registered type", name) + } + } + +} From 160c27c62ee7d985bd0c36a4ff3571ab6b95d1df Mon Sep 17 00:00:00 2001 From: Sabari Kumar Murugesan Date: Thu, 8 Nov 2018 16:04:43 -0800 Subject: [PATCH 32/54] Adds Kafka Channel Provisioner Controllers (#468) * Kafka Channel Provisioner Controllers * Remove controllerRuntimeStart and address PR comments * Remove fetching configmap in controller * Add more tests, improv coverage * Add ChannelStatus.IsReady * Address PR comments * Remove unfinished README * Switch from logr to zap * Provision Channel as Kafka Topic * Deprovision Channel * Merge matzew/try_kafka_provisioner into try_kafka_provisioner * Fix few more after pr 562 * Fix tests and imports * PR feedback for removing ClusterChannelProvisioner name from configmap There were some concerns in fetching the provisioner name from a config map. * Adding instructions for Channel provisioner * short cut code for missing ... * Updating to latest Kafka client, and fixing idle bug * Fix conflicts and unit tests * Address PR feedback * Updating docs, based on feedback --- Gopkg.lock | 60 ++ Gopkg.toml | 4 + config/provisioners/kafka/README.md | 78 ++ config/provisioners/kafka/broker/README.md | 13 + .../kafka/broker/kafka-broker.yaml | 87 ++ .../provisioners/kafka/kafka-provisioner.yaml | 85 ++ pkg/apis/eventing/v1alpha1/channel_types.go | 5 + .../eventing/v1alpha1/channel_types_test.go | 2 + .../cluster_channel_provisioner_types.go | 5 + .../cluster_channel_provisioner_types_test.go | 24 +- .../kafka/controller/channel/provider.go | 78 ++ .../kafka/controller/channel/reconcile.go | 266 +++++ .../controller/channel/reconcile_test.go | 493 +++++++++ pkg/provisioners/kafka/controller/provider.go | 72 ++ .../kafka/controller/reconcile.go | 94 ++ .../kafka/controller/reconcile_test.go | 188 ++++ pkg/provisioners/kafka/controller/types.go | 5 + pkg/provisioners/kafka/main.go | 103 ++ pkg/provisioners/logging.go | 63 ++ third_party/VENDOR-LICENSE | 209 ++++ vendor/github.com/Shopify/sarama/LICENSE | 20 + .../github.com/Shopify/sarama/acl_bindings.go | 119 +++ .../Shopify/sarama/acl_create_request.go | 76 ++ .../Shopify/sarama/acl_create_response.go | 88 ++ .../Shopify/sarama/acl_delete_request.go | 48 + .../Shopify/sarama/acl_delete_response.go | 155 +++ .../Shopify/sarama/acl_describe_request.go | 25 + .../Shopify/sarama/acl_describe_response.go | 80 ++ .../github.com/Shopify/sarama/acl_filter.go | 61 ++ vendor/github.com/Shopify/sarama/acl_types.go | 42 + .../sarama/add_offsets_to_txn_request.go | 52 + .../sarama/add_offsets_to_txn_response.go | 44 + .../sarama/add_partitions_to_txn_request.go | 76 ++ .../sarama/add_partitions_to_txn_response.go | 108 ++ vendor/github.com/Shopify/sarama/admin.go | 382 +++++++ .../Shopify/sarama/alter_configs_request.go | 120 +++ .../Shopify/sarama/alter_configs_response.go | 95 ++ .../Shopify/sarama/api_versions_request.go | 24 + .../Shopify/sarama/api_versions_response.go | 87 ++ .../Shopify/sarama/async_producer.go | 932 ++++++++++++++++++ .../Shopify/sarama/balance_strategy.go | 129 +++ vendor/github.com/Shopify/sarama/broker.go | 884 +++++++++++++++++ vendor/github.com/Shopify/sarama/client.go | 876 ++++++++++++++++ vendor/github.com/Shopify/sarama/config.go | 563 +++++++++++ .../Shopify/sarama/config_resource_type.go | 15 + vendor/github.com/Shopify/sarama/consumer.go | 807 +++++++++++++++ .../Shopify/sarama/consumer_group.go | 774 +++++++++++++++ .../Shopify/sarama/consumer_group_members.go | 94 ++ .../sarama/consumer_metadata_request.go | 33 + .../sarama/consumer_metadata_response.go | 77 ++ .../github.com/Shopify/sarama/crc32_field.go | 69 ++ .../sarama/create_partitions_request.go | 121 +++ .../sarama/create_partitions_response.go | 94 ++ .../Shopify/sarama/create_topics_request.go | 174 ++++ .../Shopify/sarama/create_topics_response.go | 112 +++ .../Shopify/sarama/delete_groups_request.go | 30 + .../Shopify/sarama/delete_groups_response.go | 70 ++ .../Shopify/sarama/delete_records_request.go | 126 +++ .../Shopify/sarama/delete_records_response.go | 158 +++ .../Shopify/sarama/delete_topics_request.go | 48 + .../Shopify/sarama/delete_topics_response.go | 78 ++ .../sarama/describe_configs_request.go | 91 ++ .../sarama/describe_configs_response.go | 188 ++++ .../Shopify/sarama/describe_groups_request.go | 30 + .../sarama/describe_groups_response.go | 187 ++++ .../Shopify/sarama/encoder_decoder.go | 89 ++ .../Shopify/sarama/end_txn_request.go | 50 + .../Shopify/sarama/end_txn_response.go | 44 + vendor/github.com/Shopify/sarama/errors.go | 281 ++++++ .../Shopify/sarama/fetch_request.go | 170 ++++ .../Shopify/sarama/fetch_response.go | 396 ++++++++ .../sarama/find_coordinator_request.go | 61 ++ .../sarama/find_coordinator_response.go | 92 ++ .../Shopify/sarama/heartbeat_request.go | 47 + .../Shopify/sarama/heartbeat_response.go | 32 + .../sarama/init_producer_id_request.go | 43 + .../sarama/init_producer_id_response.go | 55 ++ .../Shopify/sarama/join_group_request.go | 163 +++ .../Shopify/sarama/join_group_response.go | 135 +++ .../Shopify/sarama/leave_group_request.go | 40 + .../Shopify/sarama/leave_group_response.go | 32 + .../github.com/Shopify/sarama/length_field.go | 82 ++ .../Shopify/sarama/list_groups_request.go | 24 + .../Shopify/sarama/list_groups_response.go | 69 ++ vendor/github.com/Shopify/sarama/message.go | 223 +++++ .../github.com/Shopify/sarama/message_set.go | 108 ++ .../Shopify/sarama/metadata_request.go | 88 ++ .../Shopify/sarama/metadata_response.go | 321 ++++++ vendor/github.com/Shopify/sarama/metrics.go | 51 + .../github.com/Shopify/sarama/mockbroker.go | 330 +++++++ .../Shopify/sarama/mockresponses.go | 727 ++++++++++++++ .../Shopify/sarama/offset_commit_request.go | 204 ++++ .../Shopify/sarama/offset_commit_response.go | 85 ++ .../Shopify/sarama/offset_fetch_request.go | 81 ++ .../Shopify/sarama/offset_fetch_response.go | 143 +++ .../Shopify/sarama/offset_manager.go | 572 +++++++++++ .../Shopify/sarama/offset_request.go | 132 +++ .../Shopify/sarama/offset_response.go | 174 ++++ .../Shopify/sarama/packet_decoder.go | 60 ++ .../Shopify/sarama/packet_encoder.go | 65 ++ .../github.com/Shopify/sarama/partitioner.go | 217 ++++ .../github.com/Shopify/sarama/prep_encoder.go | 153 +++ .../Shopify/sarama/produce_request.go | 252 +++++ .../Shopify/sarama/produce_response.go | 183 ++++ .../github.com/Shopify/sarama/produce_set.go | 252 +++++ .../github.com/Shopify/sarama/real_decoder.go | 324 ++++++ .../github.com/Shopify/sarama/real_encoder.go | 156 +++ vendor/github.com/Shopify/sarama/record.go | 113 +++ .../github.com/Shopify/sarama/record_batch.go | 268 +++++ vendor/github.com/Shopify/sarama/records.go | 194 ++++ vendor/github.com/Shopify/sarama/request.go | 149 +++ .../Shopify/sarama/response_header.go | 21 + vendor/github.com/Shopify/sarama/sarama.go | 99 ++ .../Shopify/sarama/sasl_handshake_request.go | 33 + .../Shopify/sarama/sasl_handshake_response.go | 38 + .../Shopify/sarama/sync_group_request.go | 100 ++ .../Shopify/sarama/sync_group_response.go | 41 + .../Shopify/sarama/sync_producer.go | 149 +++ vendor/github.com/Shopify/sarama/timestamp.go | 40 + .../sarama/txn_offset_commit_request.go | 126 +++ .../sarama/txn_offset_commit_response.go | 83 ++ vendor/github.com/Shopify/sarama/utils.go | 214 ++++ .../github.com/eapache/go-resiliency/LICENSE | 22 + .../eapache/go-resiliency/breaker/breaker.go | 161 +++ .../eapache/go-xerial-snappy/LICENSE | 21 + .../eapache/go-xerial-snappy/fuzz.go | 16 + .../eapache/go-xerial-snappy/snappy.go | 131 +++ vendor/github.com/eapache/queue/LICENSE | 21 + vendor/github.com/eapache/queue/queue.go | 102 ++ vendor/github.com/golang/snappy/AUTHORS | 15 + vendor/github.com/golang/snappy/CONTRIBUTORS | 37 + vendor/github.com/golang/snappy/LICENSE | 27 + vendor/github.com/golang/snappy/decode.go | 237 +++++ .../github.com/golang/snappy/decode_amd64.go | 14 + .../github.com/golang/snappy/decode_amd64.s | 490 +++++++++ .../github.com/golang/snappy/decode_other.go | 101 ++ vendor/github.com/golang/snappy/encode.go | 285 ++++++ .../github.com/golang/snappy/encode_amd64.go | 29 + .../github.com/golang/snappy/encode_amd64.s | 730 ++++++++++++++ .../github.com/golang/snappy/encode_other.go | 238 +++++ vendor/github.com/golang/snappy/snappy.go | 98 ++ vendor/github.com/pierrec/lz4/LICENSE | 28 + vendor/github.com/pierrec/lz4/block.go | 397 ++++++++ vendor/github.com/pierrec/lz4/debug.go | 23 + vendor/github.com/pierrec/lz4/debug_stub.go | 7 + .../pierrec/lz4/internal/xxh32/xxh32zero.go | 222 +++++ vendor/github.com/pierrec/lz4/lz4.go | 68 ++ vendor/github.com/pierrec/lz4/lz4_go1.10.go | 29 + .../github.com/pierrec/lz4/lz4_notgo1.10.go | 29 + vendor/github.com/pierrec/lz4/reader.go | 295 ++++++ vendor/github.com/pierrec/lz4/writer.go | 267 +++++ vendor/github.com/rcrowley/go-metrics/LICENSE | 29 + .../github.com/rcrowley/go-metrics/counter.go | 112 +++ .../github.com/rcrowley/go-metrics/debug.go | 76 ++ vendor/github.com/rcrowley/go-metrics/ewma.go | 138 +++ .../github.com/rcrowley/go-metrics/gauge.go | 120 +++ .../rcrowley/go-metrics/gauge_float64.go | 125 +++ .../rcrowley/go-metrics/graphite.go | 113 +++ .../rcrowley/go-metrics/healthcheck.go | 61 ++ .../rcrowley/go-metrics/histogram.go | 202 ++++ vendor/github.com/rcrowley/go-metrics/json.go | 31 + vendor/github.com/rcrowley/go-metrics/log.go | 80 ++ .../github.com/rcrowley/go-metrics/meter.go | 251 +++++ .../github.com/rcrowley/go-metrics/metrics.go | 13 + .../rcrowley/go-metrics/opentsdb.go | 119 +++ .../rcrowley/go-metrics/registry.go | 363 +++++++ .../github.com/rcrowley/go-metrics/runtime.go | 212 ++++ .../rcrowley/go-metrics/runtime_cgo.go | 10 + .../go-metrics/runtime_gccpufraction.go | 9 + .../rcrowley/go-metrics/runtime_no_cgo.go | 7 + .../go-metrics/runtime_no_gccpufraction.go | 9 + .../github.com/rcrowley/go-metrics/sample.go | 616 ++++++++++++ .../github.com/rcrowley/go-metrics/syslog.go | 78 ++ .../github.com/rcrowley/go-metrics/timer.go | 329 +++++++ .../github.com/rcrowley/go-metrics/writer.go | 100 ++ 175 files changed, 26201 insertions(+), 12 deletions(-) create mode 100644 config/provisioners/kafka/README.md create mode 100644 config/provisioners/kafka/broker/README.md create mode 100644 config/provisioners/kafka/broker/kafka-broker.yaml create mode 100644 config/provisioners/kafka/kafka-provisioner.yaml create mode 100644 pkg/provisioners/kafka/controller/channel/provider.go create mode 100644 pkg/provisioners/kafka/controller/channel/reconcile.go create mode 100644 pkg/provisioners/kafka/controller/channel/reconcile_test.go create mode 100644 pkg/provisioners/kafka/controller/provider.go create mode 100644 pkg/provisioners/kafka/controller/reconcile.go create mode 100644 pkg/provisioners/kafka/controller/reconcile_test.go create mode 100644 pkg/provisioners/kafka/controller/types.go create mode 100644 pkg/provisioners/kafka/main.go create mode 100644 pkg/provisioners/logging.go create mode 100644 vendor/github.com/Shopify/sarama/LICENSE create mode 100644 vendor/github.com/Shopify/sarama/acl_bindings.go create mode 100644 vendor/github.com/Shopify/sarama/acl_create_request.go create mode 100644 vendor/github.com/Shopify/sarama/acl_create_response.go create mode 100644 vendor/github.com/Shopify/sarama/acl_delete_request.go create mode 100644 vendor/github.com/Shopify/sarama/acl_delete_response.go create mode 100644 vendor/github.com/Shopify/sarama/acl_describe_request.go create mode 100644 vendor/github.com/Shopify/sarama/acl_describe_response.go create mode 100644 vendor/github.com/Shopify/sarama/acl_filter.go create mode 100644 vendor/github.com/Shopify/sarama/acl_types.go create mode 100644 vendor/github.com/Shopify/sarama/add_offsets_to_txn_request.go create mode 100644 vendor/github.com/Shopify/sarama/add_offsets_to_txn_response.go create mode 100644 vendor/github.com/Shopify/sarama/add_partitions_to_txn_request.go create mode 100644 vendor/github.com/Shopify/sarama/add_partitions_to_txn_response.go create mode 100644 vendor/github.com/Shopify/sarama/admin.go create mode 100644 vendor/github.com/Shopify/sarama/alter_configs_request.go create mode 100644 vendor/github.com/Shopify/sarama/alter_configs_response.go create mode 100644 vendor/github.com/Shopify/sarama/api_versions_request.go create mode 100644 vendor/github.com/Shopify/sarama/api_versions_response.go create mode 100644 vendor/github.com/Shopify/sarama/async_producer.go create mode 100644 vendor/github.com/Shopify/sarama/balance_strategy.go create mode 100644 vendor/github.com/Shopify/sarama/broker.go create mode 100644 vendor/github.com/Shopify/sarama/client.go create mode 100644 vendor/github.com/Shopify/sarama/config.go create mode 100644 vendor/github.com/Shopify/sarama/config_resource_type.go create mode 100644 vendor/github.com/Shopify/sarama/consumer.go create mode 100644 vendor/github.com/Shopify/sarama/consumer_group.go create mode 100644 vendor/github.com/Shopify/sarama/consumer_group_members.go create mode 100644 vendor/github.com/Shopify/sarama/consumer_metadata_request.go create mode 100644 vendor/github.com/Shopify/sarama/consumer_metadata_response.go create mode 100644 vendor/github.com/Shopify/sarama/crc32_field.go create mode 100644 vendor/github.com/Shopify/sarama/create_partitions_request.go create mode 100644 vendor/github.com/Shopify/sarama/create_partitions_response.go create mode 100644 vendor/github.com/Shopify/sarama/create_topics_request.go create mode 100644 vendor/github.com/Shopify/sarama/create_topics_response.go create mode 100644 vendor/github.com/Shopify/sarama/delete_groups_request.go create mode 100644 vendor/github.com/Shopify/sarama/delete_groups_response.go create mode 100644 vendor/github.com/Shopify/sarama/delete_records_request.go create mode 100644 vendor/github.com/Shopify/sarama/delete_records_response.go create mode 100644 vendor/github.com/Shopify/sarama/delete_topics_request.go create mode 100644 vendor/github.com/Shopify/sarama/delete_topics_response.go create mode 100644 vendor/github.com/Shopify/sarama/describe_configs_request.go create mode 100644 vendor/github.com/Shopify/sarama/describe_configs_response.go create mode 100644 vendor/github.com/Shopify/sarama/describe_groups_request.go create mode 100644 vendor/github.com/Shopify/sarama/describe_groups_response.go create mode 100644 vendor/github.com/Shopify/sarama/encoder_decoder.go create mode 100644 vendor/github.com/Shopify/sarama/end_txn_request.go create mode 100644 vendor/github.com/Shopify/sarama/end_txn_response.go create mode 100644 vendor/github.com/Shopify/sarama/errors.go create mode 100644 vendor/github.com/Shopify/sarama/fetch_request.go create mode 100644 vendor/github.com/Shopify/sarama/fetch_response.go create mode 100644 vendor/github.com/Shopify/sarama/find_coordinator_request.go create mode 100644 vendor/github.com/Shopify/sarama/find_coordinator_response.go create mode 100644 vendor/github.com/Shopify/sarama/heartbeat_request.go create mode 100644 vendor/github.com/Shopify/sarama/heartbeat_response.go create mode 100644 vendor/github.com/Shopify/sarama/init_producer_id_request.go create mode 100644 vendor/github.com/Shopify/sarama/init_producer_id_response.go create mode 100644 vendor/github.com/Shopify/sarama/join_group_request.go create mode 100644 vendor/github.com/Shopify/sarama/join_group_response.go create mode 100644 vendor/github.com/Shopify/sarama/leave_group_request.go create mode 100644 vendor/github.com/Shopify/sarama/leave_group_response.go create mode 100644 vendor/github.com/Shopify/sarama/length_field.go create mode 100644 vendor/github.com/Shopify/sarama/list_groups_request.go create mode 100644 vendor/github.com/Shopify/sarama/list_groups_response.go create mode 100644 vendor/github.com/Shopify/sarama/message.go create mode 100644 vendor/github.com/Shopify/sarama/message_set.go create mode 100644 vendor/github.com/Shopify/sarama/metadata_request.go create mode 100644 vendor/github.com/Shopify/sarama/metadata_response.go create mode 100644 vendor/github.com/Shopify/sarama/metrics.go create mode 100644 vendor/github.com/Shopify/sarama/mockbroker.go create mode 100644 vendor/github.com/Shopify/sarama/mockresponses.go create mode 100644 vendor/github.com/Shopify/sarama/offset_commit_request.go create mode 100644 vendor/github.com/Shopify/sarama/offset_commit_response.go create mode 100644 vendor/github.com/Shopify/sarama/offset_fetch_request.go create mode 100644 vendor/github.com/Shopify/sarama/offset_fetch_response.go create mode 100644 vendor/github.com/Shopify/sarama/offset_manager.go create mode 100644 vendor/github.com/Shopify/sarama/offset_request.go create mode 100644 vendor/github.com/Shopify/sarama/offset_response.go create mode 100644 vendor/github.com/Shopify/sarama/packet_decoder.go create mode 100644 vendor/github.com/Shopify/sarama/packet_encoder.go create mode 100644 vendor/github.com/Shopify/sarama/partitioner.go create mode 100644 vendor/github.com/Shopify/sarama/prep_encoder.go create mode 100644 vendor/github.com/Shopify/sarama/produce_request.go create mode 100644 vendor/github.com/Shopify/sarama/produce_response.go create mode 100644 vendor/github.com/Shopify/sarama/produce_set.go create mode 100644 vendor/github.com/Shopify/sarama/real_decoder.go create mode 100644 vendor/github.com/Shopify/sarama/real_encoder.go create mode 100644 vendor/github.com/Shopify/sarama/record.go create mode 100644 vendor/github.com/Shopify/sarama/record_batch.go create mode 100644 vendor/github.com/Shopify/sarama/records.go create mode 100644 vendor/github.com/Shopify/sarama/request.go create mode 100644 vendor/github.com/Shopify/sarama/response_header.go create mode 100644 vendor/github.com/Shopify/sarama/sarama.go create mode 100644 vendor/github.com/Shopify/sarama/sasl_handshake_request.go create mode 100644 vendor/github.com/Shopify/sarama/sasl_handshake_response.go create mode 100644 vendor/github.com/Shopify/sarama/sync_group_request.go create mode 100644 vendor/github.com/Shopify/sarama/sync_group_response.go create mode 100644 vendor/github.com/Shopify/sarama/sync_producer.go create mode 100644 vendor/github.com/Shopify/sarama/timestamp.go create mode 100644 vendor/github.com/Shopify/sarama/txn_offset_commit_request.go create mode 100644 vendor/github.com/Shopify/sarama/txn_offset_commit_response.go create mode 100644 vendor/github.com/Shopify/sarama/utils.go create mode 100644 vendor/github.com/eapache/go-resiliency/LICENSE create mode 100644 vendor/github.com/eapache/go-resiliency/breaker/breaker.go create mode 100644 vendor/github.com/eapache/go-xerial-snappy/LICENSE create mode 100644 vendor/github.com/eapache/go-xerial-snappy/fuzz.go create mode 100644 vendor/github.com/eapache/go-xerial-snappy/snappy.go create mode 100644 vendor/github.com/eapache/queue/LICENSE create mode 100644 vendor/github.com/eapache/queue/queue.go create mode 100644 vendor/github.com/golang/snappy/AUTHORS create mode 100644 vendor/github.com/golang/snappy/CONTRIBUTORS create mode 100644 vendor/github.com/golang/snappy/LICENSE create mode 100644 vendor/github.com/golang/snappy/decode.go create mode 100644 vendor/github.com/golang/snappy/decode_amd64.go create mode 100644 vendor/github.com/golang/snappy/decode_amd64.s create mode 100644 vendor/github.com/golang/snappy/decode_other.go create mode 100644 vendor/github.com/golang/snappy/encode.go create mode 100644 vendor/github.com/golang/snappy/encode_amd64.go create mode 100644 vendor/github.com/golang/snappy/encode_amd64.s create mode 100644 vendor/github.com/golang/snappy/encode_other.go create mode 100644 vendor/github.com/golang/snappy/snappy.go create mode 100644 vendor/github.com/pierrec/lz4/LICENSE create mode 100644 vendor/github.com/pierrec/lz4/block.go create mode 100644 vendor/github.com/pierrec/lz4/debug.go create mode 100644 vendor/github.com/pierrec/lz4/debug_stub.go create mode 100644 vendor/github.com/pierrec/lz4/internal/xxh32/xxh32zero.go create mode 100644 vendor/github.com/pierrec/lz4/lz4.go create mode 100644 vendor/github.com/pierrec/lz4/lz4_go1.10.go create mode 100644 vendor/github.com/pierrec/lz4/lz4_notgo1.10.go create mode 100644 vendor/github.com/pierrec/lz4/reader.go create mode 100644 vendor/github.com/pierrec/lz4/writer.go create mode 100644 vendor/github.com/rcrowley/go-metrics/LICENSE create mode 100644 vendor/github.com/rcrowley/go-metrics/counter.go create mode 100644 vendor/github.com/rcrowley/go-metrics/debug.go create mode 100644 vendor/github.com/rcrowley/go-metrics/ewma.go create mode 100644 vendor/github.com/rcrowley/go-metrics/gauge.go create mode 100644 vendor/github.com/rcrowley/go-metrics/gauge_float64.go create mode 100644 vendor/github.com/rcrowley/go-metrics/graphite.go create mode 100644 vendor/github.com/rcrowley/go-metrics/healthcheck.go create mode 100644 vendor/github.com/rcrowley/go-metrics/histogram.go create mode 100644 vendor/github.com/rcrowley/go-metrics/json.go create mode 100644 vendor/github.com/rcrowley/go-metrics/log.go create mode 100644 vendor/github.com/rcrowley/go-metrics/meter.go create mode 100644 vendor/github.com/rcrowley/go-metrics/metrics.go create mode 100644 vendor/github.com/rcrowley/go-metrics/opentsdb.go create mode 100644 vendor/github.com/rcrowley/go-metrics/registry.go create mode 100644 vendor/github.com/rcrowley/go-metrics/runtime.go create mode 100644 vendor/github.com/rcrowley/go-metrics/runtime_cgo.go create mode 100644 vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go create mode 100644 vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go create mode 100644 vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go create mode 100644 vendor/github.com/rcrowley/go-metrics/sample.go create mode 100644 vendor/github.com/rcrowley/go-metrics/syslog.go create mode 100644 vendor/github.com/rcrowley/go-metrics/timer.go create mode 100644 vendor/github.com/rcrowley/go-metrics/writer.go diff --git a/Gopkg.lock b/Gopkg.lock index 4b42c229402..a221505d40a 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -8,6 +8,14 @@ pruneopts = "NUT" revision = "90f2606161ee6a14efe2ca79fc05ac2b8efe250b" +[[projects]] + digest = "1:a074ae0f4788ea4c4c7045ab37f21943920bc20cf6ff8afcb2d971154cfa87ab" + name = "github.com/Shopify/sarama" + packages = ["."] + pruneopts = "NUT" + revision = "ec843464b50d4c8b56403ec9d589cf41ea30e722" + version = "v1.19.0" + [[projects]] branch = "master" digest = "1:707ebe952a8b3d00b343c01536c79c73771d100f63ec6babeaed5c79e2b8a8dd" @@ -24,6 +32,30 @@ revision = "346938d642f2ec3594ed81d874461961cd0faa76" version = "v1.1.0" +[[projects]] + digest = "1:08143362be979b087c2c1bae5dde986e988d3d5d4dc661727cbe436411b3f33a" + name = "github.com/eapache/go-resiliency" + packages = ["breaker"] + pruneopts = "NUT" + revision = "ea41b0fad31007accc7f806884dcdf3da98b79ce" + version = "v1.1.0" + +[[projects]] + branch = "master" + digest = "1:d3430c048e919ed27813d20dc65a32d4e3bae3ad05b83700e244a81eaaf48e2a" + name = "github.com/eapache/go-xerial-snappy" + packages = ["."] + pruneopts = "NUT" + revision = "776d5712da21bc4762676d614db1d8a64f4238b0" + +[[projects]] + digest = "1:0d36a2b325b9e75f8057f7f9fbe778d348d70ba652cb9335485b69d1a5c4e038" + name = "github.com/eapache/queue" + packages = ["."] + pruneopts = "NUT" + revision = "44cc805cf13205b55f69e14bcb69867d1ae92f98" + version = "v1.1.0" + [[projects]] digest = "1:32598368f409bbee79deb9d43569fcd92b9fb27f39155f5e166b3371217f051f" name = "github.com/evanphx/json-patch" @@ -113,6 +145,14 @@ revision = "b4deda0973fb4c70b50d226b1af49f3da59f5265" version = "v1.1.0" +[[projects]] + branch = "master" + digest = "1:7f114b78210bf5b75f307fc97cff293633c835bab1e0ea8a744a44b39c042dfe" + name = "github.com/golang/snappy" + packages = ["."] + pruneopts = "NUT" + revision = "2e65f85255dbc3072edf28d6b5b8efc472979f5a" + [[projects]] branch = "master" digest = "1:245bd4eb633039cd66106a5d340ae826d87f4e36a8602fcc940e14176fd26ea7" @@ -358,6 +398,17 @@ revision = "5f041e8faa004a95c88a202771f4cc3e991971e6" version = "v2.0.1" +[[projects]] + digest = "1:1d920dce8e11bfff65b5709e883a8ece131b63a5bc4b2cd404f9ef7eb445f73f" + name = "github.com/pierrec/lz4" + packages = [ + ".", + "internal/xxh32", + ] + pruneopts = "NUT" + revision = "635575b42742856941dbc767b44905bb9ba083f6" + version = "v2.0.7" + [[projects]] digest = "1:03bca087b180bf24c4f9060775f137775550a0834e18f0bca0520a868679dbd7" name = "github.com/prometheus/client_golang" @@ -402,6 +453,14 @@ pruneopts = "NUT" revision = "94663424ae5ae9856b40a9f170762b4197024661" +[[projects]] + branch = "master" + digest = "1:120b256a4d3cd2946ffa4b87102731c2f004aed6d836dc2fba400ed9398696e7" + name = "github.com/rcrowley/go-metrics" + packages = ["."] + pruneopts = "NUT" + revision = "3113b8401b8a98917cde58f8bbd42a1b1c03b1fd" + [[projects]] digest = "1:15e5c398fbd9d2c439b635a08ac161b13d04f0c2aa587fe256b65dc0c3efe8b7" name = "github.com/spf13/pflag" @@ -919,6 +978,7 @@ analyzer-name = "dep" analyzer-version = 1 input-imports = [ + "github.com/Shopify/sarama", "github.com/fsnotify/fsnotify", "github.com/golang/glog", "github.com/google/go-cmp/cmp", diff --git a/Gopkg.toml b/Gopkg.toml index bb939be2027..9ea4d08a427 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -79,6 +79,10 @@ required = [ name = "github.com/knative/serving" version = "v0.1.1" +[[override]] + name = "github.com/Shopify/sarama" + version = "1.19.0" + [[constraint]] name = "sigs.k8s.io/controller-runtime" # HEAD as of 2018-09-19 diff --git a/config/provisioners/kafka/README.md b/config/provisioners/kafka/README.md new file mode 100644 index 00000000000..b8c18cf31b7 --- /dev/null +++ b/config/provisioners/kafka/README.md @@ -0,0 +1,78 @@ +# Apache Kafka Channels + +Deployment steps: +1. Setup [Knative Eventing](../../../DEVELOPMENT.md) +1. If not done already, install an Apache Kafka cluster. There are two choices: + * Simple installation of [Apache Kafka](broker). + * A production grade installation using the [Strimzi Kafka Operator](strimzi). + Installation [guides](http://strimzi.io/quickstarts/) are provided for + kubernetes and Openshift. + +1. Now that Apache Kafka is installed, you need to configure the +`bootstrap_servers` value in the `kafka-channel-controller-config` ConfigMap, +located inside the `config/provisioners/kafka/kafka-provisioner.yaml` file: + ``` + ... + apiVersion: v1 + kind: ConfigMap + metadata: + name: kafka-channel-controller-config + namespace: knative-eventing + data: + # Broker URL's for the provisioner + bootstrap_servers: kafkabroker.kafka:9092 + ... + ``` + > Note: The `bootstrap_servers` needs to contain the address of at least + one broker of your Apache Kafka cluster. If you are using Strimzi, you need + to update the `bootstrap_servers` value to + `my-cluster-kafka-bootstrap.mynamespace:9092`. +1. Apply the 'Kafka' ClusterChannelProvisioner, Controller, and Dispatcher: + ``` + ko apply -f config/provisioners/kafka/kafka-provisioner.yaml + ``` +1. Create Channels that reference the 'kafka-channel'. + + ```yaml + apiVersion: eventing.knative.dev/v1alpha1 + kind: Channel + metadata: + name: my-kafka-channel + spec: + provisioner: + apiVersion: eventing.knative.dev/v1alpha1 + kind: ClusterChannelProvisioner + name: kafka-channel + ``` + +## Components + +The major components are: +* ClusterChannelProvisioner Controller +* Channel Controller +* Channel Controller Config Map. +* Channel Dispatcher +* Channel Dispatcher Config Map. + +The ClusterChannelProvisioner Controller and the Channel Controller are colocated +in one Pod: +```shell +kubectl get deployment -n knative-eventing kafka-channel-controller +``` + +The Channel Controller Config Map is used to configure the `bootstrap_servers` +of your Apache Kafka installation: +```shell +kubectl get configmap -n knative-eventing kafka-channel-dispatcher-config-map +``` + +The Channel Dispatcher receives and distributes all events: +```shell +kubectl get statefulset -n knative-eventing kafka-channel-dispatcher +``` + +The Channel Dispatcher Config Map is used to send information about Channels and +Subscriptions from the Channel Controller to the Channel Dispatcher: +```shell +kubectl get configmap -n knative-eventing kafka-channel-dispatcher-config-map +``` diff --git a/config/provisioners/kafka/broker/README.md b/config/provisioners/kafka/broker/README.md new file mode 100644 index 00000000000..b47d0945734 --- /dev/null +++ b/config/provisioners/kafka/broker/README.md @@ -0,0 +1,13 @@ +# Apache Kafka - simple installation + +1. For an installation of a simple (**non production**) Apache Kafka cluster, a setup is provided: + ``` + kubectl create namespace kafka + kubectl apply -n kafka -f kafka-broker.yaml + ``` + > Note: If you are running Knative on OpenShift you will need to run the following command first to allow the Kafka broker to run as root: + ``` + oc adm policy add-scc-to-user anyuid -z default -n kafka + ``` + +Continue the configuration of Knative Eventing with [step `3`](../). diff --git a/config/provisioners/kafka/broker/kafka-broker.yaml b/config/provisioners/kafka/broker/kafka-broker.yaml new file mode 100644 index 00000000000..469e697a526 --- /dev/null +++ b/config/provisioners/kafka/broker/kafka-broker.yaml @@ -0,0 +1,87 @@ +########################################## KAFKA BROKER ###################################### +# The following does not need to live in the same namespace as the bus. +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: kafka-broker +spec: + replicas: 1 + template: + metadata: + labels: + app: kafka-broker + spec: + containers: + - name: kafka-broker + image: wurstmeister/kafka:1.1.0 + ports: + - containerPort: 9092 + env: + - name: MY_POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: KAFKA_BROKER_ID + value: "0" + - name: KAFKA_LISTENERS + value: "INTERNAL://:9093,EXTERNAL://:9092" + - name: KAFKA_ADVERTISED_LISTENERS + value: "INTERNAL://:9093,EXTERNAL://kafkabroker.$(MY_POD_NAMESPACE):9092" + - name: KAFKA_LISTENER_SECURITY_PROTOCOL_MAP + value: "INTERNAL:PLAINTEXT,EXTERNAL:PLAINTEXT" + - name: KAFKA_INTER_BROKER_LISTENER_NAME + value: "INTERNAL" + - name: KAFKA_ZOOKEEPER_CONNECT + value: "zookeeper.$(MY_POD_NAMESPACE):2181" + - name: KAFKA_AUTO_CREATE_TOPICS_ENABLE + value: "false" +--- +apiVersion: v1 +kind: Service +metadata: + name: kafkabroker +spec: + type: NodePort + selector: + app: kafka-broker + ports: + - port: 9092 + name: kafka + protocol: TCP +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: zookeeper +spec: + replicas: 1 + template: + metadata: + labels: + app: zookeeper + spec: + containers: + - name: zookeeper + image: wurstmeister/zookeeper:3.4.6 + ports: + - containerPort: 2181 + env: + - name: ZOOKEEPER_ID + value: "1" + - name: ZOOKEEPER_SERVER_1 + value: zookeeper + +--- +apiVersion: v1 +kind: Service +metadata: + name: zookeeper +spec: + selector: + app: zookeeper + ports: + - port: 2181 + name: zookeeper + protocol: TCP + diff --git a/config/provisioners/kafka/kafka-provisioner.yaml b/config/provisioners/kafka/kafka-provisioner.yaml new file mode 100644 index 00000000000..746e39ab341 --- /dev/null +++ b/config/provisioners/kafka/kafka-provisioner.yaml @@ -0,0 +1,85 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://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. + +apiVersion: eventing.knative.dev/v1alpha1 +kind: ClusterChannelProvisioner +metadata: + name: kafka-channel +spec: {} +--- + +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kafka-channel-controller + namespace: knative-eventing +--- + +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1 +metadata: + name: kafka-channel-controller +rules: +- apiGroups: ["eventing.knative.dev"] + resources: ["clusterchannelprovisioners", "channels"] + verbs: ["get", "watch", "list", "update"] +--- + +apiVersion: rbac.authorization.k8s.io/v1beta1 +kind: ClusterRoleBinding +metadata: + name: kafka-channel-controller-manage +subjects: + - kind: ServiceAccount + name: kafka-channel-controller + namespace: knative-eventing +roleRef: + kind: ClusterRole + name: kafka-channel-controller + apiGroup: rbac.authorization.k8s.io +--- + +apiVersion: v1 +kind: ConfigMap +metadata: + name: kafka-channel-controller-config + namespace: knative-eventing +data: + # Broker URL's for the provisioner + bootstrap_servers: kafkabroker.kafka:9092 +--- + +apiVersion: apps/v1beta1 +kind: Deployment +metadata: + name: kafka-channel-controller + namespace: knative-eventing +spec: + replicas: 1 + template: + metadata: + labels: + app: kafka-channel-controller + spec: + serviceAccountName: kafka-channel-controller + containers: + - name: kafka-channel-controller-controller + image: github.com/knative/eventing/pkg/provisioners/kafka + volumeMounts: + - name: kafka-channel-controller-config + mountPath: /etc/config-provisioner + volumes: + - name: kafka-channel-controller-config + configMap: + name: kafka-channel-controller-config diff --git a/pkg/apis/eventing/v1alpha1/channel_types.go b/pkg/apis/eventing/v1alpha1/channel_types.go index afcf9805211..2b6606bb717 100644 --- a/pkg/apis/eventing/v1alpha1/channel_types.go +++ b/pkg/apis/eventing/v1alpha1/channel_types.go @@ -136,6 +136,11 @@ func (cs *ChannelStatus) MarkProvisioned() { chanCondSet.Manage(cs).MarkTrue(ChannelConditionProvisioned) } +// MarkNotProvisioned sets ChannelConditionProvisioned condition to False state. +func (cs *ChannelStatus) MarkNotProvisioned(reason, messageFormat string, messageA ...interface{}) { + chanCondSet.Manage(cs).MarkFalse(ChannelConditionProvisioned, reason, messageFormat, messageA...) +} + // SetAddress makes this Channel addressable by setting the hostname. It also // sets the ChannelConditionAddressable to true. func (cs *ChannelStatus) SetAddress(hostname string) { diff --git a/pkg/apis/eventing/v1alpha1/channel_types_test.go b/pkg/apis/eventing/v1alpha1/channel_types_test.go index ed9d3737713..d96bb62e38f 100644 --- a/pkg/apis/eventing/v1alpha1/channel_types_test.go +++ b/pkg/apis/eventing/v1alpha1/channel_types_test.go @@ -179,6 +179,8 @@ func TestChannelIsReady(t *testing.T) { cs := &ChannelStatus{} if test.markProvisioned { cs.MarkProvisioned() + } else { + cs.MarkNotProvisioned("NotProvisioned", "testing") } if test.setAddress { cs.SetAddress("foo.bar") diff --git a/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types.go b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types.go index 657bd67bab3..751925c66eb 100644 --- a/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types.go +++ b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types.go @@ -93,6 +93,11 @@ func (ps *ClusterChannelProvisionerStatus) IsReady() bool { return ccProvCondSet.Manage(ps).IsHappy() } +// MarkProvisionerNotReady sets the condition that the provisioner is not ready to provision backing resource. +func (ps *ClusterChannelProvisionerStatus) MarkNotReady(reason, messageFormat string, messageA ...interface{}) { + ccProvCondSet.Manage(ps).MarkFalse(ClusterChannelProvisionerConditionReady, reason, messageFormat, messageA...) +} + // InitializeConditions sets relevant unset conditions to Unknown state. func (ps *ClusterChannelProvisionerStatus) InitializeConditions() { ccProvCondSet.Manage(ps).InitializeConditions() diff --git a/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types_test.go b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types_test.go index ac2fa9ae915..c7943ee0d6e 100644 --- a/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types_test.go +++ b/pkg/apis/eventing/v1alpha1/cluster_channel_provisioner_types_test.go @@ -43,21 +43,21 @@ func TestClusterChannelProvisionerStatusIsReady(t *testing.T) { want: false, }, { name: "ready true condition", - ps: &ClusterChannelProvisionerStatus{ - Conditions: []duckv1alpha1.Condition{{ - Type: ChannelConditionReady, - Status: corev1.ConditionTrue, - }}, - }, + ps: func() *ClusterChannelProvisionerStatus { + ps := &ClusterChannelProvisionerStatus{} + ps.InitializeConditions() + ps.MarkReady() + return ps + }(), want: true, }, { name: "ready false condition", - ps: &ClusterChannelProvisionerStatus{ - Conditions: []duckv1alpha1.Condition{{ - Type: ChannelConditionReady, - Status: corev1.ConditionFalse, - }}, - }, + ps: func() *ClusterChannelProvisionerStatus { + ps := &ClusterChannelProvisionerStatus{} + ps.InitializeConditions() + ps.MarkNotReady("Not Ready", "testing") + return ps + }(), want: false, }, { name: "unknown condition", diff --git a/pkg/provisioners/kafka/controller/channel/provider.go b/pkg/provisioners/kafka/controller/channel/provider.go new file mode 100644 index 00000000000..b386576ee9f --- /dev/null +++ b/pkg/provisioners/kafka/controller/channel/provider.go @@ -0,0 +1,78 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package channel + +import ( + "github.com/Shopify/sarama" + "go.uber.org/zap" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" + + "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + common "github.com/knative/eventing/pkg/provisioners/kafka/controller" +) + +const ( + // controllerAgentName is the string used by this controller to identify + // itself when creating events. + controllerAgentName = "kafka-provisioner-channel-controller" +) + +type reconciler struct { + client client.Client + recorder record.EventRecorder + logger *zap.Logger + config *common.KafkaProvisionerConfig + // Using a shared kafkaClusterAdmin does not work currently because of an issue with + // Shopify/sarama, see https://github.com/Shopify/sarama/issues/1162. + kafkaClusterAdmin sarama.ClusterAdmin +} + +// Verify the struct implements reconcile.Reconciler +var _ reconcile.Reconciler = &reconciler{} + +// ProvideController returns a Channel controller. +func ProvideController(mgr manager.Manager, config *common.KafkaProvisionerConfig, logger *zap.Logger) (controller.Controller, error) { + // Setup a new controller to Reconcile Channel. + c, err := controller.New(controllerAgentName, mgr, controller.Options{ + Reconciler: &reconciler{ + recorder: mgr.GetRecorder(controllerAgentName), + logger: logger, + config: config, + }, + }) + if err != nil { + return nil, err + } + + // Watch Channel events and enqueue Channel object key. + if err := c.Watch(&source.Kind{Type: &v1alpha1.Channel{}}, &handler.EnqueueRequestForObject{}); err != nil { + return nil, err + } + + return c, nil +} + +func (r *reconciler) InjectClient(c client.Client) error { + r.client = c + return nil +} diff --git a/pkg/provisioners/kafka/controller/channel/reconcile.go b/pkg/provisioners/kafka/controller/channel/reconcile.go new file mode 100644 index 00000000000..dc5248dfcdf --- /dev/null +++ b/pkg/provisioners/kafka/controller/channel/reconcile.go @@ -0,0 +1,266 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package channel + +import ( + "context" + "encoding/json" + "fmt" + + "github.com/Shopify/sarama" + "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/util/sets" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/knative/eventing/pkg/provisioners/kafka/controller" +) + +const ( + finalizerName = controllerAgentName + + DefaultNumPartitions = 1 +) + +type channelArgs struct { + NumPartitions int32 `json:"NumPartitions,omitempty"` +} + +// Reconcile compares the actual state with the desired, and attempts to +// converge the two. It then updates the Status block of the Channel resource +// with the current status of the resource. +func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) { + ctx := context.TODO() + r.logger.Info("Reconciling channel", zap.Any("request", request)) + channel := &v1alpha1.Channel{} + err := r.client.Get(context.TODO(), request.NamespacedName, channel) + + // The Channel may have been deleted since it was added to the workqueue. If so, there is + // nothing to be done since the dependent resources would have been deleted as well. + if errors.IsNotFound(err) { + r.logger.Info("could not find channel", zap.Any("request", request)) + return reconcile.Result{}, nil + } + + // Any other error should be retried in another reconciliation. + if err != nil { + r.logger.Error("could not fetch channel", zap.Error(err)) + return reconcile.Result{}, err + } + + // Skip Channel as it is not targeting any provisioner + if channel.Spec.Provisioner == nil { + return reconcile.Result{}, nil + } + + // Skip channel not managed by this provisioner + clusterChannelProvisioner, err := r.getClusterChannelProvisioner() + if err != nil { + return reconcile.Result{}, err + } + provisionerRef := channel.Spec.Provisioner + if provisionerRef.Name != clusterChannelProvisioner.Name { + return reconcile.Result{}, nil + } + + newChannel := channel.DeepCopy() + + newChannel.Status.InitializeConditions() + + if clusterChannelProvisioner.Status.IsReady() { + // Reconcile this copy of the Channel and then write back any status + // updates regardless of whether the reconcile error out. + err = r.reconcile(newChannel) + } else { + newChannel.Status.MarkNotProvisioned("NotProvisioned", "ClusterChannelProvisioner %s is not ready", clusterChannelProvisioner.Name) + err = fmt.Errorf("ClusterChannelProvisioner %s is not ready", clusterChannelProvisioner.Name) + } + + if updateChannelErr := r.updateChannel(ctx, newChannel); updateChannelErr != nil { + r.logger.Info("failed to update channel status", zap.Error(updateChannelErr)) + return reconcile.Result{}, updateChannelErr + } + + // Requeue if the resource is not ready: + return reconcile.Result{}, err +} + +func (r *reconciler) reconcile(channel *v1alpha1.Channel) error { + + // We don't currently initialize r.kafkaClusterAdmin, hence we end up creating the cluster admin client every time. + // This is because of an issue with Shopify/sarama. See https://github.com/Shopify/sarama/issues/1162. + // Once the issue is fixed we should use a shared cluster admin client. Also, r.kafkaClusterAdmin is currently + // used to pass a fake admin client in the tests. + kafkaClusterAdmin := r.kafkaClusterAdmin + if kafkaClusterAdmin == nil { + var err error + kafkaClusterAdmin, err = createKafkaAdminClient(r.config) + if err != nil { + r.logger.Fatal("unable to build kafka admin client", zap.Error(err)) + return err + } + } + + // See if the channel has been deleted + accessor, err := meta.Accessor(channel) + if err != nil { + r.logger.Info("failed to get metadata", zap.Error(err)) + return err + } + deletionTimestamp := accessor.GetDeletionTimestamp() + if deletionTimestamp != nil { + r.logger.Info(fmt.Sprintf("DeletionTimestamp: %v", deletionTimestamp)) + if err := r.deprovisionChannel(channel, kafkaClusterAdmin); err != nil { + return err + } + r.removeFinalizer(channel) + return nil + } + + r.addFinalizer(channel) + + if err := r.provisionChannel(channel, kafkaClusterAdmin); err != nil { + channel.Status.MarkNotProvisioned("NotProvisioned", "error while provisioning: %s", err) + return err + } + channel.Status.MarkProvisioned() + + // close the connection + kafkaClusterAdmin.Close() + + return nil +} + +func (r *reconciler) provisionChannel(channel *v1alpha1.Channel, kafkaClusterAdmin sarama.ClusterAdmin) error { + topicName := topicName(channel) + r.logger.Info("creating topic on kafka cluster", zap.String("topic", topicName)) + + var arguments channelArgs + + if channel.Spec.Arguments != nil { + var err error + arguments, err = unmarshalArguments(channel.Spec.Arguments.Raw) + if err != nil { + return err + } + } + + if arguments.NumPartitions == 0 { + arguments.NumPartitions = DefaultNumPartitions + } + + err := kafkaClusterAdmin.CreateTopic(topicName, &sarama.TopicDetail{ + ReplicationFactor: 1, + NumPartitions: arguments.NumPartitions, + }, false) + if err == sarama.ErrTopicAlreadyExists { + return nil + } else if err != nil { + r.logger.Error("error creating topic", zap.String("topic", topicName), zap.Error(err)) + } else { + r.logger.Info("successfully created topic", zap.String("topic", topicName)) + } + return err +} + +func (r *reconciler) deprovisionChannel(channel *v1alpha1.Channel, kafkaClusterAdmin sarama.ClusterAdmin) error { + topicName := topicName(channel) + r.logger.Info("deleting topic on kafka cluster", zap.String("topic", topicName)) + + err := kafkaClusterAdmin.DeleteTopic(topicName) + if err == sarama.ErrUnknownTopicOrPartition { + return nil + } else if err != nil { + r.logger.Error("error deleting topic", zap.String("topic", topicName), zap.Error(err)) + } else { + r.logger.Info("successfully deleted topic %s", zap.String("topic", topicName)) + } + return err +} + +func (r *reconciler) getClusterChannelProvisioner() (*v1alpha1.ClusterChannelProvisioner, error) { + clusterChannelProvisioner := &v1alpha1.ClusterChannelProvisioner{} + objKey := client.ObjectKey{ + Name: controller.Name, + } + if err := r.client.Get(context.Background(), objKey, clusterChannelProvisioner); err != nil { + return nil, err + } + return clusterChannelProvisioner, nil +} + +func (r *reconciler) updateChannel(ctx context.Context, u *v1alpha1.Channel) error { + channel := &v1alpha1.Channel{} + err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, channel) + if err != nil { + return err + } + + updated := false + if !equality.Semantic.DeepEqual(channel.Finalizers, u.Finalizers) { + channel.SetFinalizers(u.ObjectMeta.Finalizers) + updated = true + } + + if !equality.Semantic.DeepEqual(channel.Status, u.Status) { + channel.Status = u.Status + updated = true + } + + if updated == false { + return nil + } + return r.client.Update(ctx, channel) +} + +func (r *reconciler) addFinalizer(channel *v1alpha1.Channel) { + finalizers := sets.NewString(channel.Finalizers...) + finalizers.Insert(finalizerName) + channel.Finalizers = finalizers.List() +} + +func (r *reconciler) removeFinalizer(channel *v1alpha1.Channel) { + finalizers := sets.NewString(channel.Finalizers...) + finalizers.Delete(finalizerName) + channel.Finalizers = finalizers.List() +} + +func createKafkaAdminClient(config *controller.KafkaProvisionerConfig) (sarama.ClusterAdmin, error) { + saramaConf := sarama.NewConfig() + saramaConf.Version = sarama.V1_1_0_0 + saramaConf.ClientID = controllerAgentName + return sarama.NewClusterAdmin(config.Brokers, saramaConf) +} + +func topicName(channel *v1alpha1.Channel) string { + return fmt.Sprintf("%s.%s", channel.Namespace, channel.Name) +} + +// unmarshalArguments unmarshal's a json/yaml serialized input and returns channelArgs +func unmarshalArguments(bytes []byte) (channelArgs, error) { + var arguments channelArgs + if len(bytes) > 0 { + if err := json.Unmarshal(bytes, &arguments); err != nil { + return arguments, fmt.Errorf("error unmarshalling arguments: %s", err) + } + } + return arguments, nil +} diff --git a/pkg/provisioners/kafka/controller/channel/reconcile_test.go b/pkg/provisioners/kafka/controller/channel/reconcile_test.go new file mode 100644 index 00000000000..6becf1e1cbe --- /dev/null +++ b/pkg/provisioners/kafka/controller/channel/reconcile_test.go @@ -0,0 +1,493 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Veroute.on 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 channel + +import ( + "context" + "encoding/json" + "fmt" + "testing" + + "github.com/Shopify/sarama" + "github.com/google/go-cmp/cmp" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + controllertesting "github.com/knative/eventing/pkg/controller/testing" + "github.com/knative/eventing/pkg/provisioners" + "github.com/knative/eventing/pkg/provisioners/kafka/controller" +) + +const ( + channelName = "test-channel" + clusterChannelProvisionerName = "kafka-channel" + testNS = "test-namespace" + argumentNumPartitions = "NumPartitions" +) + +var ( + deletedTs = metav1.Now().Rfc3339Copy() +) + +func init() { + // Add types to scheme + eventingv1alpha1.AddToScheme(scheme.Scheme) +} + +var mockFetchError = controllertesting.Mocks{ + MockGets: []controllertesting.MockGet{ + func(innerClient client.Client, ctx context.Context, key client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { + if _, ok := obj.(*eventingv1alpha1.Channel); ok { + err := fmt.Errorf("error fetching") + return controllertesting.Handled, err + } + return controllertesting.Unhandled, nil + }, + }, +} + +type mockClusterAdmin struct { + mockCreateTopicFunc func(topic string, detail *sarama.TopicDetail, validateOnly bool) error + mockDeleteTopicFunc func(topic string) error +} + +func (ca *mockClusterAdmin) CreateTopic(topic string, detail *sarama.TopicDetail, validateOnly bool) error { + if ca.mockCreateTopicFunc != nil { + return ca.mockCreateTopicFunc(topic, detail, validateOnly) + } + return nil +} + +func (ca *mockClusterAdmin) Close() error { + return nil +} + +func (ca *mockClusterAdmin) DeleteTopic(topic string) error { + if ca.mockDeleteTopicFunc != nil { + return ca.mockDeleteTopicFunc(topic) + } + return nil +} + +func (ca *mockClusterAdmin) CreatePartitions(topic string, count int32, assignment [][]int32, validateOnly bool) error { + return nil +} + +func (ca *mockClusterAdmin) DeleteRecords(topic string, partitionOffsets map[int32]int64) error { + return nil +} + +func (ca *mockClusterAdmin) DescribeConfig(resource sarama.ConfigResource) ([]sarama.ConfigEntry, error) { + return nil, nil +} + +func (ca *mockClusterAdmin) AlterConfig(resourceType sarama.ConfigResourceType, name string, entries map[string]*string, validateOnly bool) error { + return nil +} + +func (ca *mockClusterAdmin) CreateACL(resource sarama.Resource, acl sarama.Acl) error { + return nil +} + +func (ca *mockClusterAdmin) ListAcls(filter sarama.AclFilter) ([]sarama.ResourceAcls, error) { + return nil, nil +} + +func (ca *mockClusterAdmin) DeleteACL(filter sarama.AclFilter, validateOnly bool) ([]sarama.MatchingAcl, error) { + return nil, nil +} + +var testCases = []controllertesting.TestCase{ + { + Name: "new channel with valid provisioner: adds provisioned status", + InitialState: []runtime.Object{ + getNewClusterChannelProvisioner(clusterChannelProvisionerName, true), + getNewChannel(channelName, clusterChannelProvisionerName), + }, + ReconcileKey: fmt.Sprintf("%s/%s", testNS, channelName), + WantResult: reconcile.Result{}, + WantPresent: []runtime.Object{ + getNewChannelProvisionedStatus(channelName, clusterChannelProvisionerName), + }, + IgnoreTimes: true, + }, + { + Name: "new channel with provisioner not ready: error", + InitialState: []runtime.Object{ + getNewClusterChannelProvisioner(clusterChannelProvisionerName, false), + getNewChannel(channelName, clusterChannelProvisionerName), + }, + ReconcileKey: fmt.Sprintf("%s/%s", testNS, channelName), + WantResult: reconcile.Result{}, + WantErrMsg: "ClusterChannelProvisioner " + clusterChannelProvisionerName + " is not ready", + WantPresent: []runtime.Object{ + getNewChannelNotProvisionedStatus(channelName, clusterChannelProvisionerName, + "ClusterChannelProvisioner "+clusterChannelProvisionerName+" is not ready"), + }, + IgnoreTimes: true, + }, + { + Name: "new channel with missing provisioner: error", + InitialState: []runtime.Object{ + getNewChannel(channelName, clusterChannelProvisionerName), + }, + ReconcileKey: fmt.Sprintf("%s/%s", testNS, channelName), + WantResult: reconcile.Result{}, + WantErrMsg: "clusterchannelprovisioners.eventing.knative.dev \"" + clusterChannelProvisionerName + "\" not found", + IgnoreTimes: true, + }, + { + Name: "new channel with provisioner not managed by this controller: skips channel", + InitialState: []runtime.Object{ + getNewChannel(channelName, "not-our-provisioner"), + getNewClusterChannelProvisioner("not-our-provisioner", true), + getNewClusterChannelProvisioner(clusterChannelProvisionerName, true), + }, + ReconcileKey: fmt.Sprintf("%s/%s", testNS, channelName), + WantResult: reconcile.Result{}, + WantPresent: []runtime.Object{ + getNewChannel(channelName, "not-our-provisioner"), + }, + IgnoreTimes: true, + }, + { + Name: "new channel with missing provisioner reference: skips channel", + InitialState: []runtime.Object{ + getNewChannelNoProvisioner(channelName), + }, + ReconcileKey: fmt.Sprintf("%s/%s", testNS, channelName), + WantResult: reconcile.Result{}, + WantPresent: []runtime.Object{ + getNewChannelNoProvisioner(channelName), + }, + IgnoreTimes: true, + }, + { + Name: "channel not found", + InitialState: []runtime.Object{}, + ReconcileKey: fmt.Sprintf("%s/%s", testNS, channelName), + WantResult: reconcile.Result{}, + WantPresent: []runtime.Object{}, + IgnoreTimes: true, + }, + { + Name: "error fetching channel", + InitialState: []runtime.Object{ + getNewClusterChannelProvisioner(clusterChannelProvisionerName, true), + getNewChannel(channelName, clusterChannelProvisionerName), + }, + Mocks: mockFetchError, + ReconcileKey: fmt.Sprintf("%s/%s", testNS, channelName), + WantErrMsg: "error fetching", + WantPresent: []runtime.Object{ + getNewClusterChannelProvisioner(clusterChannelProvisionerName, true), + getNewChannel(channelName, clusterChannelProvisionerName), + }, + }, + { + Name: "deleted channel", + InitialState: []runtime.Object{ + getNewClusterChannelProvisioner(clusterChannelProvisionerName, true), + getNewChannelDeleted(channelName, clusterChannelProvisionerName), + }, + ReconcileKey: fmt.Sprintf("%s/%s", testNS, channelName), + WantResult: reconcile.Result{}, + WantPresent: []runtime.Object{}, + IgnoreTimes: true, + }, +} + +func TestAllCases(t *testing.T) { + recorder := record.NewBroadcaster().NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) + + for _, tc := range testCases { + c := tc.GetClient() + logger := provisioners.NewProvisionerLoggerFromConfig(provisioners.NewLoggingConfig()) + r := &reconciler{ + client: c, + recorder: recorder, + logger: logger.Desugar(), + config: getControllerConfig(), + kafkaClusterAdmin: &mockClusterAdmin{}, + } + t.Logf("Running test %s", tc.Name) + t.Run(tc.Name, tc.Runner(t, r, c)) + } +} + +func TestProvisionChannel(t *testing.T) { + provisionTestCases := []struct { + name string + c *eventingv1alpha1.Channel + wantTopicName string + wantTopicDetail *sarama.TopicDetail + mockError error + wantError string + }{ + { + name: "provision with no channel arguments - uses default", + c: getNewChannel(channelName, clusterChannelProvisionerName), + wantTopicName: fmt.Sprintf("%s.%s", testNS, channelName), + wantTopicDetail: &sarama.TopicDetail{ + ReplicationFactor: 1, + NumPartitions: 1, + }, + }, + { + name: "provision with unknown channel arguments - uses default", + c: getNewChannelWithArgs(channelName, map[string]interface{}{"testing": "testing"}), + wantTopicName: fmt.Sprintf("%s.%s", testNS, channelName), + wantTopicDetail: &sarama.TopicDetail{ + ReplicationFactor: 1, + NumPartitions: 1, + }, + }, + { + name: "provision with invalid channel arguments - errors", + c: getNewChannelWithArgs(channelName, map[string]interface{}{argumentNumPartitions: "invalid"}), + wantError: fmt.Sprintf("error unmarshalling arguments: json: cannot unmarshal string into Go struct field channelArgs.%s of type int32", argumentNumPartitions), + }, + { + name: "provision with unmarshallable channel arguments - errors", + c: func() *eventingv1alpha1.Channel { + channel := getNewChannel(channelName, clusterChannelProvisionerName) + channel.Spec.Arguments = &runtime.RawExtension{ + Raw: []byte("invalid"), + } + return channel + }(), + wantError: "error unmarshalling arguments: invalid character 'i' looking for beginning of value", + }, + { + name: "provision with valid channel arguments", + c: getNewChannelWithArgs(channelName, map[string]interface{}{argumentNumPartitions: 2}), + wantTopicName: fmt.Sprintf("%s.%s", testNS, channelName), + wantTopicDetail: &sarama.TopicDetail{ + ReplicationFactor: 1, + NumPartitions: 2, + }, + }, + { + name: "provision but topic already exists - no error", + c: getNewChannelWithArgs(channelName, map[string]interface{}{argumentNumPartitions: 2}), + wantTopicName: fmt.Sprintf("%s.%s", testNS, channelName), + wantTopicDetail: &sarama.TopicDetail{ + ReplicationFactor: 1, + NumPartitions: 2, + }, + mockError: sarama.ErrTopicAlreadyExists, + }, + { + name: "provision but error creating topic", + c: getNewChannelWithArgs(channelName, map[string]interface{}{argumentNumPartitions: 2}), + wantTopicName: fmt.Sprintf("%s.%s", testNS, channelName), + wantTopicDetail: &sarama.TopicDetail{ + ReplicationFactor: 1, + NumPartitions: 2, + }, + mockError: fmt.Errorf("unknown sarama error"), + wantError: "unknown sarama error", + }} + + for _, tc := range provisionTestCases { + t.Logf("running test %s", tc.name) + logger := provisioners.NewProvisionerLoggerFromConfig(provisioners.NewLoggingConfig()) + r := &reconciler{ + logger: logger.Desugar(), + } + kafkaClusterAdmin := &mockClusterAdmin{ + mockCreateTopicFunc: func(topic string, detail *sarama.TopicDetail, validateOnly bool) error { + if topic != tc.wantTopicName { + t.Errorf("expected topic name: %+v got: %+v", tc.wantTopicName, topic) + } + return tc.mockError + }} + err := r.provisionChannel(tc.c, kafkaClusterAdmin) + var got string + if err != nil { + got = err.Error() + } + if diff := cmp.Diff(tc.wantError, got); diff != "" { + t.Errorf("unexpected error (-want, +got) = %v", diff) + } + } +} + +func TestDeprovisionChannel(t *testing.T) { + deprovisionTestCases := []struct { + name string + c *eventingv1alpha1.Channel + wantTopicName string + mockError error + wantError string + }{ + { + name: "deprovision channel - unknown error", + c: getNewChannel(channelName, clusterChannelProvisionerName), + wantTopicName: fmt.Sprintf("%s.%s", testNS, channelName), + mockError: fmt.Errorf("unknown sarama error"), + wantError: "unknown sarama error", + }, + { + name: "deprovision channel - topic already deleted", + c: getNewChannel(channelName, clusterChannelProvisionerName), + wantTopicName: fmt.Sprintf("%s.%s", testNS, channelName), + mockError: sarama.ErrUnknownTopicOrPartition, + }, + { + name: "deprovision channel - success", + c: getNewChannel(channelName, clusterChannelProvisionerName), + wantTopicName: fmt.Sprintf("%s.%s", testNS, channelName), + }} + + for _, tc := range deprovisionTestCases { + t.Logf("running test %s", tc.name) + logger := provisioners.NewProvisionerLoggerFromConfig(provisioners.NewLoggingConfig()) + r := &reconciler{ + logger: logger.Desugar()} + kafkaClusterAdmin := &mockClusterAdmin{ + mockDeleteTopicFunc: func(topic string) error { + if topic != tc.wantTopicName { + t.Errorf("expected topic name: %+v got: %+v", tc.wantTopicName, topic) + } + return tc.mockError + }} + + err := r.deprovisionChannel(tc.c, kafkaClusterAdmin) + var got string + if err != nil { + got = err.Error() + } + if diff := cmp.Diff(tc.wantError, got); diff != "" { + t.Errorf("unexpected error (-want, +got) = %v", diff) + } + } +} + +func getNewChannelNoProvisioner(name string) *eventingv1alpha1.Channel { + channel := &eventingv1alpha1.Channel{ + TypeMeta: channelType(), + ObjectMeta: om(testNS, name), + Spec: eventingv1alpha1.ChannelSpec{}, + } + // selflink is not filled in when we create the object, so clear it + channel.ObjectMeta.SelfLink = "" + return channel +} + +func getNewChannel(name, provisioner string) *eventingv1alpha1.Channel { + channel := &eventingv1alpha1.Channel{ + TypeMeta: channelType(), + ObjectMeta: om(testNS, name), + Spec: eventingv1alpha1.ChannelSpec{ + Provisioner: &corev1.ObjectReference{ + Name: provisioner, + Kind: "ClusterChannelProvisioner", + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + }, + }, + } + // selflink is not filled in when we create the object, so clear it + channel.ObjectMeta.SelfLink = "" + return channel +} + +func getNewChannelWithArgs(name string, args map[string]interface{}) *eventingv1alpha1.Channel { + c := getNewChannelNoProvisioner(name) + bytes, _ := json.Marshal(args) + c.Spec.Arguments = &runtime.RawExtension{ + Raw: bytes, + } + return c +} + +func getNewChannelProvisionedStatus(name, provisioner string) *eventingv1alpha1.Channel { + c := getNewChannel(name, provisioner) + c.Status.InitializeConditions() + c.Status.MarkProvisioned() + c.Finalizers = []string{finalizerName} + return c +} + +func getNewChannelDeleted(name, provisioner string) *eventingv1alpha1.Channel { + c := getNewChannelProvisionedStatus(name, provisioner) + c.DeletionTimestamp = &deletedTs + return c +} + +func getNewChannelNotProvisionedStatus(name, provisioner, msg string) *eventingv1alpha1.Channel { + c := getNewChannel(name, provisioner) + c.Status.InitializeConditions() + c.Status.MarkNotProvisioned("NotProvisioned", msg) + return c +} + +func channelType() metav1.TypeMeta { + return metav1.TypeMeta{ + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "Channel", + } +} + +func getNewClusterChannelProvisioner(name string, isReady bool) *eventingv1alpha1.ClusterChannelProvisioner { + var condStatus corev1.ConditionStatus + if isReady { + condStatus = corev1.ConditionTrue + } else { + condStatus = corev1.ConditionFalse + } + clusterChannelProvisioner := &eventingv1alpha1.ClusterChannelProvisioner{ + TypeMeta: metav1.TypeMeta{ + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "ClusterChannelProvisioner", + }, + ObjectMeta: om("", name), + Spec: eventingv1alpha1.ClusterChannelProvisionerSpec{}, + Status: eventingv1alpha1.ClusterChannelProvisionerStatus{ + Conditions: []duckv1alpha1.Condition{ + { + Type: eventingv1alpha1.ClusterChannelProvisionerConditionReady, + Status: condStatus, + }, + }, + }, + } + // selflink is not filled in when we create the object, so clear it + clusterChannelProvisioner.ObjectMeta.SelfLink = "" + return clusterChannelProvisioner +} + +func om(namespace, name string) metav1.ObjectMeta { + return metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + SelfLink: fmt.Sprintf("/apis/eventing/v1alpha1/namespaces/%s/object/%s", namespace, name), + } +} + +func getControllerConfig() *controller.KafkaProvisionerConfig { + return &controller.KafkaProvisionerConfig{ + Brokers: []string{"test-broker"}, + } +} diff --git a/pkg/provisioners/kafka/controller/provider.go b/pkg/provisioners/kafka/controller/provider.go new file mode 100644 index 00000000000..59c0ec94697 --- /dev/null +++ b/pkg/provisioners/kafka/controller/provider.go @@ -0,0 +1,72 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "go.uber.org/zap" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/handler" + "sigs.k8s.io/controller-runtime/pkg/manager" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + "sigs.k8s.io/controller-runtime/pkg/source" +) + +const ( + // controllerAgentName is the string used by this controller to identify + // itself when creating events. + controllerAgentName = "kafka-provisioner-controller" +) + +type reconciler struct { + client client.Client + recorder record.EventRecorder + logger *zap.Logger + config *KafkaProvisionerConfig +} + +// Verify the struct implements reconcile.Reconciler +var _ reconcile.Reconciler = &reconciler{} + +// ProvideController returns a Provisioner controller. +func ProvideController(mgr manager.Manager, config *KafkaProvisionerConfig, logger *zap.Logger) (controller.Controller, error) { + // Setup a new controller to Reconcile Provisioners. + c, err := controller.New(controllerAgentName, mgr, controller.Options{ + Reconciler: &reconciler{ + recorder: mgr.GetRecorder(controllerAgentName), + logger: logger, + config: config, + }, + }) + if err != nil { + return nil, err + } + + // Watch ClusterChannelProvisioner events and enqueue ClusterChannelProvisioner object key. + if err := c.Watch(&source.Kind{Type: &v1alpha1.ClusterChannelProvisioner{}}, &handler.EnqueueRequestForObject{}); err != nil { + return nil, err + } + + return c, nil +} + +func (r *reconciler) InjectClient(c client.Client) error { + r.client = c + return nil +} diff --git a/pkg/provisioners/kafka/controller/reconcile.go b/pkg/provisioners/kafka/controller/reconcile.go new file mode 100644 index 00000000000..dd5d84b47a0 --- /dev/null +++ b/pkg/provisioners/kafka/controller/reconcile.go @@ -0,0 +1,94 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "context" + "fmt" + + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/meta" + "sigs.k8s.io/controller-runtime/pkg/reconcile" + + "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + util "github.com/knative/eventing/pkg/provisioners" +) + +const ( + // Name is the name of the kafka ClusterChannelProvisioner. + Name = "kafka-channel" +) + +// Reconcile compares the actual state with the desired, and attempts to +// converge the two. It then updates the Status block of the Provisioner resource +// with the current status of the resource. +func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, error) { + ctx := context.TODO() + r.logger.Info("reconciling ClusterChannelProvisioner", zap.Any("request", request)) + provisioner := &v1alpha1.ClusterChannelProvisioner{} + err := r.client.Get(context.TODO(), request.NamespacedName, provisioner) + + if errors.IsNotFound(err) { + r.logger.Info("could not find ClusterChannelProvisioner", zap.Any("request", request)) + return reconcile.Result{}, nil + } + + if err != nil { + r.logger.Error("could not fetch ClusterChannelProvisioner", zap.Error(err)) + return reconcile.Result{}, err + } + + // Skip channel provisioners that we don't manage + if provisioner.Name != Name { + r.logger.Info("not reconciling ClusterChannelProvisioner, it is not controlled by this Controller", zap.Any("request", request)) + return reconcile.Result{}, nil + } + + newProvisioner := provisioner.DeepCopy() + + // Reconcile this copy of the Provisioner and then write back any status + // updates regardless of whether the reconcile error out. + err = r.reconcile(newProvisioner) + if updateStatusErr := util.UpdateClusterChannelProvisionerStatus(ctx, r.client, newProvisioner); updateStatusErr != nil { + r.logger.Info("error updating ClusterChannelProvisioner Status", zap.Error(updateStatusErr)) + return reconcile.Result{}, updateStatusErr + } + + // Requeue if the resource is not ready: + return reconcile.Result{}, err +} + +func (r *reconciler) reconcile(provisioner *v1alpha1.ClusterChannelProvisioner) error { + // See if the provisioner has been deleted + accessor, err := meta.Accessor(provisioner) + if err != nil { + r.logger.Info("failed to get metadata", zap.Error(err)) + return err + } + deletionTimestamp := accessor.GetDeletionTimestamp() + if deletionTimestamp != nil { + r.logger.Info(fmt.Sprintf("DeletionTimestamp: %v", deletionTimestamp)) + return nil + } + + provisioner.Status.InitializeConditions() + // Update Status as Ready + provisioner.Status.MarkReady() + + return nil +} diff --git a/pkg/provisioners/kafka/controller/reconcile_test.go b/pkg/provisioners/kafka/controller/reconcile_test.go new file mode 100644 index 00000000000..e7ab3eea676 --- /dev/null +++ b/pkg/provisioners/kafka/controller/reconcile_test.go @@ -0,0 +1,188 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Veroute.on 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 controller + +import ( + "context" + "fmt" + "testing" + + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + controllertesting "github.com/knative/eventing/pkg/controller/testing" + "github.com/knative/eventing/pkg/provisioners" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" + "k8s.io/client-go/tools/record" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/reconcile" +) + +const ( + clusterChannelProvisionerName = "kafka-channel" + testNS = "" +) + +func init() { + // Add types to scheme + eventingv1alpha1.AddToScheme(scheme.Scheme) +} + +var ClusterChannelProvisionerConditionReady = duckv1alpha1.Condition{ + Type: eventingv1alpha1.ClusterChannelProvisionerConditionReady, + Status: corev1.ConditionTrue, +} + +var mockFetchError = controllertesting.Mocks{ + MockGets: []controllertesting.MockGet{ + func(innerClient client.Client, ctx context.Context, key client.ObjectKey, obj runtime.Object) (controllertesting.MockHandled, error) { + if _, ok := obj.(*eventingv1alpha1.ClusterChannelProvisioner); ok { + err := fmt.Errorf("error fetching") + return controllertesting.Handled, err + } + return controllertesting.Unhandled, nil + }, + }, +} + +var testCases = []controllertesting.TestCase{ + { + Name: "new channel clusterChannelProvisioner: adds status", + InitialState: []runtime.Object{ + GetNewChannelClusterChannelProvisioner(clusterChannelProvisionerName), + }, + ReconcileKey: fmt.Sprintf("%s/%s", testNS, clusterChannelProvisionerName), + WantResult: reconcile.Result{}, + WantPresent: []runtime.Object{ + GetNewChannelClusterChannelProvisionerReady(clusterChannelProvisionerName), + }, + IgnoreTimes: true, + }, + { + Name: "reconciles only associated provisioner", + InitialState: []runtime.Object{ + GetNewChannelClusterChannelProvisioner("not-default-provisioner"), + }, + ReconcileKey: fmt.Sprintf("%s/%s", testNS, "not-default-provisioner"), + WantResult: reconcile.Result{}, + WantPresent: []runtime.Object{ + GetNewChannelClusterChannelProvisioner("not-default-provisioner"), + }, + }, + { + Name: "clusterChannelProvisioner not found", + InitialState: []runtime.Object{}, + ReconcileKey: fmt.Sprintf("%s/%s", testNS, clusterChannelProvisionerName), + WantResult: reconcile.Result{}, + WantPresent: []runtime.Object{}, + }, + { + Name: "error fetching clusterChannelProvisioner", + InitialState: []runtime.Object{ + GetNewChannelClusterChannelProvisioner(clusterChannelProvisionerName), + }, + Mocks: mockFetchError, + ReconcileKey: fmt.Sprintf("%s/%s", testNS, clusterChannelProvisionerName), + WantErrMsg: "error fetching", + WantPresent: []runtime.Object{ + GetNewChannelClusterChannelProvisioner(clusterChannelProvisionerName), + }, + }, + { + Name: "deleted clusterChannelProvisioner", + InitialState: []runtime.Object{ + GetNewChannelClusterChannelProvisionerDeleted(clusterChannelProvisionerName), + }, + ReconcileKey: fmt.Sprintf("%s/%s", testNS, clusterChannelProvisionerName), + WantResult: reconcile.Result{}, + WantPresent: []runtime.Object{ + GetNewChannelClusterChannelProvisionerDeleted(clusterChannelProvisionerName), + }, + }, +} + +func TestAllCases(t *testing.T) { + recorder := record.NewBroadcaster().NewRecorder(scheme.Scheme, corev1.EventSource{Component: controllerAgentName}) + + for _, tc := range testCases { + c := tc.GetClient() + logger := provisioners.NewProvisionerLoggerFromConfig(provisioners.NewLoggingConfig()) + r := &reconciler{ + client: c, + recorder: recorder, + logger: logger.Desugar(), + config: getControllerConfig(), + } + t.Logf("Running test %s", tc.Name) + t.Run(tc.Name, tc.Runner(t, r, c)) + } +} + +func GetNewChannelClusterChannelProvisioner(name string) *eventingv1alpha1.ClusterChannelProvisioner { + return getNewClusterChannelProvisioner(name, "Channel") +} + +func getNewClusterChannelProvisioner(name string, reconcileKind string) *eventingv1alpha1.ClusterChannelProvisioner { + clusterChannelProvisioner := &eventingv1alpha1.ClusterChannelProvisioner{ + TypeMeta: ClusterProvisonerType(), + ObjectMeta: om(testNS, name), + Spec: eventingv1alpha1.ClusterChannelProvisionerSpec{}, + } + // selflink is not filled in when we create the object, so clear it + clusterChannelProvisioner.ObjectMeta.SelfLink = "" + return clusterChannelProvisioner +} + +func GetNewChannelClusterChannelProvisionerReady(name string) *eventingv1alpha1.ClusterChannelProvisioner { + c := GetNewChannelClusterChannelProvisioner(name) + c.Status = eventingv1alpha1.ClusterChannelProvisionerStatus{ + Conditions: []duckv1alpha1.Condition{ + ClusterChannelProvisionerConditionReady, + }, + } + return c +} + +func GetNewChannelClusterChannelProvisionerDeleted(name string) *eventingv1alpha1.ClusterChannelProvisioner { + c := GetNewChannelClusterChannelProvisioner(name) + deletedTime := metav1.Now().Rfc3339Copy() + c.DeletionTimestamp = &deletedTime + return c +} + +func ClusterProvisonerType() metav1.TypeMeta { + return metav1.TypeMeta{ + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "ClusterChannelProvisioner", + } +} + +func om(namespace, name string) metav1.ObjectMeta { + return metav1.ObjectMeta{ + Namespace: namespace, + Name: name, + SelfLink: fmt.Sprintf("/apis/eventing/v1alpha1/namespaces/%s/object/%s", namespace, name), + } +} + +func getControllerConfig() *KafkaProvisionerConfig { + return &KafkaProvisionerConfig{ + Brokers: []string{"test-broker"}, + } +} diff --git a/pkg/provisioners/kafka/controller/types.go b/pkg/provisioners/kafka/controller/types.go new file mode 100644 index 00000000000..46a85cea400 --- /dev/null +++ b/pkg/provisioners/kafka/controller/types.go @@ -0,0 +1,5 @@ +package controller + +type KafkaProvisionerConfig struct { + Brokers []string +} diff --git a/pkg/provisioners/kafka/main.go b/pkg/provisioners/kafka/main.go new file mode 100644 index 00000000000..65b97003b68 --- /dev/null +++ b/pkg/provisioners/kafka/main.go @@ -0,0 +1,103 @@ +package main + +import ( + "flag" + "fmt" + "os" + "strings" + + "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "github.com/knative/eventing/pkg/provisioners" + provisionerController "github.com/knative/eventing/pkg/provisioners/kafka/controller" + "github.com/knative/eventing/pkg/provisioners/kafka/controller/channel" + "github.com/knative/pkg/configmap" + "go.uber.org/zap" + "k8s.io/apimachinery/pkg/runtime" + _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" + "sigs.k8s.io/controller-runtime/pkg/client/config" + "sigs.k8s.io/controller-runtime/pkg/controller" + "sigs.k8s.io/controller-runtime/pkg/manager" + logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" + "sigs.k8s.io/controller-runtime/pkg/runtime/signals" +) + +const ( + BrokerConfigMapKey = "bootstrap_servers" +) + +// SchemeFunc adds types to a Scheme. +type SchemeFunc func(*runtime.Scheme) error + +// ProvideFunc adds a controller to a Manager. +type ProvideFunc func(mgr manager.Manager, config *provisionerController.KafkaProvisionerConfig, logger *zap.Logger) (controller.Controller, error) + +func main() { + flag.Parse() + logf.SetLogger(logf.ZapLogger(false)) + + logger := provisioners.NewProvisionerLoggerFromConfig(provisioners.NewLoggingConfig()) + defer logger.Sync() + + // Setup a Manager + mgr, err := manager.New(config.GetConfigOrDie(), manager.Options{}) + if err != nil { + logger.Error(err, "unable to run controller manager") + os.Exit(1) + } + + // Add custom types to this array to get them into the manager's scheme. + schemeFuncs := []SchemeFunc{ + v1alpha1.AddToScheme, + } + for _, schemeFunc := range schemeFuncs { + schemeFunc(mgr.GetScheme()) + } + + // Add each controller's ProvideController func to this list to have the + // manager run it. + providers := []ProvideFunc{ + provisionerController.ProvideController, + channel.ProvideController, + } + + // TODO the underlying config map needs to be watched and the config should be reloaded if there is a change. + provisionerConfig, err := getProvisionerConfig() + + if err != nil { + logger.Error(err, "unable to run controller manager") + os.Exit(1) + } + + for _, provider := range providers { + if _, err := provider(mgr, provisionerConfig, logger.Desugar()); err != nil { + logger.Error(err, "unable to run controller manager") + os.Exit(1) + } + } + + mgr.Start(signals.SetupSignalHandler()) +} + +// getProvisionerConfig returns the details of the associated Provisioner/ClusterChannelProvisioner object +func getProvisionerConfig() (*provisionerController.KafkaProvisionerConfig, error) { + configMap, err := configmap.Load("/etc/config-provisioner") + if err != nil { + return nil, fmt.Errorf("error loading provisioner configuration: %s", err) + } + + if len(configMap) == 0 { + return nil, fmt.Errorf("missing provisioner configuration") + } + + config := &provisionerController.KafkaProvisionerConfig{} + + if value, ok := configMap[BrokerConfigMapKey]; ok { + bootstrapServers := strings.Split(value, ",") + if len(bootstrapServers) != 0 { + config.Brokers = bootstrapServers + return config, nil + } + } + + return nil, fmt.Errorf("missing key %s in provisioner configuration", BrokerConfigMapKey) +} diff --git a/pkg/provisioners/logging.go b/pkg/provisioners/logging.go new file mode 100644 index 00000000000..46dc9830151 --- /dev/null +++ b/pkg/provisioners/logging.go @@ -0,0 +1,63 @@ +/* + * Copyright 2018 The Knative Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package provisioners + +import ( + "github.com/knative/pkg/logging" + "go.uber.org/zap" + "go.uber.org/zap/zapcore" +) + +const ( + provisionerLoggingComponent = "provisioner" +) + +// NewLoggingConfig creates a static logging configuration appropriate for a +// provisioner. All logging levels are set to Info. +func NewLoggingConfig() *logging.Config { + lc := &logging.Config{} + lc.LoggingConfig = `{ + "level": "info", + "development": false, + "outputPaths": ["stdout"], + "errorOutputPaths": ["stderr"], + "encoding": "json", + "encoderConfig": { + "timeKey": "ts", + "levelKey": "level", + "nameKey": "logger", + "callerKey": "caller", + "messageKey": "msg", + "stacktraceKey": "stacktrace", + "lineEnding": "", + "levelEncoder": "", + "timeEncoder": "iso8601", + "durationEncoder": "", + "callerEncoder": "" + } + }` + lc.LoggingLevel = make(map[string]zapcore.Level) + lc.LoggingLevel[provisionerLoggingComponent] = zapcore.InfoLevel + return lc +} + +// NewProvisionerLoggerFromConfig creates a new zap logger for the provisioner component based +// on the provided configuration +func NewProvisionerLoggerFromConfig(config *logging.Config) *zap.SugaredLogger { + logger, _ := logging.NewLoggerFromConfig(config, provisionerLoggingComponent) + return logger +} diff --git a/third_party/VENDOR-LICENSE b/third_party/VENDOR-LICENSE index a6e32d48afd..a4b76f23095 100644 --- a/third_party/VENDOR-LICENSE +++ b/third_party/VENDOR-LICENSE @@ -207,6 +207,32 @@ Import: github.com/knative/eventing/vendor/cloud.google.com/go +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/Shopify/sarama + +Copyright (c) 2013 Shopify + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/beorn7/perks @@ -254,6 +280,87 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/eapache/go-resiliency + +The MIT License (MIT) + +Copyright (c) 2014 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/eapache/go-xerial-snappy + +The MIT License (MIT) + +Copyright (c) 2016 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + + +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/eapache/queue + +The MIT License (MIT) + +Copyright (c) 2014 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/evanphx/json-patch @@ -1276,6 +1383,39 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/golang/snappy + +Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/google/btree @@ -3544,6 +3684,40 @@ THE SOFTWARE. +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/pierrec/lz4 + +Copyright (c) 2015, Pierre Curto +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of xxHash nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/prometheus/client_golang @@ -4372,6 +4546,41 @@ Import: github.com/knative/eventing/vendor/github.com/prometheus/procfs +=========================================================== +Import: github.com/knative/eventing/vendor/github.com/rcrowley/go-metrics + +Copyright 2012 Richard Crowley. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +THIS SOFTWARE IS PROVIDED BY RICHARD CROWLEY ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL RICHARD CROWLEY OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation +are those of the authors and should not be interpreted as representing +official policies, either expressed or implied, of Richard Crowley. + + + =========================================================== Import: github.com/knative/eventing/vendor/github.com/spf13/pflag diff --git a/vendor/github.com/Shopify/sarama/LICENSE b/vendor/github.com/Shopify/sarama/LICENSE new file mode 100644 index 00000000000..d2bf4352f4c --- /dev/null +++ b/vendor/github.com/Shopify/sarama/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2013 Shopify + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/Shopify/sarama/acl_bindings.go b/vendor/github.com/Shopify/sarama/acl_bindings.go new file mode 100644 index 00000000000..51517359abc --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_bindings.go @@ -0,0 +1,119 @@ +package sarama + +type Resource struct { + ResourceType AclResourceType + ResourceName string +} + +func (r *Resource) encode(pe packetEncoder) error { + pe.putInt8(int8(r.ResourceType)) + + if err := pe.putString(r.ResourceName); err != nil { + return err + } + + return nil +} + +func (r *Resource) decode(pd packetDecoder, version int16) (err error) { + resourceType, err := pd.getInt8() + if err != nil { + return err + } + r.ResourceType = AclResourceType(resourceType) + + if r.ResourceName, err = pd.getString(); err != nil { + return err + } + + return nil +} + +type Acl struct { + Principal string + Host string + Operation AclOperation + PermissionType AclPermissionType +} + +func (a *Acl) encode(pe packetEncoder) error { + if err := pe.putString(a.Principal); err != nil { + return err + } + + if err := pe.putString(a.Host); err != nil { + return err + } + + pe.putInt8(int8(a.Operation)) + pe.putInt8(int8(a.PermissionType)) + + return nil +} + +func (a *Acl) decode(pd packetDecoder, version int16) (err error) { + if a.Principal, err = pd.getString(); err != nil { + return err + } + + if a.Host, err = pd.getString(); err != nil { + return err + } + + operation, err := pd.getInt8() + if err != nil { + return err + } + a.Operation = AclOperation(operation) + + permissionType, err := pd.getInt8() + if err != nil { + return err + } + a.PermissionType = AclPermissionType(permissionType) + + return nil +} + +type ResourceAcls struct { + Resource + Acls []*Acl +} + +func (r *ResourceAcls) encode(pe packetEncoder) error { + if err := r.Resource.encode(pe); err != nil { + return err + } + + if err := pe.putArrayLength(len(r.Acls)); err != nil { + return err + } + for _, acl := range r.Acls { + if err := acl.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (r *ResourceAcls) decode(pd packetDecoder, version int16) error { + if err := r.Resource.decode(pd, version); err != nil { + return err + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Acls = make([]*Acl, n) + for i := 0; i < n; i++ { + r.Acls[i] = new(Acl) + if err := r.Acls[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/acl_create_request.go b/vendor/github.com/Shopify/sarama/acl_create_request.go new file mode 100644 index 00000000000..0b6ecbec3e1 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_create_request.go @@ -0,0 +1,76 @@ +package sarama + +type CreateAclsRequest struct { + AclCreations []*AclCreation +} + +func (c *CreateAclsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(c.AclCreations)); err != nil { + return err + } + + for _, aclCreation := range c.AclCreations { + if err := aclCreation.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (c *CreateAclsRequest) decode(pd packetDecoder, version int16) (err error) { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + c.AclCreations = make([]*AclCreation, n) + + for i := 0; i < n; i++ { + c.AclCreations[i] = new(AclCreation) + if err := c.AclCreations[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (d *CreateAclsRequest) key() int16 { + return 30 +} + +func (d *CreateAclsRequest) version() int16 { + return 0 +} + +func (d *CreateAclsRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +type AclCreation struct { + Resource + Acl +} + +func (a *AclCreation) encode(pe packetEncoder) error { + if err := a.Resource.encode(pe); err != nil { + return err + } + if err := a.Acl.encode(pe); err != nil { + return err + } + + return nil +} + +func (a *AclCreation) decode(pd packetDecoder, version int16) (err error) { + if err := a.Resource.decode(pd, version); err != nil { + return err + } + if err := a.Acl.decode(pd, version); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/acl_create_response.go b/vendor/github.com/Shopify/sarama/acl_create_response.go new file mode 100644 index 00000000000..8a56f357354 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_create_response.go @@ -0,0 +1,88 @@ +package sarama + +import "time" + +type CreateAclsResponse struct { + ThrottleTime time.Duration + AclCreationResponses []*AclCreationResponse +} + +func (c *CreateAclsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(c.ThrottleTime / time.Millisecond)) + + if err := pe.putArrayLength(len(c.AclCreationResponses)); err != nil { + return err + } + + for _, aclCreationResponse := range c.AclCreationResponses { + if err := aclCreationResponse.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (c *CreateAclsResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + c.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + c.AclCreationResponses = make([]*AclCreationResponse, n) + for i := 0; i < n; i++ { + c.AclCreationResponses[i] = new(AclCreationResponse) + if err := c.AclCreationResponses[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (d *CreateAclsResponse) key() int16 { + return 30 +} + +func (d *CreateAclsResponse) version() int16 { + return 0 +} + +func (d *CreateAclsResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +type AclCreationResponse struct { + Err KError + ErrMsg *string +} + +func (a *AclCreationResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(a.Err)) + + if err := pe.putNullableString(a.ErrMsg); err != nil { + return err + } + + return nil +} + +func (a *AclCreationResponse) decode(pd packetDecoder, version int16) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + a.Err = KError(kerr) + + if a.ErrMsg, err = pd.getNullableString(); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/acl_delete_request.go b/vendor/github.com/Shopify/sarama/acl_delete_request.go new file mode 100644 index 00000000000..4133dceab71 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_delete_request.go @@ -0,0 +1,48 @@ +package sarama + +type DeleteAclsRequest struct { + Filters []*AclFilter +} + +func (d *DeleteAclsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(d.Filters)); err != nil { + return err + } + + for _, filter := range d.Filters { + if err := filter.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (d *DeleteAclsRequest) decode(pd packetDecoder, version int16) (err error) { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + d.Filters = make([]*AclFilter, n) + for i := 0; i < n; i++ { + d.Filters[i] = new(AclFilter) + if err := d.Filters[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (d *DeleteAclsRequest) key() int16 { + return 31 +} + +func (d *DeleteAclsRequest) version() int16 { + return 0 +} + +func (d *DeleteAclsRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/acl_delete_response.go b/vendor/github.com/Shopify/sarama/acl_delete_response.go new file mode 100644 index 00000000000..b5e1c45eb5d --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_delete_response.go @@ -0,0 +1,155 @@ +package sarama + +import "time" + +type DeleteAclsResponse struct { + ThrottleTime time.Duration + FilterResponses []*FilterResponse +} + +func (a *DeleteAclsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(a.ThrottleTime / time.Millisecond)) + + if err := pe.putArrayLength(len(a.FilterResponses)); err != nil { + return err + } + + for _, filterResponse := range a.FilterResponses { + if err := filterResponse.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (a *DeleteAclsResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + a.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + a.FilterResponses = make([]*FilterResponse, n) + + for i := 0; i < n; i++ { + a.FilterResponses[i] = new(FilterResponse) + if err := a.FilterResponses[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (d *DeleteAclsResponse) key() int16 { + return 31 +} + +func (d *DeleteAclsResponse) version() int16 { + return 0 +} + +func (d *DeleteAclsResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +type FilterResponse struct { + Err KError + ErrMsg *string + MatchingAcls []*MatchingAcl +} + +func (f *FilterResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(f.Err)) + if err := pe.putNullableString(f.ErrMsg); err != nil { + return err + } + + if err := pe.putArrayLength(len(f.MatchingAcls)); err != nil { + return err + } + for _, matchingAcl := range f.MatchingAcls { + if err := matchingAcl.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (f *FilterResponse) decode(pd packetDecoder, version int16) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + f.Err = KError(kerr) + + if f.ErrMsg, err = pd.getNullableString(); err != nil { + return err + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + f.MatchingAcls = make([]*MatchingAcl, n) + for i := 0; i < n; i++ { + f.MatchingAcls[i] = new(MatchingAcl) + if err := f.MatchingAcls[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +type MatchingAcl struct { + Err KError + ErrMsg *string + Resource + Acl +} + +func (m *MatchingAcl) encode(pe packetEncoder) error { + pe.putInt16(int16(m.Err)) + if err := pe.putNullableString(m.ErrMsg); err != nil { + return err + } + + if err := m.Resource.encode(pe); err != nil { + return err + } + + if err := m.Acl.encode(pe); err != nil { + return err + } + + return nil +} + +func (m *MatchingAcl) decode(pd packetDecoder, version int16) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + m.Err = KError(kerr) + + if m.ErrMsg, err = pd.getNullableString(); err != nil { + return err + } + + if err := m.Resource.decode(pd, version); err != nil { + return err + } + + if err := m.Acl.decode(pd, version); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/acl_describe_request.go b/vendor/github.com/Shopify/sarama/acl_describe_request.go new file mode 100644 index 00000000000..02a5a1f0e22 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_describe_request.go @@ -0,0 +1,25 @@ +package sarama + +type DescribeAclsRequest struct { + AclFilter +} + +func (d *DescribeAclsRequest) encode(pe packetEncoder) error { + return d.AclFilter.encode(pe) +} + +func (d *DescribeAclsRequest) decode(pd packetDecoder, version int16) (err error) { + return d.AclFilter.decode(pd, version) +} + +func (d *DescribeAclsRequest) key() int16 { + return 29 +} + +func (d *DescribeAclsRequest) version() int16 { + return 0 +} + +func (d *DescribeAclsRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/acl_describe_response.go b/vendor/github.com/Shopify/sarama/acl_describe_response.go new file mode 100644 index 00000000000..5bc9497f4c5 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_describe_response.go @@ -0,0 +1,80 @@ +package sarama + +import "time" + +type DescribeAclsResponse struct { + ThrottleTime time.Duration + Err KError + ErrMsg *string + ResourceAcls []*ResourceAcls +} + +func (d *DescribeAclsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(d.ThrottleTime / time.Millisecond)) + pe.putInt16(int16(d.Err)) + + if err := pe.putNullableString(d.ErrMsg); err != nil { + return err + } + + if err := pe.putArrayLength(len(d.ResourceAcls)); err != nil { + return err + } + + for _, resourceAcl := range d.ResourceAcls { + if err := resourceAcl.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (d *DescribeAclsResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + d.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + kerr, err := pd.getInt16() + if err != nil { + return err + } + d.Err = KError(kerr) + + errmsg, err := pd.getString() + if err != nil { + return err + } + if errmsg != "" { + d.ErrMsg = &errmsg + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + d.ResourceAcls = make([]*ResourceAcls, n) + + for i := 0; i < n; i++ { + d.ResourceAcls[i] = new(ResourceAcls) + if err := d.ResourceAcls[i].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (d *DescribeAclsResponse) key() int16 { + return 29 +} + +func (d *DescribeAclsResponse) version() int16 { + return 0 +} + +func (d *DescribeAclsResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/acl_filter.go b/vendor/github.com/Shopify/sarama/acl_filter.go new file mode 100644 index 00000000000..97063542198 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_filter.go @@ -0,0 +1,61 @@ +package sarama + +type AclFilter struct { + ResourceType AclResourceType + ResourceName *string + Principal *string + Host *string + Operation AclOperation + PermissionType AclPermissionType +} + +func (a *AclFilter) encode(pe packetEncoder) error { + pe.putInt8(int8(a.ResourceType)) + if err := pe.putNullableString(a.ResourceName); err != nil { + return err + } + if err := pe.putNullableString(a.Principal); err != nil { + return err + } + if err := pe.putNullableString(a.Host); err != nil { + return err + } + pe.putInt8(int8(a.Operation)) + pe.putInt8(int8(a.PermissionType)) + + return nil +} + +func (a *AclFilter) decode(pd packetDecoder, version int16) (err error) { + resourceType, err := pd.getInt8() + if err != nil { + return err + } + a.ResourceType = AclResourceType(resourceType) + + if a.ResourceName, err = pd.getNullableString(); err != nil { + return err + } + + if a.Principal, err = pd.getNullableString(); err != nil { + return err + } + + if a.Host, err = pd.getNullableString(); err != nil { + return err + } + + operation, err := pd.getInt8() + if err != nil { + return err + } + a.Operation = AclOperation(operation) + + permissionType, err := pd.getInt8() + if err != nil { + return err + } + a.PermissionType = AclPermissionType(permissionType) + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/acl_types.go b/vendor/github.com/Shopify/sarama/acl_types.go new file mode 100644 index 00000000000..19da6f2f451 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/acl_types.go @@ -0,0 +1,42 @@ +package sarama + +type AclOperation int + +// ref: https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/acl/AclOperation.java +const ( + AclOperationUnknown AclOperation = 0 + AclOperationAny AclOperation = 1 + AclOperationAll AclOperation = 2 + AclOperationRead AclOperation = 3 + AclOperationWrite AclOperation = 4 + AclOperationCreate AclOperation = 5 + AclOperationDelete AclOperation = 6 + AclOperationAlter AclOperation = 7 + AclOperationDescribe AclOperation = 8 + AclOperationClusterAction AclOperation = 9 + AclOperationDescribeConfigs AclOperation = 10 + AclOperationAlterConfigs AclOperation = 11 + AclOperationIdempotentWrite AclOperation = 12 +) + +type AclPermissionType int + +// ref: https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/acl/AclPermissionType.java +const ( + AclPermissionUnknown AclPermissionType = 0 + AclPermissionAny AclPermissionType = 1 + AclPermissionDeny AclPermissionType = 2 + AclPermissionAllow AclPermissionType = 3 +) + +type AclResourceType int + +// ref: https://github.com/apache/kafka/blob/trunk/clients/src/main/java/org/apache/kafka/common/resource/ResourceType.java +const ( + AclResourceUnknown AclResourceType = 0 + AclResourceAny AclResourceType = 1 + AclResourceTopic AclResourceType = 2 + AclResourceGroup AclResourceType = 3 + AclResourceCluster AclResourceType = 4 + AclResourceTransactionalID AclResourceType = 5 +) diff --git a/vendor/github.com/Shopify/sarama/add_offsets_to_txn_request.go b/vendor/github.com/Shopify/sarama/add_offsets_to_txn_request.go new file mode 100644 index 00000000000..6da166c634b --- /dev/null +++ b/vendor/github.com/Shopify/sarama/add_offsets_to_txn_request.go @@ -0,0 +1,52 @@ +package sarama + +type AddOffsetsToTxnRequest struct { + TransactionalID string + ProducerID int64 + ProducerEpoch int16 + GroupID string +} + +func (a *AddOffsetsToTxnRequest) encode(pe packetEncoder) error { + if err := pe.putString(a.TransactionalID); err != nil { + return err + } + + pe.putInt64(a.ProducerID) + + pe.putInt16(a.ProducerEpoch) + + if err := pe.putString(a.GroupID); err != nil { + return err + } + + return nil +} + +func (a *AddOffsetsToTxnRequest) decode(pd packetDecoder, version int16) (err error) { + if a.TransactionalID, err = pd.getString(); err != nil { + return err + } + if a.ProducerID, err = pd.getInt64(); err != nil { + return err + } + if a.ProducerEpoch, err = pd.getInt16(); err != nil { + return err + } + if a.GroupID, err = pd.getString(); err != nil { + return err + } + return nil +} + +func (a *AddOffsetsToTxnRequest) key() int16 { + return 25 +} + +func (a *AddOffsetsToTxnRequest) version() int16 { + return 0 +} + +func (a *AddOffsetsToTxnRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/add_offsets_to_txn_response.go b/vendor/github.com/Shopify/sarama/add_offsets_to_txn_response.go new file mode 100644 index 00000000000..3a46151a050 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/add_offsets_to_txn_response.go @@ -0,0 +1,44 @@ +package sarama + +import ( + "time" +) + +type AddOffsetsToTxnResponse struct { + ThrottleTime time.Duration + Err KError +} + +func (a *AddOffsetsToTxnResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(a.ThrottleTime / time.Millisecond)) + pe.putInt16(int16(a.Err)) + return nil +} + +func (a *AddOffsetsToTxnResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + a.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + kerr, err := pd.getInt16() + if err != nil { + return err + } + a.Err = KError(kerr) + + return nil +} + +func (a *AddOffsetsToTxnResponse) key() int16 { + return 25 +} + +func (a *AddOffsetsToTxnResponse) version() int16 { + return 0 +} + +func (a *AddOffsetsToTxnResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/add_partitions_to_txn_request.go b/vendor/github.com/Shopify/sarama/add_partitions_to_txn_request.go new file mode 100644 index 00000000000..a8a59225e4d --- /dev/null +++ b/vendor/github.com/Shopify/sarama/add_partitions_to_txn_request.go @@ -0,0 +1,76 @@ +package sarama + +type AddPartitionsToTxnRequest struct { + TransactionalID string + ProducerID int64 + ProducerEpoch int16 + TopicPartitions map[string][]int32 +} + +func (a *AddPartitionsToTxnRequest) encode(pe packetEncoder) error { + if err := pe.putString(a.TransactionalID); err != nil { + return err + } + pe.putInt64(a.ProducerID) + pe.putInt16(a.ProducerEpoch) + + if err := pe.putArrayLength(len(a.TopicPartitions)); err != nil { + return err + } + for topic, partitions := range a.TopicPartitions { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putInt32Array(partitions); err != nil { + return err + } + } + + return nil +} + +func (a *AddPartitionsToTxnRequest) decode(pd packetDecoder, version int16) (err error) { + if a.TransactionalID, err = pd.getString(); err != nil { + return err + } + if a.ProducerID, err = pd.getInt64(); err != nil { + return err + } + if a.ProducerEpoch, err = pd.getInt16(); err != nil { + return err + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + a.TopicPartitions = make(map[string][]int32) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + + partitions, err := pd.getInt32Array() + if err != nil { + return err + } + + a.TopicPartitions[topic] = partitions + } + + return nil +} + +func (a *AddPartitionsToTxnRequest) key() int16 { + return 24 +} + +func (a *AddPartitionsToTxnRequest) version() int16 { + return 0 +} + +func (a *AddPartitionsToTxnRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/add_partitions_to_txn_response.go b/vendor/github.com/Shopify/sarama/add_partitions_to_txn_response.go new file mode 100644 index 00000000000..581c556c5ce --- /dev/null +++ b/vendor/github.com/Shopify/sarama/add_partitions_to_txn_response.go @@ -0,0 +1,108 @@ +package sarama + +import ( + "time" +) + +type AddPartitionsToTxnResponse struct { + ThrottleTime time.Duration + Errors map[string][]*PartitionError +} + +func (a *AddPartitionsToTxnResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(a.ThrottleTime / time.Millisecond)) + if err := pe.putArrayLength(len(a.Errors)); err != nil { + return err + } + + for topic, e := range a.Errors { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putArrayLength(len(e)); err != nil { + return err + } + for _, partitionError := range e { + if err := partitionError.encode(pe); err != nil { + return err + } + } + } + + return nil +} + +func (a *AddPartitionsToTxnResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + a.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + a.Errors = make(map[string][]*PartitionError) + + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + + m, err := pd.getArrayLength() + if err != nil { + return err + } + + a.Errors[topic] = make([]*PartitionError, m) + + for j := 0; j < m; j++ { + a.Errors[topic][j] = new(PartitionError) + if err := a.Errors[topic][j].decode(pd, version); err != nil { + return err + } + } + } + + return nil +} + +func (a *AddPartitionsToTxnResponse) key() int16 { + return 24 +} + +func (a *AddPartitionsToTxnResponse) version() int16 { + return 0 +} + +func (a *AddPartitionsToTxnResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +type PartitionError struct { + Partition int32 + Err KError +} + +func (p *PartitionError) encode(pe packetEncoder) error { + pe.putInt32(p.Partition) + pe.putInt16(int16(p.Err)) + return nil +} + +func (p *PartitionError) decode(pd packetDecoder, version int16) (err error) { + if p.Partition, err = pd.getInt32(); err != nil { + return err + } + + kerr, err := pd.getInt16() + if err != nil { + return err + } + p.Err = KError(kerr) + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/admin.go b/vendor/github.com/Shopify/sarama/admin.go new file mode 100644 index 00000000000..52725758d21 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/admin.go @@ -0,0 +1,382 @@ +package sarama + +import "errors" + +// ClusterAdmin is the administrative client for Kafka, which supports managing and inspecting topics, +// brokers, configurations and ACLs. The minimum broker version required is 0.10.0.0. +// Methods with stricter requirements will specify the minimum broker version required. +// You MUST call Close() on a client to avoid leaks +type ClusterAdmin interface { + // Creates a new topic. This operation is supported by brokers with version 0.10.1.0 or higher. + // It may take several seconds after CreateTopic returns success for all the brokers + // to become aware that the topic has been created. During this time, listTopics + // may not return information about the new topic.The validateOnly option is supported from version 0.10.2.0. + CreateTopic(topic string, detail *TopicDetail, validateOnly bool) error + + // Delete a topic. It may take several seconds after the DeleteTopic to returns success + // and for all the brokers to become aware that the topics are gone. + // During this time, listTopics may continue to return information about the deleted topic. + // If delete.topic.enable is false on the brokers, deleteTopic will mark + // the topic for deletion, but not actually delete them. + // This operation is supported by brokers with version 0.10.1.0 or higher. + DeleteTopic(topic string) error + + // Increase the number of partitions of the topics according to the corresponding values. + // If partitions are increased for a topic that has a key, the partition logic or ordering of + // the messages will be affected. It may take several seconds after this method returns + // success for all the brokers to become aware that the partitions have been created. + // During this time, ClusterAdmin#describeTopics may not return information about the + // new partitions. This operation is supported by brokers with version 1.0.0 or higher. + CreatePartitions(topic string, count int32, assignment [][]int32, validateOnly bool) error + + // Delete records whose offset is smaller than the given offset of the corresponding partition. + // This operation is supported by brokers with version 0.11.0.0 or higher. + DeleteRecords(topic string, partitionOffsets map[int32]int64) error + + // Get the configuration for the specified resources. + // The returned configuration includes default values and the Default is true + // can be used to distinguish them from user supplied values. + // Config entries where ReadOnly is true cannot be updated. + // The value of config entries where Sensitive is true is always nil so + // sensitive information is not disclosed. + // This operation is supported by brokers with version 0.11.0.0 or higher. + DescribeConfig(resource ConfigResource) ([]ConfigEntry, error) + + // Update the configuration for the specified resources with the default options. + // This operation is supported by brokers with version 0.11.0.0 or higher. + // The resources with their configs (topic is the only resource type with configs + // that can be updated currently Updates are not transactional so they may succeed + // for some resources while fail for others. The configs for a particular resource are updated automatically. + AlterConfig(resourceType ConfigResourceType, name string, entries map[string]*string, validateOnly bool) error + + // Creates access control lists (ACLs) which are bound to specific resources. + // This operation is not transactional so it may succeed for some ACLs while fail for others. + // If you attempt to add an ACL that duplicates an existing ACL, no error will be raised, but + // no changes will be made. This operation is supported by brokers with version 0.11.0.0 or higher. + CreateACL(resource Resource, acl Acl) error + + // Lists access control lists (ACLs) according to the supplied filter. + // it may take some time for changes made by createAcls or deleteAcls to be reflected in the output of ListAcls + // This operation is supported by brokers with version 0.11.0.0 or higher. + ListAcls(filter AclFilter) ([]ResourceAcls, error) + + // Deletes access control lists (ACLs) according to the supplied filters. + // This operation is not transactional so it may succeed for some ACLs while fail for others. + // This operation is supported by brokers with version 0.11.0.0 or higher. + DeleteACL(filter AclFilter, validateOnly bool) ([]MatchingAcl, error) + + // Close shuts down the admin and closes underlying client. + Close() error +} + +type clusterAdmin struct { + client Client + conf *Config +} + +// NewClusterAdmin creates a new ClusterAdmin using the given broker addresses and configuration. +func NewClusterAdmin(addrs []string, conf *Config) (ClusterAdmin, error) { + client, err := NewClient(addrs, conf) + if err != nil { + return nil, err + } + + //make sure we can retrieve the controller + _, err = client.Controller() + if err != nil { + return nil, err + } + + ca := &clusterAdmin{ + client: client, + conf: client.Config(), + } + return ca, nil +} + +func (ca *clusterAdmin) Close() error { + return ca.client.Close() +} + +func (ca *clusterAdmin) Controller() (*Broker, error) { + return ca.client.Controller() +} + +func (ca *clusterAdmin) CreateTopic(topic string, detail *TopicDetail, validateOnly bool) error { + + if topic == "" { + return ErrInvalidTopic + } + + if detail == nil { + return errors.New("You must specify topic details") + } + + topicDetails := make(map[string]*TopicDetail) + topicDetails[topic] = detail + + request := &CreateTopicsRequest{ + TopicDetails: topicDetails, + ValidateOnly: validateOnly, + Timeout: ca.conf.Admin.Timeout, + } + + if ca.conf.Version.IsAtLeast(V0_11_0_0) { + request.Version = 1 + } + if ca.conf.Version.IsAtLeast(V1_0_0_0) { + request.Version = 2 + } + + b, err := ca.Controller() + if err != nil { + return err + } + + rsp, err := b.CreateTopics(request) + if err != nil { + return err + } + + topicErr, ok := rsp.TopicErrors[topic] + if !ok { + return ErrIncompleteResponse + } + + if topicErr.Err != ErrNoError { + return topicErr.Err + } + + return nil +} + +func (ca *clusterAdmin) DeleteTopic(topic string) error { + + if topic == "" { + return ErrInvalidTopic + } + + request := &DeleteTopicsRequest{ + Topics: []string{topic}, + Timeout: ca.conf.Admin.Timeout, + } + + if ca.conf.Version.IsAtLeast(V0_11_0_0) { + request.Version = 1 + } + + b, err := ca.Controller() + if err != nil { + return err + } + + rsp, err := b.DeleteTopics(request) + if err != nil { + return err + } + + topicErr, ok := rsp.TopicErrorCodes[topic] + if !ok { + return ErrIncompleteResponse + } + + if topicErr != ErrNoError { + return topicErr + } + return nil +} + +func (ca *clusterAdmin) CreatePartitions(topic string, count int32, assignment [][]int32, validateOnly bool) error { + if topic == "" { + return ErrInvalidTopic + } + + topicPartitions := make(map[string]*TopicPartition) + topicPartitions[topic] = &TopicPartition{Count: count, Assignment: assignment} + + request := &CreatePartitionsRequest{ + TopicPartitions: topicPartitions, + Timeout: ca.conf.Admin.Timeout, + } + + b, err := ca.Controller() + if err != nil { + return err + } + + rsp, err := b.CreatePartitions(request) + if err != nil { + return err + } + + topicErr, ok := rsp.TopicPartitionErrors[topic] + if !ok { + return ErrIncompleteResponse + } + + if topicErr.Err != ErrNoError { + return topicErr.Err + } + + return nil +} + +func (ca *clusterAdmin) DeleteRecords(topic string, partitionOffsets map[int32]int64) error { + + if topic == "" { + return ErrInvalidTopic + } + + topics := make(map[string]*DeleteRecordsRequestTopic) + topics[topic] = &DeleteRecordsRequestTopic{PartitionOffsets: partitionOffsets} + request := &DeleteRecordsRequest{ + Topics: topics, + Timeout: ca.conf.Admin.Timeout, + } + + b, err := ca.Controller() + if err != nil { + return err + } + + rsp, err := b.DeleteRecords(request) + if err != nil { + return err + } + + _, ok := rsp.Topics[topic] + if !ok { + return ErrIncompleteResponse + } + + //todo since we are dealing with couple of partitions it would be good if we return slice of errors + //for each partition instead of one error + return nil +} + +func (ca *clusterAdmin) DescribeConfig(resource ConfigResource) ([]ConfigEntry, error) { + + var entries []ConfigEntry + var resources []*ConfigResource + resources = append(resources, &resource) + + request := &DescribeConfigsRequest{ + Resources: resources, + } + + b, err := ca.Controller() + if err != nil { + return nil, err + } + + rsp, err := b.DescribeConfigs(request) + if err != nil { + return nil, err + } + + for _, rspResource := range rsp.Resources { + if rspResource.Name == resource.Name { + if rspResource.ErrorMsg != "" { + return nil, errors.New(rspResource.ErrorMsg) + } + for _, cfgEntry := range rspResource.Configs { + entries = append(entries, *cfgEntry) + } + } + } + return entries, nil +} + +func (ca *clusterAdmin) AlterConfig(resourceType ConfigResourceType, name string, entries map[string]*string, validateOnly bool) error { + + var resources []*AlterConfigsResource + resources = append(resources, &AlterConfigsResource{ + Type: resourceType, + Name: name, + ConfigEntries: entries, + }) + + request := &AlterConfigsRequest{ + Resources: resources, + ValidateOnly: validateOnly, + } + + b, err := ca.Controller() + if err != nil { + return err + } + + rsp, err := b.AlterConfigs(request) + if err != nil { + return err + } + + for _, rspResource := range rsp.Resources { + if rspResource.Name == name { + if rspResource.ErrorMsg != "" { + return errors.New(rspResource.ErrorMsg) + } + } + } + return nil +} + +func (ca *clusterAdmin) CreateACL(resource Resource, acl Acl) error { + var acls []*AclCreation + acls = append(acls, &AclCreation{resource, acl}) + request := &CreateAclsRequest{AclCreations: acls} + + b, err := ca.Controller() + if err != nil { + return err + } + + _, err = b.CreateAcls(request) + return err +} + +func (ca *clusterAdmin) ListAcls(filter AclFilter) ([]ResourceAcls, error) { + + request := &DescribeAclsRequest{AclFilter: filter} + + b, err := ca.Controller() + if err != nil { + return nil, err + } + + rsp, err := b.DescribeAcls(request) + if err != nil { + return nil, err + } + + var lAcls []ResourceAcls + for _, rAcl := range rsp.ResourceAcls { + lAcls = append(lAcls, *rAcl) + } + return lAcls, nil +} + +func (ca *clusterAdmin) DeleteACL(filter AclFilter, validateOnly bool) ([]MatchingAcl, error) { + var filters []*AclFilter + filters = append(filters, &filter) + request := &DeleteAclsRequest{Filters: filters} + + b, err := ca.Controller() + if err != nil { + return nil, err + } + + rsp, err := b.DeleteAcls(request) + if err != nil { + return nil, err + } + + var mAcls []MatchingAcl + for _, fr := range rsp.FilterResponses { + for _, mACL := range fr.MatchingAcls { + mAcls = append(mAcls, *mACL) + } + + } + return mAcls, nil +} diff --git a/vendor/github.com/Shopify/sarama/alter_configs_request.go b/vendor/github.com/Shopify/sarama/alter_configs_request.go new file mode 100644 index 00000000000..48c44ead67a --- /dev/null +++ b/vendor/github.com/Shopify/sarama/alter_configs_request.go @@ -0,0 +1,120 @@ +package sarama + +type AlterConfigsRequest struct { + Resources []*AlterConfigsResource + ValidateOnly bool +} + +type AlterConfigsResource struct { + Type ConfigResourceType + Name string + ConfigEntries map[string]*string +} + +func (acr *AlterConfigsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(acr.Resources)); err != nil { + return err + } + + for _, r := range acr.Resources { + if err := r.encode(pe); err != nil { + return err + } + } + + pe.putBool(acr.ValidateOnly) + return nil +} + +func (acr *AlterConfigsRequest) decode(pd packetDecoder, version int16) error { + resourceCount, err := pd.getArrayLength() + if err != nil { + return err + } + + acr.Resources = make([]*AlterConfigsResource, resourceCount) + for i := range acr.Resources { + r := &AlterConfigsResource{} + err = r.decode(pd, version) + if err != nil { + return err + } + acr.Resources[i] = r + } + + validateOnly, err := pd.getBool() + if err != nil { + return err + } + + acr.ValidateOnly = validateOnly + + return nil +} + +func (ac *AlterConfigsResource) encode(pe packetEncoder) error { + pe.putInt8(int8(ac.Type)) + + if err := pe.putString(ac.Name); err != nil { + return err + } + + if err := pe.putArrayLength(len(ac.ConfigEntries)); err != nil { + return err + } + for configKey, configValue := range ac.ConfigEntries { + if err := pe.putString(configKey); err != nil { + return err + } + if err := pe.putNullableString(configValue); err != nil { + return err + } + } + + return nil +} + +func (ac *AlterConfigsResource) decode(pd packetDecoder, version int16) error { + t, err := pd.getInt8() + if err != nil { + return err + } + ac.Type = ConfigResourceType(t) + + name, err := pd.getString() + if err != nil { + return err + } + ac.Name = name + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + ac.ConfigEntries = make(map[string]*string, n) + for i := 0; i < n; i++ { + configKey, err := pd.getString() + if err != nil { + return err + } + if ac.ConfigEntries[configKey], err = pd.getNullableString(); err != nil { + return err + } + } + } + return err +} + +func (acr *AlterConfigsRequest) key() int16 { + return 33 +} + +func (acr *AlterConfigsRequest) version() int16 { + return 0 +} + +func (acr *AlterConfigsRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/alter_configs_response.go b/vendor/github.com/Shopify/sarama/alter_configs_response.go new file mode 100644 index 00000000000..29b09e1ff84 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/alter_configs_response.go @@ -0,0 +1,95 @@ +package sarama + +import "time" + +type AlterConfigsResponse struct { + ThrottleTime time.Duration + Resources []*AlterConfigsResourceResponse +} + +type AlterConfigsResourceResponse struct { + ErrorCode int16 + ErrorMsg string + Type ConfigResourceType + Name string +} + +func (ct *AlterConfigsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(ct.ThrottleTime / time.Millisecond)) + + if err := pe.putArrayLength(len(ct.Resources)); err != nil { + return err + } + + for i := range ct.Resources { + pe.putInt16(ct.Resources[i].ErrorCode) + err := pe.putString(ct.Resources[i].ErrorMsg) + if err != nil { + return nil + } + pe.putInt8(int8(ct.Resources[i].Type)) + err = pe.putString(ct.Resources[i].Name) + if err != nil { + return nil + } + } + + return nil +} + +func (acr *AlterConfigsResponse) decode(pd packetDecoder, version int16) error { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + acr.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + responseCount, err := pd.getArrayLength() + if err != nil { + return err + } + + acr.Resources = make([]*AlterConfigsResourceResponse, responseCount) + + for i := range acr.Resources { + acr.Resources[i] = new(AlterConfigsResourceResponse) + + errCode, err := pd.getInt16() + if err != nil { + return err + } + acr.Resources[i].ErrorCode = errCode + + e, err := pd.getString() + if err != nil { + return err + } + acr.Resources[i].ErrorMsg = e + + t, err := pd.getInt8() + if err != nil { + return err + } + acr.Resources[i].Type = ConfigResourceType(t) + + name, err := pd.getString() + if err != nil { + return err + } + acr.Resources[i].Name = name + } + + return nil +} + +func (r *AlterConfigsResponse) key() int16 { + return 32 +} + +func (r *AlterConfigsResponse) version() int16 { + return 0 +} + +func (r *AlterConfigsResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/api_versions_request.go b/vendor/github.com/Shopify/sarama/api_versions_request.go new file mode 100644 index 00000000000..ab65f01ccff --- /dev/null +++ b/vendor/github.com/Shopify/sarama/api_versions_request.go @@ -0,0 +1,24 @@ +package sarama + +type ApiVersionsRequest struct { +} + +func (r *ApiVersionsRequest) encode(pe packetEncoder) error { + return nil +} + +func (r *ApiVersionsRequest) decode(pd packetDecoder, version int16) (err error) { + return nil +} + +func (r *ApiVersionsRequest) key() int16 { + return 18 +} + +func (r *ApiVersionsRequest) version() int16 { + return 0 +} + +func (r *ApiVersionsRequest) requiredVersion() KafkaVersion { + return V0_10_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/api_versions_response.go b/vendor/github.com/Shopify/sarama/api_versions_response.go new file mode 100644 index 00000000000..23bc326e15f --- /dev/null +++ b/vendor/github.com/Shopify/sarama/api_versions_response.go @@ -0,0 +1,87 @@ +package sarama + +type ApiVersionsResponseBlock struct { + ApiKey int16 + MinVersion int16 + MaxVersion int16 +} + +func (b *ApiVersionsResponseBlock) encode(pe packetEncoder) error { + pe.putInt16(b.ApiKey) + pe.putInt16(b.MinVersion) + pe.putInt16(b.MaxVersion) + return nil +} + +func (b *ApiVersionsResponseBlock) decode(pd packetDecoder) error { + var err error + + if b.ApiKey, err = pd.getInt16(); err != nil { + return err + } + + if b.MinVersion, err = pd.getInt16(); err != nil { + return err + } + + if b.MaxVersion, err = pd.getInt16(); err != nil { + return err + } + + return nil +} + +type ApiVersionsResponse struct { + Err KError + ApiVersions []*ApiVersionsResponseBlock +} + +func (r *ApiVersionsResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(r.Err)) + if err := pe.putArrayLength(len(r.ApiVersions)); err != nil { + return err + } + for _, apiVersion := range r.ApiVersions { + if err := apiVersion.encode(pe); err != nil { + return err + } + } + return nil +} + +func (r *ApiVersionsResponse) decode(pd packetDecoder, version int16) error { + kerr, err := pd.getInt16() + if err != nil { + return err + } + + r.Err = KError(kerr) + + numBlocks, err := pd.getArrayLength() + if err != nil { + return err + } + + r.ApiVersions = make([]*ApiVersionsResponseBlock, numBlocks) + for i := 0; i < numBlocks; i++ { + block := new(ApiVersionsResponseBlock) + if err := block.decode(pd); err != nil { + return err + } + r.ApiVersions[i] = block + } + + return nil +} + +func (r *ApiVersionsResponse) key() int16 { + return 18 +} + +func (r *ApiVersionsResponse) version() int16 { + return 0 +} + +func (r *ApiVersionsResponse) requiredVersion() KafkaVersion { + return V0_10_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/async_producer.go b/vendor/github.com/Shopify/sarama/async_producer.go new file mode 100644 index 00000000000..89722554092 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/async_producer.go @@ -0,0 +1,932 @@ +package sarama + +import ( + "encoding/binary" + "fmt" + "sync" + "time" + + "github.com/eapache/go-resiliency/breaker" + "github.com/eapache/queue" +) + +// AsyncProducer publishes Kafka messages using a non-blocking API. It routes messages +// to the correct broker for the provided topic-partition, refreshing metadata as appropriate, +// and parses responses for errors. You must read from the Errors() channel or the +// producer will deadlock. You must call Close() or AsyncClose() on a producer to avoid +// leaks: it will not be garbage-collected automatically when it passes out of +// scope. +type AsyncProducer interface { + + // AsyncClose triggers a shutdown of the producer. The shutdown has completed + // when both the Errors and Successes channels have been closed. When calling + // AsyncClose, you *must* continue to read from those channels in order to + // drain the results of any messages in flight. + AsyncClose() + + // Close shuts down the producer and waits for any buffered messages to be + // flushed. You must call this function before a producer object passes out of + // scope, as it may otherwise leak memory. You must call this before calling + // Close on the underlying client. + Close() error + + // Input is the input channel for the user to write messages to that they + // wish to send. + Input() chan<- *ProducerMessage + + // Successes is the success output channel back to the user when Return.Successes is + // enabled. If Return.Successes is true, you MUST read from this channel or the + // Producer will deadlock. It is suggested that you send and read messages + // together in a single select statement. + Successes() <-chan *ProducerMessage + + // Errors is the error output channel back to the user. You MUST read from this + // channel or the Producer will deadlock when the channel is full. Alternatively, + // you can set Producer.Return.Errors in your config to false, which prevents + // errors to be returned. + Errors() <-chan *ProducerError +} + +type asyncProducer struct { + client Client + conf *Config + ownClient bool + + errors chan *ProducerError + input, successes, retries chan *ProducerMessage + inFlight sync.WaitGroup + + brokers map[*Broker]chan<- *ProducerMessage + brokerRefs map[chan<- *ProducerMessage]int + brokerLock sync.Mutex +} + +// NewAsyncProducer creates a new AsyncProducer using the given broker addresses and configuration. +func NewAsyncProducer(addrs []string, conf *Config) (AsyncProducer, error) { + client, err := NewClient(addrs, conf) + if err != nil { + return nil, err + } + + p, err := NewAsyncProducerFromClient(client) + if err != nil { + return nil, err + } + p.(*asyncProducer).ownClient = true + return p, nil +} + +// NewAsyncProducerFromClient creates a new Producer using the given client. It is still +// necessary to call Close() on the underlying client when shutting down this producer. +func NewAsyncProducerFromClient(client Client) (AsyncProducer, error) { + // Check that we are not dealing with a closed Client before processing any other arguments + if client.Closed() { + return nil, ErrClosedClient + } + + p := &asyncProducer{ + client: client, + conf: client.Config(), + errors: make(chan *ProducerError), + input: make(chan *ProducerMessage), + successes: make(chan *ProducerMessage), + retries: make(chan *ProducerMessage), + brokers: make(map[*Broker]chan<- *ProducerMessage), + brokerRefs: make(map[chan<- *ProducerMessage]int), + } + + // launch our singleton dispatchers + go withRecover(p.dispatcher) + go withRecover(p.retryHandler) + + return p, nil +} + +type flagSet int8 + +const ( + syn flagSet = 1 << iota // first message from partitionProducer to brokerProducer + fin // final message from partitionProducer to brokerProducer and back + shutdown // start the shutdown process +) + +// ProducerMessage is the collection of elements passed to the Producer in order to send a message. +type ProducerMessage struct { + Topic string // The Kafka topic for this message. + // The partitioning key for this message. Pre-existing Encoders include + // StringEncoder and ByteEncoder. + Key Encoder + // The actual message to store in Kafka. Pre-existing Encoders include + // StringEncoder and ByteEncoder. + Value Encoder + + // The headers are key-value pairs that are transparently passed + // by Kafka between producers and consumers. + Headers []RecordHeader + + // This field is used to hold arbitrary data you wish to include so it + // will be available when receiving on the Successes and Errors channels. + // Sarama completely ignores this field and is only to be used for + // pass-through data. + Metadata interface{} + + // Below this point are filled in by the producer as the message is processed + + // Offset is the offset of the message stored on the broker. This is only + // guaranteed to be defined if the message was successfully delivered and + // RequiredAcks is not NoResponse. + Offset int64 + // Partition is the partition that the message was sent to. This is only + // guaranteed to be defined if the message was successfully delivered. + Partition int32 + // Timestamp is the timestamp assigned to the message by the broker. This + // is only guaranteed to be defined if the message was successfully + // delivered, RequiredAcks is not NoResponse, and the Kafka broker is at + // least version 0.10.0. + Timestamp time.Time + + retries int + flags flagSet + expectation chan *ProducerError +} + +const producerMessageOverhead = 26 // the metadata overhead of CRC, flags, etc. + +func (m *ProducerMessage) byteSize(version int) int { + var size int + if version >= 2 { + size = maximumRecordOverhead + for _, h := range m.Headers { + size += len(h.Key) + len(h.Value) + 2*binary.MaxVarintLen32 + } + } else { + size = producerMessageOverhead + } + if m.Key != nil { + size += m.Key.Length() + } + if m.Value != nil { + size += m.Value.Length() + } + return size +} + +func (m *ProducerMessage) clear() { + m.flags = 0 + m.retries = 0 +} + +// ProducerError is the type of error generated when the producer fails to deliver a message. +// It contains the original ProducerMessage as well as the actual error value. +type ProducerError struct { + Msg *ProducerMessage + Err error +} + +func (pe ProducerError) Error() string { + return fmt.Sprintf("kafka: Failed to produce message to topic %s: %s", pe.Msg.Topic, pe.Err) +} + +// ProducerErrors is a type that wraps a batch of "ProducerError"s and implements the Error interface. +// It can be returned from the Producer's Close method to avoid the need to manually drain the Errors channel +// when closing a producer. +type ProducerErrors []*ProducerError + +func (pe ProducerErrors) Error() string { + return fmt.Sprintf("kafka: Failed to deliver %d messages.", len(pe)) +} + +func (p *asyncProducer) Errors() <-chan *ProducerError { + return p.errors +} + +func (p *asyncProducer) Successes() <-chan *ProducerMessage { + return p.successes +} + +func (p *asyncProducer) Input() chan<- *ProducerMessage { + return p.input +} + +func (p *asyncProducer) Close() error { + p.AsyncClose() + + if p.conf.Producer.Return.Successes { + go withRecover(func() { + for range p.successes { + } + }) + } + + var errors ProducerErrors + if p.conf.Producer.Return.Errors { + for event := range p.errors { + errors = append(errors, event) + } + } else { + <-p.errors + } + + if len(errors) > 0 { + return errors + } + return nil +} + +func (p *asyncProducer) AsyncClose() { + go withRecover(p.shutdown) +} + +// singleton +// dispatches messages by topic +func (p *asyncProducer) dispatcher() { + handlers := make(map[string]chan<- *ProducerMessage) + shuttingDown := false + + for msg := range p.input { + if msg == nil { + Logger.Println("Something tried to send a nil message, it was ignored.") + continue + } + + if msg.flags&shutdown != 0 { + shuttingDown = true + p.inFlight.Done() + continue + } else if msg.retries == 0 { + if shuttingDown { + // we can't just call returnError here because that decrements the wait group, + // which hasn't been incremented yet for this message, and shouldn't be + pErr := &ProducerError{Msg: msg, Err: ErrShuttingDown} + if p.conf.Producer.Return.Errors { + p.errors <- pErr + } else { + Logger.Println(pErr) + } + continue + } + p.inFlight.Add(1) + } + + version := 1 + if p.conf.Version.IsAtLeast(V0_11_0_0) { + version = 2 + } else if msg.Headers != nil { + p.returnError(msg, ConfigurationError("Producing headers requires Kafka at least v0.11")) + continue + } + if msg.byteSize(version) > p.conf.Producer.MaxMessageBytes { + p.returnError(msg, ErrMessageSizeTooLarge) + continue + } + + handler := handlers[msg.Topic] + if handler == nil { + handler = p.newTopicProducer(msg.Topic) + handlers[msg.Topic] = handler + } + + handler <- msg + } + + for _, handler := range handlers { + close(handler) + } +} + +// one per topic +// partitions messages, then dispatches them by partition +type topicProducer struct { + parent *asyncProducer + topic string + input <-chan *ProducerMessage + + breaker *breaker.Breaker + handlers map[int32]chan<- *ProducerMessage + partitioner Partitioner +} + +func (p *asyncProducer) newTopicProducer(topic string) chan<- *ProducerMessage { + input := make(chan *ProducerMessage, p.conf.ChannelBufferSize) + tp := &topicProducer{ + parent: p, + topic: topic, + input: input, + breaker: breaker.New(3, 1, 10*time.Second), + handlers: make(map[int32]chan<- *ProducerMessage), + partitioner: p.conf.Producer.Partitioner(topic), + } + go withRecover(tp.dispatch) + return input +} + +func (tp *topicProducer) dispatch() { + for msg := range tp.input { + if msg.retries == 0 { + if err := tp.partitionMessage(msg); err != nil { + tp.parent.returnError(msg, err) + continue + } + } + + handler := tp.handlers[msg.Partition] + if handler == nil { + handler = tp.parent.newPartitionProducer(msg.Topic, msg.Partition) + tp.handlers[msg.Partition] = handler + } + + handler <- msg + } + + for _, handler := range tp.handlers { + close(handler) + } +} + +func (tp *topicProducer) partitionMessage(msg *ProducerMessage) error { + var partitions []int32 + + err := tp.breaker.Run(func() (err error) { + var requiresConsistency = false + if ep, ok := tp.partitioner.(DynamicConsistencyPartitioner); ok { + requiresConsistency = ep.MessageRequiresConsistency(msg) + } else { + requiresConsistency = tp.partitioner.RequiresConsistency() + } + + if requiresConsistency { + partitions, err = tp.parent.client.Partitions(msg.Topic) + } else { + partitions, err = tp.parent.client.WritablePartitions(msg.Topic) + } + return + }) + + if err != nil { + return err + } + + numPartitions := int32(len(partitions)) + + if numPartitions == 0 { + return ErrLeaderNotAvailable + } + + choice, err := tp.partitioner.Partition(msg, numPartitions) + + if err != nil { + return err + } else if choice < 0 || choice >= numPartitions { + return ErrInvalidPartition + } + + msg.Partition = partitions[choice] + + return nil +} + +// one per partition per topic +// dispatches messages to the appropriate broker +// also responsible for maintaining message order during retries +type partitionProducer struct { + parent *asyncProducer + topic string + partition int32 + input <-chan *ProducerMessage + + leader *Broker + breaker *breaker.Breaker + output chan<- *ProducerMessage + + // highWatermark tracks the "current" retry level, which is the only one where we actually let messages through, + // all other messages get buffered in retryState[msg.retries].buf to preserve ordering + // retryState[msg.retries].expectChaser simply tracks whether we've seen a fin message for a given level (and + // therefore whether our buffer is complete and safe to flush) + highWatermark int + retryState []partitionRetryState +} + +type partitionRetryState struct { + buf []*ProducerMessage + expectChaser bool +} + +func (p *asyncProducer) newPartitionProducer(topic string, partition int32) chan<- *ProducerMessage { + input := make(chan *ProducerMessage, p.conf.ChannelBufferSize) + pp := &partitionProducer{ + parent: p, + topic: topic, + partition: partition, + input: input, + + breaker: breaker.New(3, 1, 10*time.Second), + retryState: make([]partitionRetryState, p.conf.Producer.Retry.Max+1), + } + go withRecover(pp.dispatch) + return input +} + +func (pp *partitionProducer) dispatch() { + // try to prefetch the leader; if this doesn't work, we'll do a proper call to `updateLeader` + // on the first message + pp.leader, _ = pp.parent.client.Leader(pp.topic, pp.partition) + if pp.leader != nil { + pp.output = pp.parent.getBrokerProducer(pp.leader) + pp.parent.inFlight.Add(1) // we're generating a syn message; track it so we don't shut down while it's still inflight + pp.output <- &ProducerMessage{Topic: pp.topic, Partition: pp.partition, flags: syn} + } + + for msg := range pp.input { + if msg.retries > pp.highWatermark { + // a new, higher, retry level; handle it and then back off + pp.newHighWatermark(msg.retries) + time.Sleep(pp.parent.conf.Producer.Retry.Backoff) + } else if pp.highWatermark > 0 { + // we are retrying something (else highWatermark would be 0) but this message is not a *new* retry level + if msg.retries < pp.highWatermark { + // in fact this message is not even the current retry level, so buffer it for now (unless it's a just a fin) + if msg.flags&fin == fin { + pp.retryState[msg.retries].expectChaser = false + pp.parent.inFlight.Done() // this fin is now handled and will be garbage collected + } else { + pp.retryState[msg.retries].buf = append(pp.retryState[msg.retries].buf, msg) + } + continue + } else if msg.flags&fin == fin { + // this message is of the current retry level (msg.retries == highWatermark) and the fin flag is set, + // meaning this retry level is done and we can go down (at least) one level and flush that + pp.retryState[pp.highWatermark].expectChaser = false + pp.flushRetryBuffers() + pp.parent.inFlight.Done() // this fin is now handled and will be garbage collected + continue + } + } + + // if we made it this far then the current msg contains real data, and can be sent to the next goroutine + // without breaking any of our ordering guarantees + + if pp.output == nil { + if err := pp.updateLeader(); err != nil { + pp.parent.returnError(msg, err) + time.Sleep(pp.parent.conf.Producer.Retry.Backoff) + continue + } + Logger.Printf("producer/leader/%s/%d selected broker %d\n", pp.topic, pp.partition, pp.leader.ID()) + } + + pp.output <- msg + } + + if pp.output != nil { + pp.parent.unrefBrokerProducer(pp.leader, pp.output) + } +} + +func (pp *partitionProducer) newHighWatermark(hwm int) { + Logger.Printf("producer/leader/%s/%d state change to [retrying-%d]\n", pp.topic, pp.partition, hwm) + pp.highWatermark = hwm + + // send off a fin so that we know when everything "in between" has made it + // back to us and we can safely flush the backlog (otherwise we risk re-ordering messages) + pp.retryState[pp.highWatermark].expectChaser = true + pp.parent.inFlight.Add(1) // we're generating a fin message; track it so we don't shut down while it's still inflight + pp.output <- &ProducerMessage{Topic: pp.topic, Partition: pp.partition, flags: fin, retries: pp.highWatermark - 1} + + // a new HWM means that our current broker selection is out of date + Logger.Printf("producer/leader/%s/%d abandoning broker %d\n", pp.topic, pp.partition, pp.leader.ID()) + pp.parent.unrefBrokerProducer(pp.leader, pp.output) + pp.output = nil +} + +func (pp *partitionProducer) flushRetryBuffers() { + Logger.Printf("producer/leader/%s/%d state change to [flushing-%d]\n", pp.topic, pp.partition, pp.highWatermark) + for { + pp.highWatermark-- + + if pp.output == nil { + if err := pp.updateLeader(); err != nil { + pp.parent.returnErrors(pp.retryState[pp.highWatermark].buf, err) + goto flushDone + } + Logger.Printf("producer/leader/%s/%d selected broker %d\n", pp.topic, pp.partition, pp.leader.ID()) + } + + for _, msg := range pp.retryState[pp.highWatermark].buf { + pp.output <- msg + } + + flushDone: + pp.retryState[pp.highWatermark].buf = nil + if pp.retryState[pp.highWatermark].expectChaser { + Logger.Printf("producer/leader/%s/%d state change to [retrying-%d]\n", pp.topic, pp.partition, pp.highWatermark) + break + } else if pp.highWatermark == 0 { + Logger.Printf("producer/leader/%s/%d state change to [normal]\n", pp.topic, pp.partition) + break + } + } +} + +func (pp *partitionProducer) updateLeader() error { + return pp.breaker.Run(func() (err error) { + if err = pp.parent.client.RefreshMetadata(pp.topic); err != nil { + return err + } + + if pp.leader, err = pp.parent.client.Leader(pp.topic, pp.partition); err != nil { + return err + } + + pp.output = pp.parent.getBrokerProducer(pp.leader) + pp.parent.inFlight.Add(1) // we're generating a syn message; track it so we don't shut down while it's still inflight + pp.output <- &ProducerMessage{Topic: pp.topic, Partition: pp.partition, flags: syn} + + return nil + }) +} + +// one per broker; also constructs an associated flusher +func (p *asyncProducer) newBrokerProducer(broker *Broker) chan<- *ProducerMessage { + var ( + input = make(chan *ProducerMessage) + bridge = make(chan *produceSet) + responses = make(chan *brokerProducerResponse) + ) + + bp := &brokerProducer{ + parent: p, + broker: broker, + input: input, + output: bridge, + responses: responses, + buffer: newProduceSet(p), + currentRetries: make(map[string]map[int32]error), + } + go withRecover(bp.run) + + // minimal bridge to make the network response `select`able + go withRecover(func() { + for set := range bridge { + request := set.buildRequest() + + response, err := broker.Produce(request) + + responses <- &brokerProducerResponse{ + set: set, + err: err, + res: response, + } + } + close(responses) + }) + + return input +} + +type brokerProducerResponse struct { + set *produceSet + err error + res *ProduceResponse +} + +// groups messages together into appropriately-sized batches for sending to the broker +// handles state related to retries etc +type brokerProducer struct { + parent *asyncProducer + broker *Broker + + input <-chan *ProducerMessage + output chan<- *produceSet + responses <-chan *brokerProducerResponse + + buffer *produceSet + timer <-chan time.Time + timerFired bool + + closing error + currentRetries map[string]map[int32]error +} + +func (bp *brokerProducer) run() { + var output chan<- *produceSet + Logger.Printf("producer/broker/%d starting up\n", bp.broker.ID()) + + for { + select { + case msg := <-bp.input: + if msg == nil { + bp.shutdown() + return + } + + if msg.flags&syn == syn { + Logger.Printf("producer/broker/%d state change to [open] on %s/%d\n", + bp.broker.ID(), msg.Topic, msg.Partition) + if bp.currentRetries[msg.Topic] == nil { + bp.currentRetries[msg.Topic] = make(map[int32]error) + } + bp.currentRetries[msg.Topic][msg.Partition] = nil + bp.parent.inFlight.Done() + continue + } + + if reason := bp.needsRetry(msg); reason != nil { + bp.parent.retryMessage(msg, reason) + + if bp.closing == nil && msg.flags&fin == fin { + // we were retrying this partition but we can start processing again + delete(bp.currentRetries[msg.Topic], msg.Partition) + Logger.Printf("producer/broker/%d state change to [closed] on %s/%d\n", + bp.broker.ID(), msg.Topic, msg.Partition) + } + + continue + } + + if bp.buffer.wouldOverflow(msg) { + if err := bp.waitForSpace(msg); err != nil { + bp.parent.retryMessage(msg, err) + continue + } + } + + if err := bp.buffer.add(msg); err != nil { + bp.parent.returnError(msg, err) + continue + } + + if bp.parent.conf.Producer.Flush.Frequency > 0 && bp.timer == nil { + bp.timer = time.After(bp.parent.conf.Producer.Flush.Frequency) + } + case <-bp.timer: + bp.timerFired = true + case output <- bp.buffer: + bp.rollOver() + case response := <-bp.responses: + bp.handleResponse(response) + } + + if bp.timerFired || bp.buffer.readyToFlush() { + output = bp.output + } else { + output = nil + } + } +} + +func (bp *brokerProducer) shutdown() { + for !bp.buffer.empty() { + select { + case response := <-bp.responses: + bp.handleResponse(response) + case bp.output <- bp.buffer: + bp.rollOver() + } + } + close(bp.output) + for response := range bp.responses { + bp.handleResponse(response) + } + + Logger.Printf("producer/broker/%d shut down\n", bp.broker.ID()) +} + +func (bp *brokerProducer) needsRetry(msg *ProducerMessage) error { + if bp.closing != nil { + return bp.closing + } + + return bp.currentRetries[msg.Topic][msg.Partition] +} + +func (bp *brokerProducer) waitForSpace(msg *ProducerMessage) error { + Logger.Printf("producer/broker/%d maximum request accumulated, waiting for space\n", bp.broker.ID()) + + for { + select { + case response := <-bp.responses: + bp.handleResponse(response) + // handling a response can change our state, so re-check some things + if reason := bp.needsRetry(msg); reason != nil { + return reason + } else if !bp.buffer.wouldOverflow(msg) { + return nil + } + case bp.output <- bp.buffer: + bp.rollOver() + return nil + } + } +} + +func (bp *brokerProducer) rollOver() { + bp.timer = nil + bp.timerFired = false + bp.buffer = newProduceSet(bp.parent) +} + +func (bp *brokerProducer) handleResponse(response *brokerProducerResponse) { + if response.err != nil { + bp.handleError(response.set, response.err) + } else { + bp.handleSuccess(response.set, response.res) + } + + if bp.buffer.empty() { + bp.rollOver() // this can happen if the response invalidated our buffer + } +} + +func (bp *brokerProducer) handleSuccess(sent *produceSet, response *ProduceResponse) { + // we iterate through the blocks in the request set, not the response, so that we notice + // if the response is missing a block completely + sent.eachPartition(func(topic string, partition int32, msgs []*ProducerMessage) { + if response == nil { + // this only happens when RequiredAcks is NoResponse, so we have to assume success + bp.parent.returnSuccesses(msgs) + return + } + + block := response.GetBlock(topic, partition) + if block == nil { + bp.parent.returnErrors(msgs, ErrIncompleteResponse) + return + } + + switch block.Err { + // Success + case ErrNoError: + if bp.parent.conf.Version.IsAtLeast(V0_10_0_0) && !block.Timestamp.IsZero() { + for _, msg := range msgs { + msg.Timestamp = block.Timestamp + } + } + for i, msg := range msgs { + msg.Offset = block.Offset + int64(i) + } + bp.parent.returnSuccesses(msgs) + // Retriable errors + case ErrInvalidMessage, ErrUnknownTopicOrPartition, ErrLeaderNotAvailable, ErrNotLeaderForPartition, + ErrRequestTimedOut, ErrNotEnoughReplicas, ErrNotEnoughReplicasAfterAppend: + Logger.Printf("producer/broker/%d state change to [retrying] on %s/%d because %v\n", + bp.broker.ID(), topic, partition, block.Err) + bp.currentRetries[topic][partition] = block.Err + bp.parent.retryMessages(msgs, block.Err) + bp.parent.retryMessages(bp.buffer.dropPartition(topic, partition), block.Err) + // Other non-retriable errors + default: + bp.parent.returnErrors(msgs, block.Err) + } + }) +} + +func (bp *brokerProducer) handleError(sent *produceSet, err error) { + switch err.(type) { + case PacketEncodingError: + sent.eachPartition(func(topic string, partition int32, msgs []*ProducerMessage) { + bp.parent.returnErrors(msgs, err) + }) + default: + Logger.Printf("producer/broker/%d state change to [closing] because %s\n", bp.broker.ID(), err) + bp.parent.abandonBrokerConnection(bp.broker) + _ = bp.broker.Close() + bp.closing = err + sent.eachPartition(func(topic string, partition int32, msgs []*ProducerMessage) { + bp.parent.retryMessages(msgs, err) + }) + bp.buffer.eachPartition(func(topic string, partition int32, msgs []*ProducerMessage) { + bp.parent.retryMessages(msgs, err) + }) + bp.rollOver() + } +} + +// singleton +// effectively a "bridge" between the flushers and the dispatcher in order to avoid deadlock +// based on https://godoc.org/github.com/eapache/channels#InfiniteChannel +func (p *asyncProducer) retryHandler() { + var msg *ProducerMessage + buf := queue.New() + + for { + if buf.Length() == 0 { + msg = <-p.retries + } else { + select { + case msg = <-p.retries: + case p.input <- buf.Peek().(*ProducerMessage): + buf.Remove() + continue + } + } + + if msg == nil { + return + } + + buf.Add(msg) + } +} + +// utility functions + +func (p *asyncProducer) shutdown() { + Logger.Println("Producer shutting down.") + p.inFlight.Add(1) + p.input <- &ProducerMessage{flags: shutdown} + + p.inFlight.Wait() + + if p.ownClient { + err := p.client.Close() + if err != nil { + Logger.Println("producer/shutdown failed to close the embedded client:", err) + } + } + + close(p.input) + close(p.retries) + close(p.errors) + close(p.successes) +} + +func (p *asyncProducer) returnError(msg *ProducerMessage, err error) { + msg.clear() + pErr := &ProducerError{Msg: msg, Err: err} + if p.conf.Producer.Return.Errors { + p.errors <- pErr + } else { + Logger.Println(pErr) + } + p.inFlight.Done() +} + +func (p *asyncProducer) returnErrors(batch []*ProducerMessage, err error) { + for _, msg := range batch { + p.returnError(msg, err) + } +} + +func (p *asyncProducer) returnSuccesses(batch []*ProducerMessage) { + for _, msg := range batch { + if p.conf.Producer.Return.Successes { + msg.clear() + p.successes <- msg + } + p.inFlight.Done() + } +} + +func (p *asyncProducer) retryMessage(msg *ProducerMessage, err error) { + if msg.retries >= p.conf.Producer.Retry.Max { + p.returnError(msg, err) + } else { + msg.retries++ + p.retries <- msg + } +} + +func (p *asyncProducer) retryMessages(batch []*ProducerMessage, err error) { + for _, msg := range batch { + p.retryMessage(msg, err) + } +} + +func (p *asyncProducer) getBrokerProducer(broker *Broker) chan<- *ProducerMessage { + p.brokerLock.Lock() + defer p.brokerLock.Unlock() + + bp := p.brokers[broker] + + if bp == nil { + bp = p.newBrokerProducer(broker) + p.brokers[broker] = bp + p.brokerRefs[bp] = 0 + } + + p.brokerRefs[bp]++ + + return bp +} + +func (p *asyncProducer) unrefBrokerProducer(broker *Broker, bp chan<- *ProducerMessage) { + p.brokerLock.Lock() + defer p.brokerLock.Unlock() + + p.brokerRefs[bp]-- + if p.brokerRefs[bp] == 0 { + close(bp) + delete(p.brokerRefs, bp) + + if p.brokers[broker] == bp { + delete(p.brokers, broker) + } + } +} + +func (p *asyncProducer) abandonBrokerConnection(broker *Broker) { + p.brokerLock.Lock() + defer p.brokerLock.Unlock() + + delete(p.brokers, broker) +} diff --git a/vendor/github.com/Shopify/sarama/balance_strategy.go b/vendor/github.com/Shopify/sarama/balance_strategy.go new file mode 100644 index 00000000000..e78988d7181 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/balance_strategy.go @@ -0,0 +1,129 @@ +package sarama + +import ( + "math" + "sort" +) + +// BalanceStrategyPlan is the results of any BalanceStrategy.Plan attempt. +// It contains an allocation of topic/partitions by memberID in the form of +// a `memberID -> topic -> partitions` map. +type BalanceStrategyPlan map[string]map[string][]int32 + +// Add assigns a topic with a number partitions to a member. +func (p BalanceStrategyPlan) Add(memberID, topic string, partitions ...int32) { + if len(partitions) == 0 { + return + } + if _, ok := p[memberID]; !ok { + p[memberID] = make(map[string][]int32, 1) + } + p[memberID][topic] = append(p[memberID][topic], partitions...) +} + +// -------------------------------------------------------------------- + +// BalanceStrategy is used to balance topics and partitions +// across memebers of a consumer group +type BalanceStrategy interface { + // Name uniquely identifies the strategy. + Name() string + + // Plan accepts a map of `memberID -> metadata` and a map of `topic -> partitions` + // and returns a distribution plan. + Plan(members map[string]ConsumerGroupMemberMetadata, topics map[string][]int32) (BalanceStrategyPlan, error) +} + +// -------------------------------------------------------------------- + +// BalanceStrategyRange is the default and assigns partitions as ranges to consumer group members. +// Example with one topic T with six partitions (0..5) and two members (M1, M2): +// M1: {T: [0, 1, 2]} +// M2: {T: [3, 4, 5]} +var BalanceStrategyRange = &balanceStrategy{ + name: "range", + coreFn: func(plan BalanceStrategyPlan, memberIDs []string, topic string, partitions []int32) { + step := float64(len(partitions)) / float64(len(memberIDs)) + + for i, memberID := range memberIDs { + pos := float64(i) + min := int(math.Floor(pos*step + 0.5)) + max := int(math.Floor((pos+1)*step + 0.5)) + plan.Add(memberID, topic, partitions[min:max]...) + } + }, +} + +// BalanceStrategyRoundRobin assigns partitions to members in alternating order. +// Example with topic T with six partitions (0..5) and two members (M1, M2): +// M1: {T: [0, 2, 4]} +// M2: {T: [1, 3, 5]} +var BalanceStrategyRoundRobin = &balanceStrategy{ + name: "roundrobin", + coreFn: func(plan BalanceStrategyPlan, memberIDs []string, topic string, partitions []int32) { + for i, part := range partitions { + memberID := memberIDs[i%len(memberIDs)] + plan.Add(memberID, topic, part) + } + }, +} + +// -------------------------------------------------------------------- + +type balanceStrategy struct { + name string + coreFn func(plan BalanceStrategyPlan, memberIDs []string, topic string, partitions []int32) +} + +// Name implements BalanceStrategy. +func (s *balanceStrategy) Name() string { return s.name } + +// Balance implements BalanceStrategy. +func (s *balanceStrategy) Plan(members map[string]ConsumerGroupMemberMetadata, topics map[string][]int32) (BalanceStrategyPlan, error) { + // Build members by topic map + mbt := make(map[string][]string) + for memberID, meta := range members { + for _, topic := range meta.Topics { + mbt[topic] = append(mbt[topic], memberID) + } + } + + // Sort members for each topic + for topic, memberIDs := range mbt { + sort.Sort(&balanceStrategySortable{ + topic: topic, + memberIDs: memberIDs, + }) + } + + // Assemble plan + plan := make(BalanceStrategyPlan, len(members)) + for topic, memberIDs := range mbt { + s.coreFn(plan, memberIDs, topic, topics[topic]) + } + return plan, nil +} + +type balanceStrategySortable struct { + topic string + memberIDs []string +} + +func (p balanceStrategySortable) Len() int { return len(p.memberIDs) } +func (p balanceStrategySortable) Swap(i, j int) { + p.memberIDs[i], p.memberIDs[j] = p.memberIDs[j], p.memberIDs[i] +} +func (p balanceStrategySortable) Less(i, j int) bool { + return balanceStrategyHashValue(p.topic, p.memberIDs[i]) < balanceStrategyHashValue(p.topic, p.memberIDs[j]) +} + +func balanceStrategyHashValue(vv ...string) uint32 { + h := uint32(2166136261) + for _, s := range vv { + for _, c := range s { + h ^= uint32(c) + h *= 16777619 + } + } + return h +} diff --git a/vendor/github.com/Shopify/sarama/broker.go b/vendor/github.com/Shopify/sarama/broker.go new file mode 100644 index 00000000000..26f63d51d6d --- /dev/null +++ b/vendor/github.com/Shopify/sarama/broker.go @@ -0,0 +1,884 @@ +package sarama + +import ( + "crypto/tls" + "encoding/binary" + "fmt" + "io" + "net" + "strconv" + "sync" + "sync/atomic" + "time" + + "github.com/rcrowley/go-metrics" +) + +// Broker represents a single Kafka broker connection. All operations on this object are entirely concurrency-safe. +type Broker struct { + id int32 + addr string + rack *string + + conf *Config + correlationID int32 + conn net.Conn + connErr error + lock sync.Mutex + opened int32 + + responses chan responsePromise + done chan bool + + incomingByteRate metrics.Meter + requestRate metrics.Meter + requestSize metrics.Histogram + requestLatency metrics.Histogram + outgoingByteRate metrics.Meter + responseRate metrics.Meter + responseSize metrics.Histogram + brokerIncomingByteRate metrics.Meter + brokerRequestRate metrics.Meter + brokerRequestSize metrics.Histogram + brokerRequestLatency metrics.Histogram + brokerOutgoingByteRate metrics.Meter + brokerResponseRate metrics.Meter + brokerResponseSize metrics.Histogram +} + +type responsePromise struct { + requestTime time.Time + correlationID int32 + packets chan []byte + errors chan error +} + +// NewBroker creates and returns a Broker targeting the given host:port address. +// This does not attempt to actually connect, you have to call Open() for that. +func NewBroker(addr string) *Broker { + return &Broker{id: -1, addr: addr} +} + +// Open tries to connect to the Broker if it is not already connected or connecting, but does not block +// waiting for the connection to complete. This means that any subsequent operations on the broker will +// block waiting for the connection to succeed or fail. To get the effect of a fully synchronous Open call, +// follow it by a call to Connected(). The only errors Open will return directly are ConfigurationError or +// AlreadyConnected. If conf is nil, the result of NewConfig() is used. +func (b *Broker) Open(conf *Config) error { + if !atomic.CompareAndSwapInt32(&b.opened, 0, 1) { + return ErrAlreadyConnected + } + + if conf == nil { + conf = NewConfig() + } + + err := conf.Validate() + if err != nil { + return err + } + + b.lock.Lock() + + go withRecover(func() { + defer b.lock.Unlock() + + dialer := net.Dialer{ + Timeout: conf.Net.DialTimeout, + KeepAlive: conf.Net.KeepAlive, + LocalAddr: conf.Net.LocalAddr, + } + + if conf.Net.TLS.Enable { + b.conn, b.connErr = tls.DialWithDialer(&dialer, "tcp", b.addr, conf.Net.TLS.Config) + } else { + b.conn, b.connErr = dialer.Dial("tcp", b.addr) + } + if b.connErr != nil { + Logger.Printf("Failed to connect to broker %s: %s\n", b.addr, b.connErr) + b.conn = nil + atomic.StoreInt32(&b.opened, 0) + return + } + b.conn = newBufConn(b.conn) + + b.conf = conf + + // Create or reuse the global metrics shared between brokers + b.incomingByteRate = metrics.GetOrRegisterMeter("incoming-byte-rate", conf.MetricRegistry) + b.requestRate = metrics.GetOrRegisterMeter("request-rate", conf.MetricRegistry) + b.requestSize = getOrRegisterHistogram("request-size", conf.MetricRegistry) + b.requestLatency = getOrRegisterHistogram("request-latency-in-ms", conf.MetricRegistry) + b.outgoingByteRate = metrics.GetOrRegisterMeter("outgoing-byte-rate", conf.MetricRegistry) + b.responseRate = metrics.GetOrRegisterMeter("response-rate", conf.MetricRegistry) + b.responseSize = getOrRegisterHistogram("response-size", conf.MetricRegistry) + // Do not gather metrics for seeded broker (only used during bootstrap) because they share + // the same id (-1) and are already exposed through the global metrics above + if b.id >= 0 { + b.brokerIncomingByteRate = getOrRegisterBrokerMeter("incoming-byte-rate", b, conf.MetricRegistry) + b.brokerRequestRate = getOrRegisterBrokerMeter("request-rate", b, conf.MetricRegistry) + b.brokerRequestSize = getOrRegisterBrokerHistogram("request-size", b, conf.MetricRegistry) + b.brokerRequestLatency = getOrRegisterBrokerHistogram("request-latency-in-ms", b, conf.MetricRegistry) + b.brokerOutgoingByteRate = getOrRegisterBrokerMeter("outgoing-byte-rate", b, conf.MetricRegistry) + b.brokerResponseRate = getOrRegisterBrokerMeter("response-rate", b, conf.MetricRegistry) + b.brokerResponseSize = getOrRegisterBrokerHistogram("response-size", b, conf.MetricRegistry) + } + + if conf.Net.SASL.Enable { + b.connErr = b.sendAndReceiveSASLPlainAuth() + if b.connErr != nil { + err = b.conn.Close() + if err == nil { + Logger.Printf("Closed connection to broker %s\n", b.addr) + } else { + Logger.Printf("Error while closing connection to broker %s: %s\n", b.addr, err) + } + b.conn = nil + atomic.StoreInt32(&b.opened, 0) + return + } + } + + b.done = make(chan bool) + b.responses = make(chan responsePromise, b.conf.Net.MaxOpenRequests-1) + + if b.id >= 0 { + Logger.Printf("Connected to broker at %s (registered as #%d)\n", b.addr, b.id) + } else { + Logger.Printf("Connected to broker at %s (unregistered)\n", b.addr) + } + go withRecover(b.responseReceiver) + }) + + return nil +} + +// Connected returns true if the broker is connected and false otherwise. If the broker is not +// connected but it had tried to connect, the error from that connection attempt is also returned. +func (b *Broker) Connected() (bool, error) { + b.lock.Lock() + defer b.lock.Unlock() + + return b.conn != nil, b.connErr +} + +func (b *Broker) Close() error { + b.lock.Lock() + defer b.lock.Unlock() + + if b.conn == nil { + return ErrNotConnected + } + + close(b.responses) + <-b.done + + err := b.conn.Close() + + b.conn = nil + b.connErr = nil + b.done = nil + b.responses = nil + + if b.id >= 0 { + b.conf.MetricRegistry.Unregister(getMetricNameForBroker("incoming-byte-rate", b)) + b.conf.MetricRegistry.Unregister(getMetricNameForBroker("request-rate", b)) + b.conf.MetricRegistry.Unregister(getMetricNameForBroker("outgoing-byte-rate", b)) + b.conf.MetricRegistry.Unregister(getMetricNameForBroker("response-rate", b)) + } + + if err == nil { + Logger.Printf("Closed connection to broker %s\n", b.addr) + } else { + Logger.Printf("Error while closing connection to broker %s: %s\n", b.addr, err) + } + + atomic.StoreInt32(&b.opened, 0) + + return err +} + +// ID returns the broker ID retrieved from Kafka's metadata, or -1 if that is not known. +func (b *Broker) ID() int32 { + return b.id +} + +// Addr returns the broker address as either retrieved from Kafka's metadata or passed to NewBroker. +func (b *Broker) Addr() string { + return b.addr +} + +func (b *Broker) GetMetadata(request *MetadataRequest) (*MetadataResponse, error) { + response := new(MetadataResponse) + + err := b.sendAndReceive(request, response) + + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) GetConsumerMetadata(request *ConsumerMetadataRequest) (*ConsumerMetadataResponse, error) { + response := new(ConsumerMetadataResponse) + + err := b.sendAndReceive(request, response) + + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) FindCoordinator(request *FindCoordinatorRequest) (*FindCoordinatorResponse, error) { + response := new(FindCoordinatorResponse) + + err := b.sendAndReceive(request, response) + + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) GetAvailableOffsets(request *OffsetRequest) (*OffsetResponse, error) { + response := new(OffsetResponse) + + err := b.sendAndReceive(request, response) + + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) Produce(request *ProduceRequest) (*ProduceResponse, error) { + var response *ProduceResponse + var err error + + if request.RequiredAcks == NoResponse { + err = b.sendAndReceive(request, nil) + } else { + response = new(ProduceResponse) + err = b.sendAndReceive(request, response) + } + + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) Fetch(request *FetchRequest) (*FetchResponse, error) { + response := new(FetchResponse) + + err := b.sendAndReceive(request, response) + + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) CommitOffset(request *OffsetCommitRequest) (*OffsetCommitResponse, error) { + response := new(OffsetCommitResponse) + + err := b.sendAndReceive(request, response) + + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) FetchOffset(request *OffsetFetchRequest) (*OffsetFetchResponse, error) { + response := new(OffsetFetchResponse) + + err := b.sendAndReceive(request, response) + + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) JoinGroup(request *JoinGroupRequest) (*JoinGroupResponse, error) { + response := new(JoinGroupResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) SyncGroup(request *SyncGroupRequest) (*SyncGroupResponse, error) { + response := new(SyncGroupResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) LeaveGroup(request *LeaveGroupRequest) (*LeaveGroupResponse, error) { + response := new(LeaveGroupResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) Heartbeat(request *HeartbeatRequest) (*HeartbeatResponse, error) { + response := new(HeartbeatResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) ListGroups(request *ListGroupsRequest) (*ListGroupsResponse, error) { + response := new(ListGroupsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) DescribeGroups(request *DescribeGroupsRequest) (*DescribeGroupsResponse, error) { + response := new(DescribeGroupsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) ApiVersions(request *ApiVersionsRequest) (*ApiVersionsResponse, error) { + response := new(ApiVersionsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) CreateTopics(request *CreateTopicsRequest) (*CreateTopicsResponse, error) { + response := new(CreateTopicsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) DeleteTopics(request *DeleteTopicsRequest) (*DeleteTopicsResponse, error) { + response := new(DeleteTopicsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) CreatePartitions(request *CreatePartitionsRequest) (*CreatePartitionsResponse, error) { + response := new(CreatePartitionsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) DeleteRecords(request *DeleteRecordsRequest) (*DeleteRecordsResponse, error) { + response := new(DeleteRecordsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) DescribeAcls(request *DescribeAclsRequest) (*DescribeAclsResponse, error) { + response := new(DescribeAclsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) CreateAcls(request *CreateAclsRequest) (*CreateAclsResponse, error) { + response := new(CreateAclsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) DeleteAcls(request *DeleteAclsRequest) (*DeleteAclsResponse, error) { + response := new(DeleteAclsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) InitProducerID(request *InitProducerIDRequest) (*InitProducerIDResponse, error) { + response := new(InitProducerIDResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) AddPartitionsToTxn(request *AddPartitionsToTxnRequest) (*AddPartitionsToTxnResponse, error) { + response := new(AddPartitionsToTxnResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) AddOffsetsToTxn(request *AddOffsetsToTxnRequest) (*AddOffsetsToTxnResponse, error) { + response := new(AddOffsetsToTxnResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) EndTxn(request *EndTxnRequest) (*EndTxnResponse, error) { + response := new(EndTxnResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) TxnOffsetCommit(request *TxnOffsetCommitRequest) (*TxnOffsetCommitResponse, error) { + response := new(TxnOffsetCommitResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) DescribeConfigs(request *DescribeConfigsRequest) (*DescribeConfigsResponse, error) { + response := new(DescribeConfigsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) AlterConfigs(request *AlterConfigsRequest) (*AlterConfigsResponse, error) { + response := new(AlterConfigsResponse) + + err := b.sendAndReceive(request, response) + if err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) DeleteGroups(request *DeleteGroupsRequest) (*DeleteGroupsResponse, error) { + response := new(DeleteGroupsResponse) + + if err := b.sendAndReceive(request, response); err != nil { + return nil, err + } + + return response, nil +} + +func (b *Broker) send(rb protocolBody, promiseResponse bool) (*responsePromise, error) { + b.lock.Lock() + defer b.lock.Unlock() + + if b.conn == nil { + if b.connErr != nil { + return nil, b.connErr + } + return nil, ErrNotConnected + } + + if !b.conf.Version.IsAtLeast(rb.requiredVersion()) { + return nil, ErrUnsupportedVersion + } + + req := &request{correlationID: b.correlationID, clientID: b.conf.ClientID, body: rb} + buf, err := encode(req, b.conf.MetricRegistry) + if err != nil { + return nil, err + } + + err = b.conn.SetWriteDeadline(time.Now().Add(b.conf.Net.WriteTimeout)) + if err != nil { + return nil, err + } + + requestTime := time.Now() + bytes, err := b.conn.Write(buf) + b.updateOutgoingCommunicationMetrics(bytes) + if err != nil { + return nil, err + } + b.correlationID++ + + if !promiseResponse { + // Record request latency without the response + b.updateRequestLatencyMetrics(time.Since(requestTime)) + return nil, nil + } + + promise := responsePromise{requestTime, req.correlationID, make(chan []byte), make(chan error)} + b.responses <- promise + + return &promise, nil +} + +func (b *Broker) sendAndReceive(req protocolBody, res versionedDecoder) error { + promise, err := b.send(req, res != nil) + + if err != nil { + return err + } + + if promise == nil { + return nil + } + + select { + case buf := <-promise.packets: + return versionedDecode(buf, res, req.version()) + case err = <-promise.errors: + return err + } +} + +func (b *Broker) decode(pd packetDecoder, version int16) (err error) { + b.id, err = pd.getInt32() + if err != nil { + return err + } + + host, err := pd.getString() + if err != nil { + return err + } + + port, err := pd.getInt32() + if err != nil { + return err + } + + if version >= 1 { + b.rack, err = pd.getNullableString() + if err != nil { + return err + } + } + + b.addr = net.JoinHostPort(host, fmt.Sprint(port)) + if _, _, err := net.SplitHostPort(b.addr); err != nil { + return err + } + + return nil +} + +func (b *Broker) encode(pe packetEncoder, version int16) (err error) { + + host, portstr, err := net.SplitHostPort(b.addr) + if err != nil { + return err + } + port, err := strconv.Atoi(portstr) + if err != nil { + return err + } + + pe.putInt32(b.id) + + err = pe.putString(host) + if err != nil { + return err + } + + pe.putInt32(int32(port)) + + if version >= 1 { + err = pe.putNullableString(b.rack) + if err != nil { + return err + } + } + + return nil +} + +func (b *Broker) responseReceiver() { + var dead error + header := make([]byte, 8) + for response := range b.responses { + if dead != nil { + response.errors <- dead + continue + } + + err := b.conn.SetReadDeadline(time.Now().Add(b.conf.Net.ReadTimeout)) + if err != nil { + dead = err + response.errors <- err + continue + } + + bytesReadHeader, err := io.ReadFull(b.conn, header) + requestLatency := time.Since(response.requestTime) + if err != nil { + b.updateIncomingCommunicationMetrics(bytesReadHeader, requestLatency) + dead = err + response.errors <- err + continue + } + + decodedHeader := responseHeader{} + err = decode(header, &decodedHeader) + if err != nil { + b.updateIncomingCommunicationMetrics(bytesReadHeader, requestLatency) + dead = err + response.errors <- err + continue + } + if decodedHeader.correlationID != response.correlationID { + b.updateIncomingCommunicationMetrics(bytesReadHeader, requestLatency) + // TODO if decoded ID < cur ID, discard until we catch up + // TODO if decoded ID > cur ID, save it so when cur ID catches up we have a response + dead = PacketDecodingError{fmt.Sprintf("correlation ID didn't match, wanted %d, got %d", response.correlationID, decodedHeader.correlationID)} + response.errors <- dead + continue + } + + buf := make([]byte, decodedHeader.length-4) + bytesReadBody, err := io.ReadFull(b.conn, buf) + b.updateIncomingCommunicationMetrics(bytesReadHeader+bytesReadBody, requestLatency) + if err != nil { + dead = err + response.errors <- err + continue + } + + response.packets <- buf + } + close(b.done) +} + +func (b *Broker) sendAndReceiveSASLPlainHandshake() error { + rb := &SaslHandshakeRequest{"PLAIN"} + req := &request{correlationID: b.correlationID, clientID: b.conf.ClientID, body: rb} + buf, err := encode(req, b.conf.MetricRegistry) + if err != nil { + return err + } + + err = b.conn.SetWriteDeadline(time.Now().Add(b.conf.Net.WriteTimeout)) + if err != nil { + return err + } + + requestTime := time.Now() + bytes, err := b.conn.Write(buf) + b.updateOutgoingCommunicationMetrics(bytes) + if err != nil { + Logger.Printf("Failed to send SASL handshake %s: %s\n", b.addr, err.Error()) + return err + } + b.correlationID++ + //wait for the response + header := make([]byte, 8) // response header + _, err = io.ReadFull(b.conn, header) + if err != nil { + Logger.Printf("Failed to read SASL handshake header : %s\n", err.Error()) + return err + } + length := binary.BigEndian.Uint32(header[:4]) + payload := make([]byte, length-4) + n, err := io.ReadFull(b.conn, payload) + if err != nil { + Logger.Printf("Failed to read SASL handshake payload : %s\n", err.Error()) + return err + } + b.updateIncomingCommunicationMetrics(n+8, time.Since(requestTime)) + res := &SaslHandshakeResponse{} + err = versionedDecode(payload, res, 0) + if err != nil { + Logger.Printf("Failed to parse SASL handshake : %s\n", err.Error()) + return err + } + if res.Err != ErrNoError { + Logger.Printf("Invalid SASL Mechanism : %s\n", res.Err.Error()) + return res.Err + } + Logger.Print("Successful SASL handshake") + return nil +} + +// Kafka 0.10.0 plans to support SASL Plain and Kerberos as per PR #812 (KIP-43)/(JIRA KAFKA-3149) +// Some hosted kafka services such as IBM Message Hub already offer SASL/PLAIN auth with Kafka 0.9 +// +// In SASL Plain, Kafka expects the auth header to be in the following format +// Message format (from https://tools.ietf.org/html/rfc4616): +// +// message = [authzid] UTF8NUL authcid UTF8NUL passwd +// authcid = 1*SAFE ; MUST accept up to 255 octets +// authzid = 1*SAFE ; MUST accept up to 255 octets +// passwd = 1*SAFE ; MUST accept up to 255 octets +// UTF8NUL = %x00 ; UTF-8 encoded NUL character +// +// SAFE = UTF1 / UTF2 / UTF3 / UTF4 +// ;; any UTF-8 encoded Unicode character except NUL +// +// When credentials are valid, Kafka returns a 4 byte array of null characters. +// When credentials are invalid, Kafka closes the connection. This does not seem to be the ideal way +// of responding to bad credentials but thats how its being done today. +func (b *Broker) sendAndReceiveSASLPlainAuth() error { + if b.conf.Net.SASL.Handshake { + handshakeErr := b.sendAndReceiveSASLPlainHandshake() + if handshakeErr != nil { + Logger.Printf("Error while performing SASL handshake %s\n", b.addr) + return handshakeErr + } + } + length := 1 + len(b.conf.Net.SASL.User) + 1 + len(b.conf.Net.SASL.Password) + authBytes := make([]byte, length+4) //4 byte length header + auth data + binary.BigEndian.PutUint32(authBytes, uint32(length)) + copy(authBytes[4:], []byte("\x00"+b.conf.Net.SASL.User+"\x00"+b.conf.Net.SASL.Password)) + + err := b.conn.SetWriteDeadline(time.Now().Add(b.conf.Net.WriteTimeout)) + if err != nil { + Logger.Printf("Failed to set write deadline when doing SASL auth with broker %s: %s\n", b.addr, err.Error()) + return err + } + + requestTime := time.Now() + bytesWritten, err := b.conn.Write(authBytes) + b.updateOutgoingCommunicationMetrics(bytesWritten) + if err != nil { + Logger.Printf("Failed to write SASL auth header to broker %s: %s\n", b.addr, err.Error()) + return err + } + + header := make([]byte, 4) + n, err := io.ReadFull(b.conn, header) + b.updateIncomingCommunicationMetrics(n, time.Since(requestTime)) + // If the credentials are valid, we would get a 4 byte response filled with null characters. + // Otherwise, the broker closes the connection and we get an EOF + if err != nil { + Logger.Printf("Failed to read response while authenticating with SASL to broker %s: %s\n", b.addr, err.Error()) + return err + } + + Logger.Printf("SASL authentication successful with broker %s:%v - %v\n", b.addr, n, header) + return nil +} + +func (b *Broker) updateIncomingCommunicationMetrics(bytes int, requestLatency time.Duration) { + b.updateRequestLatencyMetrics(requestLatency) + b.responseRate.Mark(1) + if b.brokerResponseRate != nil { + b.brokerResponseRate.Mark(1) + } + responseSize := int64(bytes) + b.incomingByteRate.Mark(responseSize) + if b.brokerIncomingByteRate != nil { + b.brokerIncomingByteRate.Mark(responseSize) + } + b.responseSize.Update(responseSize) + if b.brokerResponseSize != nil { + b.brokerResponseSize.Update(responseSize) + } +} + +func (b *Broker) updateRequestLatencyMetrics(requestLatency time.Duration) { + requestLatencyInMs := int64(requestLatency / time.Millisecond) + b.requestLatency.Update(requestLatencyInMs) + if b.brokerRequestLatency != nil { + b.brokerRequestLatency.Update(requestLatencyInMs) + } +} + +func (b *Broker) updateOutgoingCommunicationMetrics(bytes int) { + b.requestRate.Mark(1) + if b.brokerRequestRate != nil { + b.brokerRequestRate.Mark(1) + } + requestSize := int64(bytes) + b.outgoingByteRate.Mark(requestSize) + if b.brokerOutgoingByteRate != nil { + b.brokerOutgoingByteRate.Mark(requestSize) + } + b.requestSize.Update(requestSize) + if b.brokerRequestSize != nil { + b.brokerRequestSize.Update(requestSize) + } +} diff --git a/vendor/github.com/Shopify/sarama/client.go b/vendor/github.com/Shopify/sarama/client.go new file mode 100644 index 00000000000..ad805346b4b --- /dev/null +++ b/vendor/github.com/Shopify/sarama/client.go @@ -0,0 +1,876 @@ +package sarama + +import ( + "math/rand" + "sort" + "sync" + "time" +) + +// Client is a generic Kafka client. It manages connections to one or more Kafka brokers. +// You MUST call Close() on a client to avoid leaks, it will not be garbage-collected +// automatically when it passes out of scope. It is safe to share a client amongst many +// users, however Kafka will process requests from a single client strictly in serial, +// so it is generally more efficient to use the default one client per producer/consumer. +type Client interface { + // Config returns the Config struct of the client. This struct should not be + // altered after it has been created. + Config() *Config + + // Controller returns the cluster controller broker. Requires Kafka 0.10 or higher. + Controller() (*Broker, error) + + // Brokers returns the current set of active brokers as retrieved from cluster metadata. + Brokers() []*Broker + + // Topics returns the set of available topics as retrieved from cluster metadata. + Topics() ([]string, error) + + // Partitions returns the sorted list of all partition IDs for the given topic. + Partitions(topic string) ([]int32, error) + + // WritablePartitions returns the sorted list of all writable partition IDs for + // the given topic, where "writable" means "having a valid leader accepting + // writes". + WritablePartitions(topic string) ([]int32, error) + + // Leader returns the broker object that is the leader of the current + // topic/partition, as determined by querying the cluster metadata. + Leader(topic string, partitionID int32) (*Broker, error) + + // Replicas returns the set of all replica IDs for the given partition. + Replicas(topic string, partitionID int32) ([]int32, error) + + // InSyncReplicas returns the set of all in-sync replica IDs for the given + // partition. In-sync replicas are replicas which are fully caught up with + // the partition leader. + InSyncReplicas(topic string, partitionID int32) ([]int32, error) + + // RefreshMetadata takes a list of topics and queries the cluster to refresh the + // available metadata for those topics. If no topics are provided, it will refresh + // metadata for all topics. + RefreshMetadata(topics ...string) error + + // GetOffset queries the cluster to get the most recent available offset at the + // given time (in milliseconds) on the topic/partition combination. + // Time should be OffsetOldest for the earliest available offset, + // OffsetNewest for the offset of the message that will be produced next, or a time. + GetOffset(topic string, partitionID int32, time int64) (int64, error) + + // Coordinator returns the coordinating broker for a consumer group. It will + // return a locally cached value if it's available. You can call + // RefreshCoordinator to update the cached value. This function only works on + // Kafka 0.8.2 and higher. + Coordinator(consumerGroup string) (*Broker, error) + + // RefreshCoordinator retrieves the coordinator for a consumer group and stores it + // in local cache. This function only works on Kafka 0.8.2 and higher. + RefreshCoordinator(consumerGroup string) error + + // Close shuts down all broker connections managed by this client. It is required + // to call this function before a client object passes out of scope, as it will + // otherwise leak memory. You must close any Producers or Consumers using a client + // before you close the client. + Close() error + + // Closed returns true if the client has already had Close called on it + Closed() bool +} + +const ( + // OffsetNewest stands for the log head offset, i.e. the offset that will be + // assigned to the next message that will be produced to the partition. You + // can send this to a client's GetOffset method to get this offset, or when + // calling ConsumePartition to start consuming new messages. + OffsetNewest int64 = -1 + // OffsetOldest stands for the oldest offset available on the broker for a + // partition. You can send this to a client's GetOffset method to get this + // offset, or when calling ConsumePartition to start consuming from the + // oldest offset that is still available on the broker. + OffsetOldest int64 = -2 +) + +type client struct { + conf *Config + closer, closed chan none // for shutting down background metadata updater + + // the broker addresses given to us through the constructor are not guaranteed to be returned in + // the cluster metadata (I *think* it only returns brokers who are currently leading partitions?) + // so we store them separately + seedBrokers []*Broker + deadSeeds []*Broker + + controllerID int32 // cluster controller broker id + brokers map[int32]*Broker // maps broker ids to brokers + metadata map[string]map[int32]*PartitionMetadata // maps topics to partition ids to metadata + metadataTopics map[string]none // topics that need to collect metadata + coordinators map[string]int32 // Maps consumer group names to coordinating broker IDs + + // If the number of partitions is large, we can get some churn calling cachedPartitions, + // so the result is cached. It is important to update this value whenever metadata is changed + cachedPartitionsResults map[string][maxPartitionIndex][]int32 + + lock sync.RWMutex // protects access to the maps that hold cluster state. +} + +// NewClient creates a new Client. It connects to one of the given broker addresses +// and uses that broker to automatically fetch metadata on the rest of the kafka cluster. If metadata cannot +// be retrieved from any of the given broker addresses, the client is not created. +func NewClient(addrs []string, conf *Config) (Client, error) { + Logger.Println("Initializing new client") + + if conf == nil { + conf = NewConfig() + } + + if err := conf.Validate(); err != nil { + return nil, err + } + + if len(addrs) < 1 { + return nil, ConfigurationError("You must provide at least one broker address") + } + + client := &client{ + conf: conf, + closer: make(chan none), + closed: make(chan none), + brokers: make(map[int32]*Broker), + metadata: make(map[string]map[int32]*PartitionMetadata), + metadataTopics: make(map[string]none), + cachedPartitionsResults: make(map[string][maxPartitionIndex][]int32), + coordinators: make(map[string]int32), + } + + random := rand.New(rand.NewSource(time.Now().UnixNano())) + for _, index := range random.Perm(len(addrs)) { + client.seedBrokers = append(client.seedBrokers, NewBroker(addrs[index])) + } + + if conf.Metadata.Full { + // do an initial fetch of all cluster metadata by specifying an empty list of topics + err := client.RefreshMetadata() + switch err { + case nil: + break + case ErrLeaderNotAvailable, ErrReplicaNotAvailable, ErrTopicAuthorizationFailed, ErrClusterAuthorizationFailed: + // indicates that maybe part of the cluster is down, but is not fatal to creating the client + Logger.Println(err) + default: + close(client.closed) // we haven't started the background updater yet, so we have to do this manually + _ = client.Close() + return nil, err + } + } + go withRecover(client.backgroundMetadataUpdater) + + Logger.Println("Successfully initialized new client") + + return client, nil +} + +func (client *client) Config() *Config { + return client.conf +} + +func (client *client) Brokers() []*Broker { + client.lock.RLock() + defer client.lock.RUnlock() + brokers := make([]*Broker, 0) + for _, broker := range client.brokers { + brokers = append(brokers, broker) + } + return brokers +} + +func (client *client) Close() error { + if client.Closed() { + // Chances are this is being called from a defer() and the error will go unobserved + // so we go ahead and log the event in this case. + Logger.Printf("Close() called on already closed client") + return ErrClosedClient + } + + // shutdown and wait for the background thread before we take the lock, to avoid races + close(client.closer) + <-client.closed + + client.lock.Lock() + defer client.lock.Unlock() + Logger.Println("Closing Client") + + for _, broker := range client.brokers { + safeAsyncClose(broker) + } + + for _, broker := range client.seedBrokers { + safeAsyncClose(broker) + } + + client.brokers = nil + client.metadata = nil + client.metadataTopics = nil + + return nil +} + +func (client *client) Closed() bool { + return client.brokers == nil +} + +func (client *client) Topics() ([]string, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + client.lock.RLock() + defer client.lock.RUnlock() + + ret := make([]string, 0, len(client.metadata)) + for topic := range client.metadata { + ret = append(ret, topic) + } + + return ret, nil +} + +func (client *client) MetadataTopics() ([]string, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + client.lock.RLock() + defer client.lock.RUnlock() + + ret := make([]string, 0, len(client.metadataTopics)) + for topic := range client.metadataTopics { + ret = append(ret, topic) + } + + return ret, nil +} + +func (client *client) Partitions(topic string) ([]int32, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + partitions := client.cachedPartitions(topic, allPartitions) + + if len(partitions) == 0 { + err := client.RefreshMetadata(topic) + if err != nil { + return nil, err + } + partitions = client.cachedPartitions(topic, allPartitions) + } + + if partitions == nil { + return nil, ErrUnknownTopicOrPartition + } + + return partitions, nil +} + +func (client *client) WritablePartitions(topic string) ([]int32, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + partitions := client.cachedPartitions(topic, writablePartitions) + + // len==0 catches when it's nil (no such topic) and the odd case when every single + // partition is undergoing leader election simultaneously. Callers have to be able to handle + // this function returning an empty slice (which is a valid return value) but catching it + // here the first time (note we *don't* catch it below where we return ErrUnknownTopicOrPartition) triggers + // a metadata refresh as a nicety so callers can just try again and don't have to manually + // trigger a refresh (otherwise they'd just keep getting a stale cached copy). + if len(partitions) == 0 { + err := client.RefreshMetadata(topic) + if err != nil { + return nil, err + } + partitions = client.cachedPartitions(topic, writablePartitions) + } + + if partitions == nil { + return nil, ErrUnknownTopicOrPartition + } + + return partitions, nil +} + +func (client *client) Replicas(topic string, partitionID int32) ([]int32, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + metadata := client.cachedMetadata(topic, partitionID) + + if metadata == nil { + err := client.RefreshMetadata(topic) + if err != nil { + return nil, err + } + metadata = client.cachedMetadata(topic, partitionID) + } + + if metadata == nil { + return nil, ErrUnknownTopicOrPartition + } + + if metadata.Err == ErrReplicaNotAvailable { + return dupInt32Slice(metadata.Replicas), metadata.Err + } + return dupInt32Slice(metadata.Replicas), nil +} + +func (client *client) InSyncReplicas(topic string, partitionID int32) ([]int32, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + metadata := client.cachedMetadata(topic, partitionID) + + if metadata == nil { + err := client.RefreshMetadata(topic) + if err != nil { + return nil, err + } + metadata = client.cachedMetadata(topic, partitionID) + } + + if metadata == nil { + return nil, ErrUnknownTopicOrPartition + } + + if metadata.Err == ErrReplicaNotAvailable { + return dupInt32Slice(metadata.Isr), metadata.Err + } + return dupInt32Slice(metadata.Isr), nil +} + +func (client *client) Leader(topic string, partitionID int32) (*Broker, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + leader, err := client.cachedLeader(topic, partitionID) + + if leader == nil { + err = client.RefreshMetadata(topic) + if err != nil { + return nil, err + } + leader, err = client.cachedLeader(topic, partitionID) + } + + return leader, err +} + +func (client *client) RefreshMetadata(topics ...string) error { + if client.Closed() { + return ErrClosedClient + } + + // Prior to 0.8.2, Kafka will throw exceptions on an empty topic and not return a proper + // error. This handles the case by returning an error instead of sending it + // off to Kafka. See: https://github.com/Shopify/sarama/pull/38#issuecomment-26362310 + for _, topic := range topics { + if len(topic) == 0 { + return ErrInvalidTopic // this is the error that 0.8.2 and later correctly return + } + } + + return client.tryRefreshMetadata(topics, client.conf.Metadata.Retry.Max) +} + +func (client *client) GetOffset(topic string, partitionID int32, time int64) (int64, error) { + if client.Closed() { + return -1, ErrClosedClient + } + + offset, err := client.getOffset(topic, partitionID, time) + + if err != nil { + if err := client.RefreshMetadata(topic); err != nil { + return -1, err + } + return client.getOffset(topic, partitionID, time) + } + + return offset, err +} + +func (client *client) Controller() (*Broker, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + if !client.conf.Version.IsAtLeast(V0_10_0_0) { + return nil, ErrUnsupportedVersion + } + + controller := client.cachedController() + if controller == nil { + if err := client.refreshMetadata(); err != nil { + return nil, err + } + controller = client.cachedController() + } + + if controller == nil { + return nil, ErrControllerNotAvailable + } + + _ = controller.Open(client.conf) + return controller, nil +} + +func (client *client) Coordinator(consumerGroup string) (*Broker, error) { + if client.Closed() { + return nil, ErrClosedClient + } + + coordinator := client.cachedCoordinator(consumerGroup) + + if coordinator == nil { + if err := client.RefreshCoordinator(consumerGroup); err != nil { + return nil, err + } + coordinator = client.cachedCoordinator(consumerGroup) + } + + if coordinator == nil { + return nil, ErrConsumerCoordinatorNotAvailable + } + + _ = coordinator.Open(client.conf) + return coordinator, nil +} + +func (client *client) RefreshCoordinator(consumerGroup string) error { + if client.Closed() { + return ErrClosedClient + } + + response, err := client.getConsumerMetadata(consumerGroup, client.conf.Metadata.Retry.Max) + if err != nil { + return err + } + + client.lock.Lock() + defer client.lock.Unlock() + client.registerBroker(response.Coordinator) + client.coordinators[consumerGroup] = response.Coordinator.ID() + return nil +} + +// private broker management helpers + +// registerBroker makes sure a broker received by a Metadata or Coordinator request is registered +// in the brokers map. It returns the broker that is registered, which may be the provided broker, +// or a previously registered Broker instance. You must hold the write lock before calling this function. +func (client *client) registerBroker(broker *Broker) { + if client.brokers[broker.ID()] == nil { + client.brokers[broker.ID()] = broker + Logger.Printf("client/brokers registered new broker #%d at %s", broker.ID(), broker.Addr()) + } else if broker.Addr() != client.brokers[broker.ID()].Addr() { + safeAsyncClose(client.brokers[broker.ID()]) + client.brokers[broker.ID()] = broker + Logger.Printf("client/brokers replaced registered broker #%d with %s", broker.ID(), broker.Addr()) + } +} + +// deregisterBroker removes a broker from the seedsBroker list, and if it's +// not the seedbroker, removes it from brokers map completely. +func (client *client) deregisterBroker(broker *Broker) { + client.lock.Lock() + defer client.lock.Unlock() + + if len(client.seedBrokers) > 0 && broker == client.seedBrokers[0] { + client.deadSeeds = append(client.deadSeeds, broker) + client.seedBrokers = client.seedBrokers[1:] + } else { + // we do this so that our loop in `tryRefreshMetadata` doesn't go on forever, + // but we really shouldn't have to; once that loop is made better this case can be + // removed, and the function generally can be renamed from `deregisterBroker` to + // `nextSeedBroker` or something + Logger.Printf("client/brokers deregistered broker #%d at %s", broker.ID(), broker.Addr()) + delete(client.brokers, broker.ID()) + } +} + +func (client *client) resurrectDeadBrokers() { + client.lock.Lock() + defer client.lock.Unlock() + + Logger.Printf("client/brokers resurrecting %d dead seed brokers", len(client.deadSeeds)) + client.seedBrokers = append(client.seedBrokers, client.deadSeeds...) + client.deadSeeds = nil +} + +func (client *client) any() *Broker { + client.lock.RLock() + defer client.lock.RUnlock() + + if len(client.seedBrokers) > 0 { + _ = client.seedBrokers[0].Open(client.conf) + return client.seedBrokers[0] + } + + // not guaranteed to be random *or* deterministic + for _, broker := range client.brokers { + _ = broker.Open(client.conf) + return broker + } + + return nil +} + +// private caching/lazy metadata helpers + +type partitionType int + +const ( + allPartitions partitionType = iota + writablePartitions + // If you add any more types, update the partition cache in update() + + // Ensure this is the last partition type value + maxPartitionIndex +) + +func (client *client) cachedMetadata(topic string, partitionID int32) *PartitionMetadata { + client.lock.RLock() + defer client.lock.RUnlock() + + partitions := client.metadata[topic] + if partitions != nil { + return partitions[partitionID] + } + + return nil +} + +func (client *client) cachedPartitions(topic string, partitionSet partitionType) []int32 { + client.lock.RLock() + defer client.lock.RUnlock() + + partitions, exists := client.cachedPartitionsResults[topic] + + if !exists { + return nil + } + return partitions[partitionSet] +} + +func (client *client) setPartitionCache(topic string, partitionSet partitionType) []int32 { + partitions := client.metadata[topic] + + if partitions == nil { + return nil + } + + ret := make([]int32, 0, len(partitions)) + for _, partition := range partitions { + if partitionSet == writablePartitions && partition.Err == ErrLeaderNotAvailable { + continue + } + ret = append(ret, partition.ID) + } + + sort.Sort(int32Slice(ret)) + return ret +} + +func (client *client) cachedLeader(topic string, partitionID int32) (*Broker, error) { + client.lock.RLock() + defer client.lock.RUnlock() + + partitions := client.metadata[topic] + if partitions != nil { + metadata, ok := partitions[partitionID] + if ok { + if metadata.Err == ErrLeaderNotAvailable { + return nil, ErrLeaderNotAvailable + } + b := client.brokers[metadata.Leader] + if b == nil { + return nil, ErrLeaderNotAvailable + } + _ = b.Open(client.conf) + return b, nil + } + } + + return nil, ErrUnknownTopicOrPartition +} + +func (client *client) getOffset(topic string, partitionID int32, time int64) (int64, error) { + broker, err := client.Leader(topic, partitionID) + if err != nil { + return -1, err + } + + request := &OffsetRequest{} + if client.conf.Version.IsAtLeast(V0_10_1_0) { + request.Version = 1 + } + request.AddBlock(topic, partitionID, time, 1) + + response, err := broker.GetAvailableOffsets(request) + if err != nil { + _ = broker.Close() + return -1, err + } + + block := response.GetBlock(topic, partitionID) + if block == nil { + _ = broker.Close() + return -1, ErrIncompleteResponse + } + if block.Err != ErrNoError { + return -1, block.Err + } + if len(block.Offsets) != 1 { + return -1, ErrOffsetOutOfRange + } + + return block.Offsets[0], nil +} + +// core metadata update logic + +func (client *client) backgroundMetadataUpdater() { + defer close(client.closed) + + if client.conf.Metadata.RefreshFrequency == time.Duration(0) { + return + } + + ticker := time.NewTicker(client.conf.Metadata.RefreshFrequency) + defer ticker.Stop() + + for { + select { + case <-ticker.C: + if err := client.refreshMetadata(); err != nil { + Logger.Println("Client background metadata update:", err) + } + case <-client.closer: + return + } + } +} + +func (client *client) refreshMetadata() error { + topics := []string{} + + if !client.conf.Metadata.Full { + if specificTopics, err := client.MetadataTopics(); err != nil { + return err + } else if len(specificTopics) == 0 { + return ErrNoTopicsToUpdateMetadata + } else { + topics = specificTopics + } + } + + if err := client.RefreshMetadata(topics...); err != nil { + return err + } + + return nil +} + +func (client *client) tryRefreshMetadata(topics []string, attemptsRemaining int) error { + retry := func(err error) error { + if attemptsRemaining > 0 { + Logger.Printf("client/metadata retrying after %dms... (%d attempts remaining)\n", client.conf.Metadata.Retry.Backoff/time.Millisecond, attemptsRemaining) + time.Sleep(client.conf.Metadata.Retry.Backoff) + return client.tryRefreshMetadata(topics, attemptsRemaining-1) + } + return err + } + + for broker := client.any(); broker != nil; broker = client.any() { + if len(topics) > 0 { + Logger.Printf("client/metadata fetching metadata for %v from broker %s\n", topics, broker.addr) + } else { + Logger.Printf("client/metadata fetching metadata for all topics from broker %s\n", broker.addr) + } + + req := &MetadataRequest{Topics: topics} + if client.conf.Version.IsAtLeast(V0_10_0_0) { + req.Version = 1 + } + response, err := broker.GetMetadata(req) + + switch err.(type) { + case nil: + allKnownMetaData := len(topics) == 0 + // valid response, use it + shouldRetry, err := client.updateMetadata(response, allKnownMetaData) + if shouldRetry { + Logger.Println("client/metadata found some partitions to be leaderless") + return retry(err) // note: err can be nil + } + return err + + case PacketEncodingError: + // didn't even send, return the error + return err + default: + // some other error, remove that broker and try again + Logger.Println("client/metadata got error from broker while fetching metadata:", err) + _ = broker.Close() + client.deregisterBroker(broker) + } + } + + Logger.Println("client/metadata no available broker to send metadata request to") + client.resurrectDeadBrokers() + return retry(ErrOutOfBrokers) +} + +// if no fatal error, returns a list of topics that need retrying due to ErrLeaderNotAvailable +func (client *client) updateMetadata(data *MetadataResponse, allKnownMetaData bool) (retry bool, err error) { + client.lock.Lock() + defer client.lock.Unlock() + + // For all the brokers we received: + // - if it is a new ID, save it + // - if it is an existing ID, but the address we have is stale, discard the old one and save it + // - otherwise ignore it, replacing our existing one would just bounce the connection + for _, broker := range data.Brokers { + client.registerBroker(broker) + } + + client.controllerID = data.ControllerID + + if allKnownMetaData { + client.metadata = make(map[string]map[int32]*PartitionMetadata) + client.metadataTopics = make(map[string]none) + client.cachedPartitionsResults = make(map[string][maxPartitionIndex][]int32) + } + for _, topic := range data.Topics { + // topics must be added firstly to `metadataTopics` to guarantee that all + // requested topics must be recorded to keep them trackable for periodically + // metadata refresh. + if _, exists := client.metadataTopics[topic.Name]; !exists { + client.metadataTopics[topic.Name] = none{} + } + delete(client.metadata, topic.Name) + delete(client.cachedPartitionsResults, topic.Name) + + switch topic.Err { + case ErrNoError: + break + case ErrInvalidTopic, ErrTopicAuthorizationFailed: // don't retry, don't store partial results + err = topic.Err + continue + case ErrUnknownTopicOrPartition: // retry, do not store partial partition results + err = topic.Err + retry = true + continue + case ErrLeaderNotAvailable: // retry, but store partial partition results + retry = true + break + default: // don't retry, don't store partial results + Logger.Printf("Unexpected topic-level metadata error: %s", topic.Err) + err = topic.Err + continue + } + + client.metadata[topic.Name] = make(map[int32]*PartitionMetadata, len(topic.Partitions)) + for _, partition := range topic.Partitions { + client.metadata[topic.Name][partition.ID] = partition + if partition.Err == ErrLeaderNotAvailable { + retry = true + } + } + + var partitionCache [maxPartitionIndex][]int32 + partitionCache[allPartitions] = client.setPartitionCache(topic.Name, allPartitions) + partitionCache[writablePartitions] = client.setPartitionCache(topic.Name, writablePartitions) + client.cachedPartitionsResults[topic.Name] = partitionCache + } + + return +} + +func (client *client) cachedCoordinator(consumerGroup string) *Broker { + client.lock.RLock() + defer client.lock.RUnlock() + if coordinatorID, ok := client.coordinators[consumerGroup]; ok { + return client.brokers[coordinatorID] + } + return nil +} + +func (client *client) cachedController() *Broker { + client.lock.RLock() + defer client.lock.RUnlock() + + return client.brokers[client.controllerID] +} + +func (client *client) getConsumerMetadata(consumerGroup string, attemptsRemaining int) (*FindCoordinatorResponse, error) { + retry := func(err error) (*FindCoordinatorResponse, error) { + if attemptsRemaining > 0 { + Logger.Printf("client/coordinator retrying after %dms... (%d attempts remaining)\n", client.conf.Metadata.Retry.Backoff/time.Millisecond, attemptsRemaining) + time.Sleep(client.conf.Metadata.Retry.Backoff) + return client.getConsumerMetadata(consumerGroup, attemptsRemaining-1) + } + return nil, err + } + + for broker := client.any(); broker != nil; broker = client.any() { + Logger.Printf("client/coordinator requesting coordinator for consumergroup %s from %s\n", consumerGroup, broker.Addr()) + + request := new(FindCoordinatorRequest) + request.CoordinatorKey = consumerGroup + request.CoordinatorType = CoordinatorGroup + + response, err := broker.FindCoordinator(request) + + if err != nil { + Logger.Printf("client/coordinator request to broker %s failed: %s\n", broker.Addr(), err) + + switch err.(type) { + case PacketEncodingError: + return nil, err + default: + _ = broker.Close() + client.deregisterBroker(broker) + continue + } + } + + switch response.Err { + case ErrNoError: + Logger.Printf("client/coordinator coordinator for consumergroup %s is #%d (%s)\n", consumerGroup, response.Coordinator.ID(), response.Coordinator.Addr()) + return response, nil + + case ErrConsumerCoordinatorNotAvailable: + Logger.Printf("client/coordinator coordinator for consumer group %s is not available\n", consumerGroup) + + // This is very ugly, but this scenario will only happen once per cluster. + // The __consumer_offsets topic only has to be created one time. + // The number of partitions not configurable, but partition 0 should always exist. + if _, err := client.Leader("__consumer_offsets", 0); err != nil { + Logger.Printf("client/coordinator the __consumer_offsets topic is not initialized completely yet. Waiting 2 seconds...\n") + time.Sleep(2 * time.Second) + } + + return retry(ErrConsumerCoordinatorNotAvailable) + default: + return nil, response.Err + } + } + + Logger.Println("client/coordinator no available broker to send consumer metadata request to") + client.resurrectDeadBrokers() + return retry(ErrOutOfBrokers) +} diff --git a/vendor/github.com/Shopify/sarama/config.go b/vendor/github.com/Shopify/sarama/config.go new file mode 100644 index 00000000000..faf11e83839 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/config.go @@ -0,0 +1,563 @@ +package sarama + +import ( + "compress/gzip" + "crypto/tls" + "fmt" + "io/ioutil" + "net" + "regexp" + "time" + + "github.com/rcrowley/go-metrics" +) + +const defaultClientID = "sarama" + +var validID = regexp.MustCompile(`\A[A-Za-z0-9._-]+\z`) + +// Config is used to pass multiple configuration options to Sarama's constructors. +type Config struct { + // Admin is the namespace for ClusterAdmin properties used by the administrative Kafka client. + Admin struct { + // The maximum duration the administrative Kafka client will wait for ClusterAdmin operations, + // including topics, brokers, configurations and ACLs (defaults to 3 seconds). + Timeout time.Duration + } + + // Net is the namespace for network-level properties used by the Broker, and + // shared by the Client/Producer/Consumer. + Net struct { + // How many outstanding requests a connection is allowed to have before + // sending on it blocks (default 5). + MaxOpenRequests int + + // All three of the below configurations are similar to the + // `socket.timeout.ms` setting in JVM kafka. All of them default + // to 30 seconds. + DialTimeout time.Duration // How long to wait for the initial connection. + ReadTimeout time.Duration // How long to wait for a response. + WriteTimeout time.Duration // How long to wait for a transmit. + + TLS struct { + // Whether or not to use TLS when connecting to the broker + // (defaults to false). + Enable bool + // The TLS configuration to use for secure connections if + // enabled (defaults to nil). + Config *tls.Config + } + + // SASL based authentication with broker. While there are multiple SASL authentication methods + // the current implementation is limited to plaintext (SASL/PLAIN) authentication + SASL struct { + // Whether or not to use SASL authentication when connecting to the broker + // (defaults to false). + Enable bool + // Whether or not to send the Kafka SASL handshake first if enabled + // (defaults to true). You should only set this to false if you're using + // a non-Kafka SASL proxy. + Handshake bool + //username and password for SASL/PLAIN authentication + User string + Password string + } + + // KeepAlive specifies the keep-alive period for an active network connection. + // If zero, keep-alives are disabled. (default is 0: disabled). + KeepAlive time.Duration + + // LocalAddr is the local address to use when dialing an + // address. The address must be of a compatible type for the + // network being dialed. + // If nil, a local address is automatically chosen. + LocalAddr net.Addr + } + + // Metadata is the namespace for metadata management properties used by the + // Client, and shared by the Producer/Consumer. + Metadata struct { + Retry struct { + // The total number of times to retry a metadata request when the + // cluster is in the middle of a leader election (default 3). + Max int + // How long to wait for leader election to occur before retrying + // (default 250ms). Similar to the JVM's `retry.backoff.ms`. + Backoff time.Duration + } + // How frequently to refresh the cluster metadata in the background. + // Defaults to 10 minutes. Set to 0 to disable. Similar to + // `topic.metadata.refresh.interval.ms` in the JVM version. + RefreshFrequency time.Duration + + // Whether to maintain a full set of metadata for all topics, or just + // the minimal set that has been necessary so far. The full set is simpler + // and usually more convenient, but can take up a substantial amount of + // memory if you have many topics and partitions. Defaults to true. + Full bool + } + + // Producer is the namespace for configuration related to producing messages, + // used by the Producer. + Producer struct { + // The maximum permitted size of a message (defaults to 1000000). Should be + // set equal to or smaller than the broker's `message.max.bytes`. + MaxMessageBytes int + // The level of acknowledgement reliability needed from the broker (defaults + // to WaitForLocal). Equivalent to the `request.required.acks` setting of the + // JVM producer. + RequiredAcks RequiredAcks + // The maximum duration the broker will wait the receipt of the number of + // RequiredAcks (defaults to 10 seconds). This is only relevant when + // RequiredAcks is set to WaitForAll or a number > 1. Only supports + // millisecond resolution, nanoseconds will be truncated. Equivalent to + // the JVM producer's `request.timeout.ms` setting. + Timeout time.Duration + // The type of compression to use on messages (defaults to no compression). + // Similar to `compression.codec` setting of the JVM producer. + Compression CompressionCodec + // The level of compression to use on messages. The meaning depends + // on the actual compression type used and defaults to default compression + // level for the codec. + CompressionLevel int + // Generates partitioners for choosing the partition to send messages to + // (defaults to hashing the message key). Similar to the `partitioner.class` + // setting for the JVM producer. + Partitioner PartitionerConstructor + + // Return specifies what channels will be populated. If they are set to true, + // you must read from the respective channels to prevent deadlock. If, + // however, this config is used to create a `SyncProducer`, both must be set + // to true and you shall not read from the channels since the producer does + // this internally. + Return struct { + // If enabled, successfully delivered messages will be returned on the + // Successes channel (default disabled). + Successes bool + + // If enabled, messages that failed to deliver will be returned on the + // Errors channel, including error (default enabled). + Errors bool + } + + // The following config options control how often messages are batched up and + // sent to the broker. By default, messages are sent as fast as possible, and + // all messages received while the current batch is in-flight are placed + // into the subsequent batch. + Flush struct { + // The best-effort number of bytes needed to trigger a flush. Use the + // global sarama.MaxRequestSize to set a hard upper limit. + Bytes int + // The best-effort number of messages needed to trigger a flush. Use + // `MaxMessages` to set a hard upper limit. + Messages int + // The best-effort frequency of flushes. Equivalent to + // `queue.buffering.max.ms` setting of JVM producer. + Frequency time.Duration + // The maximum number of messages the producer will send in a single + // broker request. Defaults to 0 for unlimited. Similar to + // `queue.buffering.max.messages` in the JVM producer. + MaxMessages int + } + + Retry struct { + // The total number of times to retry sending a message (default 3). + // Similar to the `message.send.max.retries` setting of the JVM producer. + Max int + // How long to wait for the cluster to settle between retries + // (default 100ms). Similar to the `retry.backoff.ms` setting of the + // JVM producer. + Backoff time.Duration + } + } + + // Consumer is the namespace for configuration related to consuming messages, + // used by the Consumer. + Consumer struct { + + // Group is the namespace for configuring consumer group. + Group struct { + Session struct { + // The timeout used to detect consumer failures when using Kafka's group management facility. + // The consumer sends periodic heartbeats to indicate its liveness to the broker. + // If no heartbeats are received by the broker before the expiration of this session timeout, + // then the broker will remove this consumer from the group and initiate a rebalance. + // Note that the value must be in the allowable range as configured in the broker configuration + // by `group.min.session.timeout.ms` and `group.max.session.timeout.ms` (default 10s) + Timeout time.Duration + } + Heartbeat struct { + // The expected time between heartbeats to the consumer coordinator when using Kafka's group + // management facilities. Heartbeats are used to ensure that the consumer's session stays active and + // to facilitate rebalancing when new consumers join or leave the group. + // The value must be set lower than Consumer.Group.Session.Timeout, but typically should be set no + // higher than 1/3 of that value. + // It can be adjusted even lower to control the expected time for normal rebalances (default 3s) + Interval time.Duration + } + Rebalance struct { + // Strategy for allocating topic partitions to members (default BalanceStrategyRange) + Strategy BalanceStrategy + // The maximum allowed time for each worker to join the group once a rebalance has begun. + // This is basically a limit on the amount of time needed for all tasks to flush any pending + // data and commit offsets. If the timeout is exceeded, then the worker will be removed from + // the group, which will cause offset commit failures (default 60s). + Timeout time.Duration + + Retry struct { + // When a new consumer joins a consumer group the set of consumers attempt to "rebalance" + // the load to assign partitions to each consumer. If the set of consumers changes while + // this assignment is taking place the rebalance will fail and retry. This setting controls + // the maximum number of attempts before giving up (default 4). + Max int + // Backoff time between retries during rebalance (default 2s) + Backoff time.Duration + } + } + Member struct { + // Custom metadata to include when joining the group. The user data for all joined members + // can be retrieved by sending a DescribeGroupRequest to the broker that is the + // coordinator for the group. + UserData []byte + } + } + + Retry struct { + // How long to wait after a failing to read from a partition before + // trying again (default 2s). + Backoff time.Duration + } + + // Fetch is the namespace for controlling how many bytes are retrieved by any + // given request. + Fetch struct { + // The minimum number of message bytes to fetch in a request - the broker + // will wait until at least this many are available. The default is 1, + // as 0 causes the consumer to spin when no messages are available. + // Equivalent to the JVM's `fetch.min.bytes`. + Min int32 + // The default number of message bytes to fetch from the broker in each + // request (default 1MB). This should be larger than the majority of + // your messages, or else the consumer will spend a lot of time + // negotiating sizes and not actually consuming. Similar to the JVM's + // `fetch.message.max.bytes`. + Default int32 + // The maximum number of message bytes to fetch from the broker in a + // single request. Messages larger than this will return + // ErrMessageTooLarge and will not be consumable, so you must be sure + // this is at least as large as your largest message. Defaults to 0 + // (no limit). Similar to the JVM's `fetch.message.max.bytes`. The + // global `sarama.MaxResponseSize` still applies. + Max int32 + } + // The maximum amount of time the broker will wait for Consumer.Fetch.Min + // bytes to become available before it returns fewer than that anyways. The + // default is 250ms, since 0 causes the consumer to spin when no events are + // available. 100-500ms is a reasonable range for most cases. Kafka only + // supports precision up to milliseconds; nanoseconds will be truncated. + // Equivalent to the JVM's `fetch.wait.max.ms`. + MaxWaitTime time.Duration + + // The maximum amount of time the consumer expects a message takes to + // process for the user. If writing to the Messages channel takes longer + // than this, that partition will stop fetching more messages until it + // can proceed again. + // Note that, since the Messages channel is buffered, the actual grace time is + // (MaxProcessingTime * ChanneBufferSize). Defaults to 100ms. + // If a message is not written to the Messages channel between two ticks + // of the expiryTicker then a timeout is detected. + // Using a ticker instead of a timer to detect timeouts should typically + // result in many fewer calls to Timer functions which may result in a + // significant performance improvement if many messages are being sent + // and timeouts are infrequent. + // The disadvantage of using a ticker instead of a timer is that + // timeouts will be less accurate. That is, the effective timeout could + // be between `MaxProcessingTime` and `2 * MaxProcessingTime`. For + // example, if `MaxProcessingTime` is 100ms then a delay of 180ms + // between two messages being sent may not be recognized as a timeout. + MaxProcessingTime time.Duration + + // Return specifies what channels will be populated. If they are set to true, + // you must read from them to prevent deadlock. + Return struct { + // If enabled, any errors that occurred while consuming are returned on + // the Errors channel (default disabled). + Errors bool + } + + // Offsets specifies configuration for how and when to commit consumed + // offsets. This currently requires the manual use of an OffsetManager + // but will eventually be automated. + Offsets struct { + // How frequently to commit updated offsets. Defaults to 1s. + CommitInterval time.Duration + + // The initial offset to use if no offset was previously committed. + // Should be OffsetNewest or OffsetOldest. Defaults to OffsetNewest. + Initial int64 + + // The retention duration for committed offsets. If zero, disabled + // (in which case the `offsets.retention.minutes` option on the + // broker will be used). Kafka only supports precision up to + // milliseconds; nanoseconds will be truncated. Requires Kafka + // broker version 0.9.0 or later. + // (default is 0: disabled). + Retention time.Duration + + Retry struct { + // The total number of times to retry failing commit + // requests during OffsetManager shutdown (default 3). + Max int + } + } + } + + // A user-provided string sent with every request to the brokers for logging, + // debugging, and auditing purposes. Defaults to "sarama", but you should + // probably set it to something specific to your application. + ClientID string + // The number of events to buffer in internal and external channels. This + // permits the producer and consumer to continue processing some messages + // in the background while user code is working, greatly improving throughput. + // Defaults to 256. + ChannelBufferSize int + // The version of Kafka that Sarama will assume it is running against. + // Defaults to the oldest supported stable version. Since Kafka provides + // backwards-compatibility, setting it to a version older than you have + // will not break anything, although it may prevent you from using the + // latest features. Setting it to a version greater than you are actually + // running may lead to random breakage. + Version KafkaVersion + // The registry to define metrics into. + // Defaults to a local registry. + // If you want to disable metrics gathering, set "metrics.UseNilMetrics" to "true" + // prior to starting Sarama. + // See Examples on how to use the metrics registry + MetricRegistry metrics.Registry +} + +// NewConfig returns a new configuration instance with sane defaults. +func NewConfig() *Config { + c := &Config{} + + c.Admin.Timeout = 3 * time.Second + + c.Net.MaxOpenRequests = 5 + c.Net.DialTimeout = 30 * time.Second + c.Net.ReadTimeout = 30 * time.Second + c.Net.WriteTimeout = 30 * time.Second + c.Net.SASL.Handshake = true + + c.Metadata.Retry.Max = 3 + c.Metadata.Retry.Backoff = 250 * time.Millisecond + c.Metadata.RefreshFrequency = 10 * time.Minute + c.Metadata.Full = true + + c.Producer.MaxMessageBytes = 1000000 + c.Producer.RequiredAcks = WaitForLocal + c.Producer.Timeout = 10 * time.Second + c.Producer.Partitioner = NewHashPartitioner + c.Producer.Retry.Max = 3 + c.Producer.Retry.Backoff = 100 * time.Millisecond + c.Producer.Return.Errors = true + c.Producer.CompressionLevel = CompressionLevelDefault + + c.Consumer.Fetch.Min = 1 + c.Consumer.Fetch.Default = 1024 * 1024 + c.Consumer.Retry.Backoff = 2 * time.Second + c.Consumer.MaxWaitTime = 250 * time.Millisecond + c.Consumer.MaxProcessingTime = 100 * time.Millisecond + c.Consumer.Return.Errors = false + c.Consumer.Offsets.CommitInterval = 1 * time.Second + c.Consumer.Offsets.Initial = OffsetNewest + c.Consumer.Offsets.Retry.Max = 3 + + c.Consumer.Group.Session.Timeout = 10 * time.Second + c.Consumer.Group.Heartbeat.Interval = 3 * time.Second + c.Consumer.Group.Rebalance.Strategy = BalanceStrategyRange + c.Consumer.Group.Rebalance.Timeout = 60 * time.Second + c.Consumer.Group.Rebalance.Retry.Max = 4 + c.Consumer.Group.Rebalance.Retry.Backoff = 2 * time.Second + + c.ClientID = defaultClientID + c.ChannelBufferSize = 256 + c.Version = MinVersion + c.MetricRegistry = metrics.NewRegistry() + + return c +} + +// Validate checks a Config instance. It will return a +// ConfigurationError if the specified values don't make sense. +func (c *Config) Validate() error { + // some configuration values should be warned on but not fail completely, do those first + if c.Net.TLS.Enable == false && c.Net.TLS.Config != nil { + Logger.Println("Net.TLS is disabled but a non-nil configuration was provided.") + } + if c.Net.SASL.Enable == false { + if c.Net.SASL.User != "" { + Logger.Println("Net.SASL is disabled but a non-empty username was provided.") + } + if c.Net.SASL.Password != "" { + Logger.Println("Net.SASL is disabled but a non-empty password was provided.") + } + } + if c.Producer.RequiredAcks > 1 { + Logger.Println("Producer.RequiredAcks > 1 is deprecated and will raise an exception with kafka >= 0.8.2.0.") + } + if c.Producer.MaxMessageBytes >= int(MaxRequestSize) { + Logger.Println("Producer.MaxMessageBytes must be smaller than MaxRequestSize; it will be ignored.") + } + if c.Producer.Flush.Bytes >= int(MaxRequestSize) { + Logger.Println("Producer.Flush.Bytes must be smaller than MaxRequestSize; it will be ignored.") + } + if (c.Producer.Flush.Bytes > 0 || c.Producer.Flush.Messages > 0) && c.Producer.Flush.Frequency == 0 { + Logger.Println("Producer.Flush: Bytes or Messages are set, but Frequency is not; messages may not get flushed.") + } + if c.Producer.Timeout%time.Millisecond != 0 { + Logger.Println("Producer.Timeout only supports millisecond resolution; nanoseconds will be truncated.") + } + if c.Consumer.MaxWaitTime < 100*time.Millisecond { + Logger.Println("Consumer.MaxWaitTime is very low, which can cause high CPU and network usage. See documentation for details.") + } + if c.Consumer.MaxWaitTime%time.Millisecond != 0 { + Logger.Println("Consumer.MaxWaitTime only supports millisecond precision; nanoseconds will be truncated.") + } + if c.Consumer.Offsets.Retention%time.Millisecond != 0 { + Logger.Println("Consumer.Offsets.Retention only supports millisecond precision; nanoseconds will be truncated.") + } + if c.Consumer.Group.Session.Timeout%time.Millisecond != 0 { + Logger.Println("Consumer.Group.Session.Timeout only supports millisecond precision; nanoseconds will be truncated.") + } + if c.Consumer.Group.Heartbeat.Interval%time.Millisecond != 0 { + Logger.Println("Consumer.Group.Heartbeat.Interval only supports millisecond precision; nanoseconds will be truncated.") + } + if c.Consumer.Group.Rebalance.Timeout%time.Millisecond != 0 { + Logger.Println("Consumer.Group.Rebalance.Timeout only supports millisecond precision; nanoseconds will be truncated.") + } + if c.ClientID == defaultClientID { + Logger.Println("ClientID is the default of 'sarama', you should consider setting it to something application-specific.") + } + + // validate Net values + switch { + case c.Net.MaxOpenRequests <= 0: + return ConfigurationError("Net.MaxOpenRequests must be > 0") + case c.Net.DialTimeout <= 0: + return ConfigurationError("Net.DialTimeout must be > 0") + case c.Net.ReadTimeout <= 0: + return ConfigurationError("Net.ReadTimeout must be > 0") + case c.Net.WriteTimeout <= 0: + return ConfigurationError("Net.WriteTimeout must be > 0") + case c.Net.KeepAlive < 0: + return ConfigurationError("Net.KeepAlive must be >= 0") + case c.Net.SASL.Enable == true && c.Net.SASL.User == "": + return ConfigurationError("Net.SASL.User must not be empty when SASL is enabled") + case c.Net.SASL.Enable == true && c.Net.SASL.Password == "": + return ConfigurationError("Net.SASL.Password must not be empty when SASL is enabled") + } + + // validate the Admin values + switch { + case c.Admin.Timeout <= 0: + return ConfigurationError("Admin.Timeout must be > 0") + } + + // validate the Metadata values + switch { + case c.Metadata.Retry.Max < 0: + return ConfigurationError("Metadata.Retry.Max must be >= 0") + case c.Metadata.Retry.Backoff < 0: + return ConfigurationError("Metadata.Retry.Backoff must be >= 0") + case c.Metadata.RefreshFrequency < 0: + return ConfigurationError("Metadata.RefreshFrequency must be >= 0") + } + + // validate the Producer values + switch { + case c.Producer.MaxMessageBytes <= 0: + return ConfigurationError("Producer.MaxMessageBytes must be > 0") + case c.Producer.RequiredAcks < -1: + return ConfigurationError("Producer.RequiredAcks must be >= -1") + case c.Producer.Timeout <= 0: + return ConfigurationError("Producer.Timeout must be > 0") + case c.Producer.Partitioner == nil: + return ConfigurationError("Producer.Partitioner must not be nil") + case c.Producer.Flush.Bytes < 0: + return ConfigurationError("Producer.Flush.Bytes must be >= 0") + case c.Producer.Flush.Messages < 0: + return ConfigurationError("Producer.Flush.Messages must be >= 0") + case c.Producer.Flush.Frequency < 0: + return ConfigurationError("Producer.Flush.Frequency must be >= 0") + case c.Producer.Flush.MaxMessages < 0: + return ConfigurationError("Producer.Flush.MaxMessages must be >= 0") + case c.Producer.Flush.MaxMessages > 0 && c.Producer.Flush.MaxMessages < c.Producer.Flush.Messages: + return ConfigurationError("Producer.Flush.MaxMessages must be >= Producer.Flush.Messages when set") + case c.Producer.Retry.Max < 0: + return ConfigurationError("Producer.Retry.Max must be >= 0") + case c.Producer.Retry.Backoff < 0: + return ConfigurationError("Producer.Retry.Backoff must be >= 0") + } + + if c.Producer.Compression == CompressionLZ4 && !c.Version.IsAtLeast(V0_10_0_0) { + return ConfigurationError("lz4 compression requires Version >= V0_10_0_0") + } + + if c.Producer.Compression == CompressionGZIP { + if c.Producer.CompressionLevel != CompressionLevelDefault { + if _, err := gzip.NewWriterLevel(ioutil.Discard, c.Producer.CompressionLevel); err != nil { + return ConfigurationError(fmt.Sprintf("gzip compression does not work with level %d: %v", c.Producer.CompressionLevel, err)) + } + } + } + + // validate the Consumer values + switch { + case c.Consumer.Fetch.Min <= 0: + return ConfigurationError("Consumer.Fetch.Min must be > 0") + case c.Consumer.Fetch.Default <= 0: + return ConfigurationError("Consumer.Fetch.Default must be > 0") + case c.Consumer.Fetch.Max < 0: + return ConfigurationError("Consumer.Fetch.Max must be >= 0") + case c.Consumer.MaxWaitTime < 1*time.Millisecond: + return ConfigurationError("Consumer.MaxWaitTime must be >= 1ms") + case c.Consumer.MaxProcessingTime <= 0: + return ConfigurationError("Consumer.MaxProcessingTime must be > 0") + case c.Consumer.Retry.Backoff < 0: + return ConfigurationError("Consumer.Retry.Backoff must be >= 0") + case c.Consumer.Offsets.CommitInterval <= 0: + return ConfigurationError("Consumer.Offsets.CommitInterval must be > 0") + case c.Consumer.Offsets.Initial != OffsetOldest && c.Consumer.Offsets.Initial != OffsetNewest: + return ConfigurationError("Consumer.Offsets.Initial must be OffsetOldest or OffsetNewest") + case c.Consumer.Offsets.Retry.Max < 0: + return ConfigurationError("Consumer.Offsets.Retry.Max must be >= 0") + } + + // validate the Consumer Group values + switch { + case c.Consumer.Group.Session.Timeout <= 2*time.Millisecond: + return ConfigurationError("Consumer.Group.Session.Timeout must be >= 2ms") + case c.Consumer.Group.Heartbeat.Interval < 1*time.Millisecond: + return ConfigurationError("Consumer.Group.Heartbeat.Interval must be >= 1ms") + case c.Consumer.Group.Heartbeat.Interval >= c.Consumer.Group.Session.Timeout: + return ConfigurationError("Consumer.Group.Heartbeat.Interval must be < Consumer.Group.Session.Timeout") + case c.Consumer.Group.Rebalance.Strategy == nil: + return ConfigurationError("Consumer.Group.Rebalance.Strategy must not be empty") + case c.Consumer.Group.Rebalance.Timeout <= time.Millisecond: + return ConfigurationError("Consumer.Group.Rebalance.Timeout must be >= 1ms") + case c.Consumer.Group.Rebalance.Retry.Max < 0: + return ConfigurationError("Consumer.Group.Rebalance.Retry.Max must be >= 0") + case c.Consumer.Group.Rebalance.Retry.Backoff < 0: + return ConfigurationError("Consumer.Group.Rebalance.Retry.Backoff must be >= 0") + } + + // validate misc shared values + switch { + case c.ChannelBufferSize < 0: + return ConfigurationError("ChannelBufferSize must be >= 0") + case !validID.MatchString(c.ClientID): + return ConfigurationError("ClientID is invalid") + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/config_resource_type.go b/vendor/github.com/Shopify/sarama/config_resource_type.go new file mode 100644 index 00000000000..848cc9c90c5 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/config_resource_type.go @@ -0,0 +1,15 @@ +package sarama + +type ConfigResourceType int8 + +// Taken from : +// https://cwiki.apache.org/confluence/display/KAFKA/KIP-133%3A+Describe+and+Alter+Configs+Admin+APIs#KIP-133:DescribeandAlterConfigsAdminAPIs-WireFormattypes + +const ( + UnknownResource ConfigResourceType = 0 + AnyResource ConfigResourceType = 1 + TopicResource ConfigResourceType = 2 + GroupResource ConfigResourceType = 3 + ClusterResource ConfigResourceType = 4 + BrokerResource ConfigResourceType = 5 +) diff --git a/vendor/github.com/Shopify/sarama/consumer.go b/vendor/github.com/Shopify/sarama/consumer.go new file mode 100644 index 00000000000..33d9d143f91 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/consumer.go @@ -0,0 +1,807 @@ +package sarama + +import ( + "errors" + "fmt" + "sync" + "sync/atomic" + "time" +) + +// ConsumerMessage encapsulates a Kafka message returned by the consumer. +type ConsumerMessage struct { + Key, Value []byte + Topic string + Partition int32 + Offset int64 + Timestamp time.Time // only set if kafka is version 0.10+, inner message timestamp + BlockTimestamp time.Time // only set if kafka is version 0.10+, outer (compressed) block timestamp + Headers []*RecordHeader // only set if kafka is version 0.11+ +} + +// ConsumerError is what is provided to the user when an error occurs. +// It wraps an error and includes the topic and partition. +type ConsumerError struct { + Topic string + Partition int32 + Err error +} + +func (ce ConsumerError) Error() string { + return fmt.Sprintf("kafka: error while consuming %s/%d: %s", ce.Topic, ce.Partition, ce.Err) +} + +// ConsumerErrors is a type that wraps a batch of errors and implements the Error interface. +// It can be returned from the PartitionConsumer's Close methods to avoid the need to manually drain errors +// when stopping. +type ConsumerErrors []*ConsumerError + +func (ce ConsumerErrors) Error() string { + return fmt.Sprintf("kafka: %d errors while consuming", len(ce)) +} + +// Consumer manages PartitionConsumers which process Kafka messages from brokers. You MUST call Close() +// on a consumer to avoid leaks, it will not be garbage-collected automatically when it passes out of +// scope. +// +// Sarama's Consumer type does not currently support automatic consumer-group rebalancing and offset tracking. +// For Zookeeper-based tracking (Kafka 0.8.2 and earlier), the https://github.com/wvanbergen/kafka library +// builds on Sarama to add this support. For Kafka-based tracking (Kafka 0.9 and later), the +// https://github.com/bsm/sarama-cluster library builds on Sarama to add this support. +type Consumer interface { + + // Topics returns the set of available topics as retrieved from the cluster + // metadata. This method is the same as Client.Topics(), and is provided for + // convenience. + Topics() ([]string, error) + + // Partitions returns the sorted list of all partition IDs for the given topic. + // This method is the same as Client.Partitions(), and is provided for convenience. + Partitions(topic string) ([]int32, error) + + // ConsumePartition creates a PartitionConsumer on the given topic/partition with + // the given offset. It will return an error if this Consumer is already consuming + // on the given topic/partition. Offset can be a literal offset, or OffsetNewest + // or OffsetOldest + ConsumePartition(topic string, partition int32, offset int64) (PartitionConsumer, error) + + // HighWaterMarks returns the current high water marks for each topic and partition. + // Consistency between partitions is not guaranteed since high water marks are updated separately. + HighWaterMarks() map[string]map[int32]int64 + + // Close shuts down the consumer. It must be called after all child + // PartitionConsumers have already been closed. + Close() error +} + +type consumer struct { + client Client + conf *Config + ownClient bool + + lock sync.Mutex + children map[string]map[int32]*partitionConsumer + brokerConsumers map[*Broker]*brokerConsumer +} + +// NewConsumer creates a new consumer using the given broker addresses and configuration. +func NewConsumer(addrs []string, config *Config) (Consumer, error) { + client, err := NewClient(addrs, config) + if err != nil { + return nil, err + } + + c, err := NewConsumerFromClient(client) + if err != nil { + return nil, err + } + c.(*consumer).ownClient = true + return c, nil +} + +// NewConsumerFromClient creates a new consumer using the given client. It is still +// necessary to call Close() on the underlying client when shutting down this consumer. +func NewConsumerFromClient(client Client) (Consumer, error) { + // Check that we are not dealing with a closed Client before processing any other arguments + if client.Closed() { + return nil, ErrClosedClient + } + + c := &consumer{ + client: client, + conf: client.Config(), + children: make(map[string]map[int32]*partitionConsumer), + brokerConsumers: make(map[*Broker]*brokerConsumer), + } + + return c, nil +} + +func (c *consumer) Close() error { + if c.ownClient { + return c.client.Close() + } + return nil +} + +func (c *consumer) Topics() ([]string, error) { + return c.client.Topics() +} + +func (c *consumer) Partitions(topic string) ([]int32, error) { + return c.client.Partitions(topic) +} + +func (c *consumer) ConsumePartition(topic string, partition int32, offset int64) (PartitionConsumer, error) { + child := &partitionConsumer{ + consumer: c, + conf: c.conf, + topic: topic, + partition: partition, + messages: make(chan *ConsumerMessage, c.conf.ChannelBufferSize), + errors: make(chan *ConsumerError, c.conf.ChannelBufferSize), + feeder: make(chan *FetchResponse, 1), + trigger: make(chan none, 1), + dying: make(chan none), + fetchSize: c.conf.Consumer.Fetch.Default, + } + + if err := child.chooseStartingOffset(offset); err != nil { + return nil, err + } + + var leader *Broker + var err error + if leader, err = c.client.Leader(child.topic, child.partition); err != nil { + return nil, err + } + + if err := c.addChild(child); err != nil { + return nil, err + } + + go withRecover(child.dispatcher) + go withRecover(child.responseFeeder) + + child.broker = c.refBrokerConsumer(leader) + child.broker.input <- child + + return child, nil +} + +func (c *consumer) HighWaterMarks() map[string]map[int32]int64 { + c.lock.Lock() + defer c.lock.Unlock() + + hwms := make(map[string]map[int32]int64) + for topic, p := range c.children { + hwm := make(map[int32]int64, len(p)) + for partition, pc := range p { + hwm[partition] = pc.HighWaterMarkOffset() + } + hwms[topic] = hwm + } + + return hwms +} + +func (c *consumer) addChild(child *partitionConsumer) error { + c.lock.Lock() + defer c.lock.Unlock() + + topicChildren := c.children[child.topic] + if topicChildren == nil { + topicChildren = make(map[int32]*partitionConsumer) + c.children[child.topic] = topicChildren + } + + if topicChildren[child.partition] != nil { + return ConfigurationError("That topic/partition is already being consumed") + } + + topicChildren[child.partition] = child + return nil +} + +func (c *consumer) removeChild(child *partitionConsumer) { + c.lock.Lock() + defer c.lock.Unlock() + + delete(c.children[child.topic], child.partition) +} + +func (c *consumer) refBrokerConsumer(broker *Broker) *brokerConsumer { + c.lock.Lock() + defer c.lock.Unlock() + + bc := c.brokerConsumers[broker] + if bc == nil { + bc = c.newBrokerConsumer(broker) + c.brokerConsumers[broker] = bc + } + + bc.refs++ + + return bc +} + +func (c *consumer) unrefBrokerConsumer(brokerWorker *brokerConsumer) { + c.lock.Lock() + defer c.lock.Unlock() + + brokerWorker.refs-- + + if brokerWorker.refs == 0 { + close(brokerWorker.input) + if c.brokerConsumers[brokerWorker.broker] == brokerWorker { + delete(c.brokerConsumers, brokerWorker.broker) + } + } +} + +func (c *consumer) abandonBrokerConsumer(brokerWorker *brokerConsumer) { + c.lock.Lock() + defer c.lock.Unlock() + + delete(c.brokerConsumers, brokerWorker.broker) +} + +// PartitionConsumer + +// PartitionConsumer processes Kafka messages from a given topic and partition. You MUST call one of Close() or +// AsyncClose() on a PartitionConsumer to avoid leaks; it will not be garbage-collected automatically when it passes out +// of scope. +// +// The simplest way of using a PartitionConsumer is to loop over its Messages channel using a for/range +// loop. The PartitionConsumer will only stop itself in one case: when the offset being consumed is reported +// as out of range by the brokers. In this case you should decide what you want to do (try a different offset, +// notify a human, etc) and handle it appropriately. For all other error cases, it will just keep retrying. +// By default, it logs these errors to sarama.Logger; if you want to be notified directly of all errors, set +// your config's Consumer.Return.Errors to true and read from the Errors channel, using a select statement +// or a separate goroutine. Check out the Consumer examples to see implementations of these different approaches. +// +// To terminate such a for/range loop while the loop is executing, call AsyncClose. This will kick off the process of +// consumer tear-down & return imediately. Continue to loop, servicing the Messages channel until the teardown process +// AsyncClose initiated closes it (thus terminating the for/range loop). If you've already ceased reading Messages, call +// Close; this will signal the PartitionConsumer's goroutines to begin shutting down (just like AsyncClose), but will +// also drain the Messages channel, harvest all errors & return them once cleanup has completed. +type PartitionConsumer interface { + + // AsyncClose initiates a shutdown of the PartitionConsumer. This method will return immediately, after which you + // should continue to service the 'Messages' and 'Errors' channels until they are empty. It is required to call this + // function, or Close before a consumer object passes out of scope, as it will otherwise leak memory. You must call + // this before calling Close on the underlying client. + AsyncClose() + + // Close stops the PartitionConsumer from fetching messages. It will initiate a shutdown just like AsyncClose, drain + // the Messages channel, harvest any errors & return them to the caller. Note that if you are continuing to service + // the Messages channel when this function is called, you will be competing with Close for messages; consider + // calling AsyncClose, instead. It is required to call this function (or AsyncClose) before a consumer object passes + // out of scope, as it will otherwise leak memory. You must call this before calling Close on the underlying client. + Close() error + + // Messages returns the read channel for the messages that are returned by + // the broker. + Messages() <-chan *ConsumerMessage + + // Errors returns a read channel of errors that occurred during consuming, if + // enabled. By default, errors are logged and not returned over this channel. + // If you want to implement any custom error handling, set your config's + // Consumer.Return.Errors setting to true, and read from this channel. + Errors() <-chan *ConsumerError + + // HighWaterMarkOffset returns the high water mark offset of the partition, + // i.e. the offset that will be used for the next message that will be produced. + // You can use this to determine how far behind the processing is. + HighWaterMarkOffset() int64 +} + +type partitionConsumer struct { + highWaterMarkOffset int64 // must be at the top of the struct because https://golang.org/pkg/sync/atomic/#pkg-note-BUG + consumer *consumer + conf *Config + topic string + partition int32 + + broker *brokerConsumer + messages chan *ConsumerMessage + errors chan *ConsumerError + feeder chan *FetchResponse + + trigger, dying chan none + responseResult error + closeOnce sync.Once + + fetchSize int32 + offset int64 +} + +var errTimedOut = errors.New("timed out feeding messages to the user") // not user-facing + +func (child *partitionConsumer) sendError(err error) { + cErr := &ConsumerError{ + Topic: child.topic, + Partition: child.partition, + Err: err, + } + + if child.conf.Consumer.Return.Errors { + child.errors <- cErr + } else { + Logger.Println(cErr) + } +} + +func (child *partitionConsumer) dispatcher() { + for range child.trigger { + select { + case <-child.dying: + close(child.trigger) + case <-time.After(child.conf.Consumer.Retry.Backoff): + if child.broker != nil { + child.consumer.unrefBrokerConsumer(child.broker) + child.broker = nil + } + + Logger.Printf("consumer/%s/%d finding new broker\n", child.topic, child.partition) + if err := child.dispatch(); err != nil { + child.sendError(err) + child.trigger <- none{} + } + } + } + + if child.broker != nil { + child.consumer.unrefBrokerConsumer(child.broker) + } + child.consumer.removeChild(child) + close(child.feeder) +} + +func (child *partitionConsumer) dispatch() error { + if err := child.consumer.client.RefreshMetadata(child.topic); err != nil { + return err + } + + var leader *Broker + var err error + if leader, err = child.consumer.client.Leader(child.topic, child.partition); err != nil { + return err + } + + child.broker = child.consumer.refBrokerConsumer(leader) + + child.broker.input <- child + + return nil +} + +func (child *partitionConsumer) chooseStartingOffset(offset int64) error { + newestOffset, err := child.consumer.client.GetOffset(child.topic, child.partition, OffsetNewest) + if err != nil { + return err + } + oldestOffset, err := child.consumer.client.GetOffset(child.topic, child.partition, OffsetOldest) + if err != nil { + return err + } + + switch { + case offset == OffsetNewest: + child.offset = newestOffset + case offset == OffsetOldest: + child.offset = oldestOffset + case offset >= oldestOffset && offset <= newestOffset: + child.offset = offset + default: + return ErrOffsetOutOfRange + } + + return nil +} + +func (child *partitionConsumer) Messages() <-chan *ConsumerMessage { + return child.messages +} + +func (child *partitionConsumer) Errors() <-chan *ConsumerError { + return child.errors +} + +func (child *partitionConsumer) AsyncClose() { + // this triggers whatever broker owns this child to abandon it and close its trigger channel, which causes + // the dispatcher to exit its loop, which removes it from the consumer then closes its 'messages' and + // 'errors' channel (alternatively, if the child is already at the dispatcher for some reason, that will + // also just close itself) + child.closeOnce.Do(func() { + close(child.dying) + }) +} + +func (child *partitionConsumer) Close() error { + child.AsyncClose() + + go withRecover(func() { + for range child.messages { + // drain + } + }) + + var errors ConsumerErrors + for err := range child.errors { + errors = append(errors, err) + } + + if len(errors) > 0 { + return errors + } + return nil +} + +func (child *partitionConsumer) HighWaterMarkOffset() int64 { + return atomic.LoadInt64(&child.highWaterMarkOffset) +} + +func (child *partitionConsumer) responseFeeder() { + var msgs []*ConsumerMessage + expiryTicker := time.NewTicker(child.conf.Consumer.MaxProcessingTime) + firstAttempt := true + +feederLoop: + for response := range child.feeder { + msgs, child.responseResult = child.parseResponse(response) + + for i, msg := range msgs { + messageSelect: + select { + case child.messages <- msg: + firstAttempt = true + case <-expiryTicker.C: + if !firstAttempt { + child.responseResult = errTimedOut + child.broker.acks.Done() + for _, msg = range msgs[i:] { + child.messages <- msg + } + child.broker.input <- child + continue feederLoop + } else { + // current message has not been sent, return to select + // statement + firstAttempt = false + goto messageSelect + } + } + } + + child.broker.acks.Done() + } + + expiryTicker.Stop() + close(child.messages) + close(child.errors) +} + +func (child *partitionConsumer) parseMessages(msgSet *MessageSet) ([]*ConsumerMessage, error) { + var messages []*ConsumerMessage + for _, msgBlock := range msgSet.Messages { + for _, msg := range msgBlock.Messages() { + offset := msg.Offset + if msg.Msg.Version >= 1 { + baseOffset := msgBlock.Offset - msgBlock.Messages()[len(msgBlock.Messages())-1].Offset + offset += baseOffset + } + if offset < child.offset { + continue + } + messages = append(messages, &ConsumerMessage{ + Topic: child.topic, + Partition: child.partition, + Key: msg.Msg.Key, + Value: msg.Msg.Value, + Offset: offset, + Timestamp: msg.Msg.Timestamp, + BlockTimestamp: msgBlock.Msg.Timestamp, + }) + child.offset = offset + 1 + } + } + if len(messages) == 0 { + return nil, ErrIncompleteResponse + } + return messages, nil +} + +func (child *partitionConsumer) parseRecords(batch *RecordBatch) ([]*ConsumerMessage, error) { + var messages []*ConsumerMessage + for _, rec := range batch.Records { + offset := batch.FirstOffset + rec.OffsetDelta + if offset < child.offset { + continue + } + messages = append(messages, &ConsumerMessage{ + Topic: child.topic, + Partition: child.partition, + Key: rec.Key, + Value: rec.Value, + Offset: offset, + Timestamp: batch.FirstTimestamp.Add(rec.TimestampDelta), + Headers: rec.Headers, + }) + child.offset = offset + 1 + } + if len(messages) == 0 { + child.offset += 1 + } + return messages, nil +} + +func (child *partitionConsumer) parseResponse(response *FetchResponse) ([]*ConsumerMessage, error) { + block := response.GetBlock(child.topic, child.partition) + if block == nil { + return nil, ErrIncompleteResponse + } + + if block.Err != ErrNoError { + return nil, block.Err + } + + nRecs, err := block.numRecords() + if err != nil { + return nil, err + } + if nRecs == 0 { + partialTrailingMessage, err := block.isPartial() + if err != nil { + return nil, err + } + // We got no messages. If we got a trailing one then we need to ask for more data. + // Otherwise we just poll again and wait for one to be produced... + if partialTrailingMessage { + if child.conf.Consumer.Fetch.Max > 0 && child.fetchSize == child.conf.Consumer.Fetch.Max { + // we can't ask for more data, we've hit the configured limit + child.sendError(ErrMessageTooLarge) + child.offset++ // skip this one so we can keep processing future messages + } else { + child.fetchSize *= 2 + if child.conf.Consumer.Fetch.Max > 0 && child.fetchSize > child.conf.Consumer.Fetch.Max { + child.fetchSize = child.conf.Consumer.Fetch.Max + } + } + } + + return nil, nil + } + + // we got messages, reset our fetch size in case it was increased for a previous request + child.fetchSize = child.conf.Consumer.Fetch.Default + atomic.StoreInt64(&child.highWaterMarkOffset, block.HighWaterMarkOffset) + + messages := []*ConsumerMessage{} + for _, records := range block.RecordsSet { + switch records.recordsType { + case legacyRecords: + messageSetMessages, err := child.parseMessages(records.MsgSet) + if err != nil { + return nil, err + } + + messages = append(messages, messageSetMessages...) + case defaultRecords: + recordBatchMessages, err := child.parseRecords(records.RecordBatch) + if err != nil { + return nil, err + } + if control, err := records.isControl(); err != nil || control { + continue + } + + messages = append(messages, recordBatchMessages...) + default: + return nil, fmt.Errorf("unknown records type: %v", records.recordsType) + } + } + + return messages, nil +} + +// brokerConsumer + +type brokerConsumer struct { + consumer *consumer + broker *Broker + input chan *partitionConsumer + newSubscriptions chan []*partitionConsumer + wait chan none + subscriptions map[*partitionConsumer]none + acks sync.WaitGroup + refs int +} + +func (c *consumer) newBrokerConsumer(broker *Broker) *brokerConsumer { + bc := &brokerConsumer{ + consumer: c, + broker: broker, + input: make(chan *partitionConsumer), + newSubscriptions: make(chan []*partitionConsumer), + wait: make(chan none), + subscriptions: make(map[*partitionConsumer]none), + refs: 0, + } + + go withRecover(bc.subscriptionManager) + go withRecover(bc.subscriptionConsumer) + + return bc +} + +func (bc *brokerConsumer) subscriptionManager() { + var buffer []*partitionConsumer + + // The subscriptionManager constantly accepts new subscriptions on `input` (even when the main subscriptionConsumer + // goroutine is in the middle of a network request) and batches it up. The main worker goroutine picks + // up a batch of new subscriptions between every network request by reading from `newSubscriptions`, so we give + // it nil if no new subscriptions are available. We also write to `wait` only when new subscriptions is available, + // so the main goroutine can block waiting for work if it has none. + for { + if len(buffer) > 0 { + select { + case event, ok := <-bc.input: + if !ok { + goto done + } + buffer = append(buffer, event) + case bc.newSubscriptions <- buffer: + buffer = nil + case bc.wait <- none{}: + } + } else { + select { + case event, ok := <-bc.input: + if !ok { + goto done + } + buffer = append(buffer, event) + case bc.newSubscriptions <- nil: + } + } + } + +done: + close(bc.wait) + if len(buffer) > 0 { + bc.newSubscriptions <- buffer + } + close(bc.newSubscriptions) +} + +func (bc *brokerConsumer) subscriptionConsumer() { + <-bc.wait // wait for our first piece of work + + // the subscriptionConsumer ensures we will get nil right away if no new subscriptions is available + for newSubscriptions := range bc.newSubscriptions { + bc.updateSubscriptions(newSubscriptions) + + if len(bc.subscriptions) == 0 { + // We're about to be shut down or we're about to receive more subscriptions. + // Either way, the signal just hasn't propagated to our goroutine yet. + <-bc.wait + continue + } + + response, err := bc.fetchNewMessages() + + if err != nil { + Logger.Printf("consumer/broker/%d disconnecting due to error processing FetchRequest: %s\n", bc.broker.ID(), err) + bc.abort(err) + return + } + + bc.acks.Add(len(bc.subscriptions)) + for child := range bc.subscriptions { + child.feeder <- response + } + bc.acks.Wait() + bc.handleResponses() + } +} + +func (bc *brokerConsumer) updateSubscriptions(newSubscriptions []*partitionConsumer) { + for _, child := range newSubscriptions { + bc.subscriptions[child] = none{} + Logger.Printf("consumer/broker/%d added subscription to %s/%d\n", bc.broker.ID(), child.topic, child.partition) + } + + for child := range bc.subscriptions { + select { + case <-child.dying: + Logger.Printf("consumer/broker/%d closed dead subscription to %s/%d\n", bc.broker.ID(), child.topic, child.partition) + close(child.trigger) + delete(bc.subscriptions, child) + default: + break + } + } +} + +func (bc *brokerConsumer) handleResponses() { + // handles the response codes left for us by our subscriptions, and abandons ones that have been closed + for child := range bc.subscriptions { + result := child.responseResult + child.responseResult = nil + + switch result { + case nil: + break + case errTimedOut: + Logger.Printf("consumer/broker/%d abandoned subscription to %s/%d because consuming was taking too long\n", + bc.broker.ID(), child.topic, child.partition) + delete(bc.subscriptions, child) + case ErrOffsetOutOfRange: + // there's no point in retrying this it will just fail the same way again + // shut it down and force the user to choose what to do + child.sendError(result) + Logger.Printf("consumer/%s/%d shutting down because %s\n", child.topic, child.partition, result) + close(child.trigger) + delete(bc.subscriptions, child) + case ErrUnknownTopicOrPartition, ErrNotLeaderForPartition, ErrLeaderNotAvailable, ErrReplicaNotAvailable: + // not an error, but does need redispatching + Logger.Printf("consumer/broker/%d abandoned subscription to %s/%d because %s\n", + bc.broker.ID(), child.topic, child.partition, result) + child.trigger <- none{} + delete(bc.subscriptions, child) + default: + // dunno, tell the user and try redispatching + child.sendError(result) + Logger.Printf("consumer/broker/%d abandoned subscription to %s/%d because %s\n", + bc.broker.ID(), child.topic, child.partition, result) + child.trigger <- none{} + delete(bc.subscriptions, child) + } + } +} + +func (bc *brokerConsumer) abort(err error) { + bc.consumer.abandonBrokerConsumer(bc) + _ = bc.broker.Close() // we don't care about the error this might return, we already have one + + for child := range bc.subscriptions { + child.sendError(err) + child.trigger <- none{} + } + + for newSubscriptions := range bc.newSubscriptions { + if len(newSubscriptions) == 0 { + <-bc.wait + continue + } + for _, child := range newSubscriptions { + child.sendError(err) + child.trigger <- none{} + } + } +} + +func (bc *brokerConsumer) fetchNewMessages() (*FetchResponse, error) { + request := &FetchRequest{ + MinBytes: bc.consumer.conf.Consumer.Fetch.Min, + MaxWaitTime: int32(bc.consumer.conf.Consumer.MaxWaitTime / time.Millisecond), + } + if bc.consumer.conf.Version.IsAtLeast(V0_10_0_0) { + request.Version = 2 + } + if bc.consumer.conf.Version.IsAtLeast(V0_10_1_0) { + request.Version = 3 + request.MaxBytes = MaxResponseSize + } + if bc.consumer.conf.Version.IsAtLeast(V0_11_0_0) { + request.Version = 4 + request.Isolation = ReadUncommitted // We don't support yet transactions. + } + + for child := range bc.subscriptions { + request.AddBlock(child.topic, child.partition, child.offset, child.fetchSize) + } + + return bc.broker.Fetch(request) +} diff --git a/vendor/github.com/Shopify/sarama/consumer_group.go b/vendor/github.com/Shopify/sarama/consumer_group.go new file mode 100644 index 00000000000..33a231477f9 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/consumer_group.go @@ -0,0 +1,774 @@ +package sarama + +import ( + "context" + "errors" + "fmt" + "sort" + "sync" + "time" +) + +// ErrClosedConsumerGroup is the error returned when a method is called on a consumer group that has been closed. +var ErrClosedConsumerGroup = errors.New("kafka: tried to use a consumer group that was closed") + +// ConsumerGroup is responsible for dividing up processing of topics and partitions +// over a collection of processes (the members of the consumer group). +type ConsumerGroup interface { + // Consume joins a cluster of consumers for a given list of topics and + // starts a blocking ConsumerGroupSession through the ConsumerGroupHandler. + // + // The life-cycle of a session is represented by the following steps: + // + // 1. The consumers join the group (as explained in https://kafka.apache.org/documentation/#intro_consumers) + // and is assigned their "fair share" of partitions, aka 'claims'. + // 2. Before processing starts, the handler's Setup() hook is called to notify the user + // of the claims and allow any necessary preparation or alteration of state. + // 3. For each of the assigned claims the handler's ConsumeClaim() function is then called + // in a separate goroutine which requires it to be thread-safe. Any state must be carefully protected + // from concurrent reads/writes. + // 4. The session will persist until one of the ConsumeClaim() functions exits. This can be either when the + // parent context is cancelled or when a server-side rebalance cycle is initiated. + // 5. Once all the ConsumeClaim() loops have exited, the handler's Cleanup() hook is called + // to allow the user to perform any final tasks before a rebalance. + // 6. Finally, marked offsets are committed one last time before claims are released. + // + // Please note, that once a relance is triggered, sessions must be completed within + // Config.Consumer.Group.Rebalance.Timeout. This means that ConsumeClaim() functions must exit + // as quickly as possible to allow time for Cleanup() and the final offset commit. If the timeout + // is exceeded, the consumer will be removed from the group by Kafka, which will cause offset + // commit failures. + Consume(ctx context.Context, topics []string, handler ConsumerGroupHandler) error + + // Errors returns a read channel of errors that occurred during the consumer life-cycle. + // By default, errors are logged and not returned over this channel. + // If you want to implement any custom error handling, set your config's + // Consumer.Return.Errors setting to true, and read from this channel. + Errors() <-chan error + + // Close stops the ConsumerGroup and detaches any running sessions. It is required to call + // this function before the object passes out of scope, as it will otherwise leak memory. + Close() error +} + +type consumerGroup struct { + client Client + ownClient bool + + config *Config + consumer Consumer + groupID string + memberID string + errors chan error + + lock sync.Mutex + closed chan none + closeOnce sync.Once +} + +// NewConsumerGroup creates a new consumer group the given broker addresses and configuration. +func NewConsumerGroup(addrs []string, groupID string, config *Config) (ConsumerGroup, error) { + client, err := NewClient(addrs, config) + if err != nil { + return nil, err + } + + c, err := NewConsumerGroupFromClient(groupID, client) + if err != nil { + _ = client.Close() + return nil, err + } + + c.(*consumerGroup).ownClient = true + return c, nil +} + +// NewConsumerFromClient creates a new consumer group using the given client. It is still +// necessary to call Close() on the underlying client when shutting down this consumer. +// PLEASE NOTE: consumer groups can only re-use but not share clients. +func NewConsumerGroupFromClient(groupID string, client Client) (ConsumerGroup, error) { + config := client.Config() + if !config.Version.IsAtLeast(V0_10_2_0) { + return nil, ConfigurationError("consumer groups require Version to be >= V0_10_2_0") + } + + consumer, err := NewConsumerFromClient(client) + if err != nil { + return nil, err + } + + return &consumerGroup{ + client: client, + consumer: consumer, + config: config, + groupID: groupID, + errors: make(chan error, config.ChannelBufferSize), + closed: make(chan none), + }, nil +} + +// Errors implements ConsumerGroup. +func (c *consumerGroup) Errors() <-chan error { return c.errors } + +// Close implements ConsumerGroup. +func (c *consumerGroup) Close() (err error) { + c.closeOnce.Do(func() { + close(c.closed) + + c.lock.Lock() + defer c.lock.Unlock() + + // leave group + if e := c.leave(); e != nil { + err = e + } + + // drain errors + go func() { + close(c.errors) + }() + for e := range c.errors { + err = e + } + + if c.ownClient { + if e := c.client.Close(); e != nil { + err = e + } + } + }) + return +} + +// Consume implements ConsumerGroup. +func (c *consumerGroup) Consume(ctx context.Context, topics []string, handler ConsumerGroupHandler) error { + // Ensure group is not closed + select { + case <-c.closed: + return ErrClosedConsumerGroup + default: + } + + c.lock.Lock() + defer c.lock.Unlock() + + // Quick exit when no topics are provided + if len(topics) == 0 { + return fmt.Errorf("no topics provided") + } + + // Refresh metadata for requested topics + if err := c.client.RefreshMetadata(topics...); err != nil { + return err + } + + // Get coordinator + coordinator, err := c.client.Coordinator(c.groupID) + if err != nil { + return err + } + + // Init session + sess, err := c.newSession(ctx, coordinator, topics, handler, c.config.Consumer.Group.Rebalance.Retry.Max) + if err == ErrClosedClient { + return ErrClosedConsumerGroup + } else if err != nil { + return err + } + + // Wait for session exit signal + <-sess.ctx.Done() + + // Gracefully release session claims + return sess.release(true) +} + +func (c *consumerGroup) newSession(ctx context.Context, coordinator *Broker, topics []string, handler ConsumerGroupHandler, retries int) (*consumerGroupSession, error) { + // Join consumer group + join, err := c.joinGroupRequest(coordinator, topics) + if err != nil { + _ = coordinator.Close() + return nil, err + } + switch join.Err { + case ErrNoError: + c.memberID = join.MemberId + case ErrUnknownMemberId, ErrIllegalGeneration: // reset member ID and retry immediately + c.memberID = "" + return c.newSession(ctx, coordinator, topics, handler, retries) + case ErrRebalanceInProgress: // retry after backoff + if retries <= 0 { + return nil, join.Err + } + + select { + case <-c.closed: + return nil, ErrClosedConsumerGroup + case <-time.After(c.config.Consumer.Group.Rebalance.Retry.Backoff): + } + + return c.newSession(ctx, coordinator, topics, handler, retries-1) + default: + return nil, join.Err + } + + // Prepare distribution plan if we joined as the leader + var plan BalanceStrategyPlan + if join.LeaderId == join.MemberId { + members, err := join.GetMembers() + if err != nil { + return nil, err + } + + plan, err = c.balance(members) + if err != nil { + return nil, err + } + } + + // Sync consumer group + sync, err := c.syncGroupRequest(coordinator, plan, join.GenerationId) + if err != nil { + _ = coordinator.Close() + return nil, err + } + switch sync.Err { + case ErrNoError: + case ErrUnknownMemberId, ErrIllegalGeneration: // reset member ID and retry immediately + c.memberID = "" + return c.newSession(ctx, coordinator, topics, handler, retries) + case ErrRebalanceInProgress: // retry after backoff + if retries <= 0 { + return nil, sync.Err + } + + select { + case <-c.closed: + return nil, ErrClosedConsumerGroup + case <-time.After(c.config.Consumer.Group.Rebalance.Retry.Backoff): + } + + return c.newSession(ctx, coordinator, topics, handler, retries-1) + default: + return nil, sync.Err + } + + // Retrieve and sort claims + var claims map[string][]int32 + if len(sync.MemberAssignment) > 0 { + members, err := sync.GetMemberAssignment() + if err != nil { + return nil, err + } + claims = members.Topics + + for _, partitions := range claims { + sort.Sort(int32Slice(partitions)) + } + } + + return newConsumerGroupSession(c, ctx, claims, join.MemberId, join.GenerationId, handler) +} + +func (c *consumerGroup) joinGroupRequest(coordinator *Broker, topics []string) (*JoinGroupResponse, error) { + req := &JoinGroupRequest{ + GroupId: c.groupID, + MemberId: c.memberID, + SessionTimeout: int32(c.config.Consumer.Group.Session.Timeout / time.Millisecond), + ProtocolType: "consumer", + } + if c.config.Version.IsAtLeast(V0_10_1_0) { + req.Version = 1 + req.RebalanceTimeout = int32(c.config.Consumer.Group.Rebalance.Timeout / time.Millisecond) + } + + meta := &ConsumerGroupMemberMetadata{ + Topics: topics, + UserData: c.config.Consumer.Group.Member.UserData, + } + strategy := c.config.Consumer.Group.Rebalance.Strategy + if err := req.AddGroupProtocolMetadata(strategy.Name(), meta); err != nil { + return nil, err + } + + return coordinator.JoinGroup(req) +} + +func (c *consumerGroup) syncGroupRequest(coordinator *Broker, plan BalanceStrategyPlan, generationID int32) (*SyncGroupResponse, error) { + req := &SyncGroupRequest{ + GroupId: c.groupID, + MemberId: c.memberID, + GenerationId: generationID, + } + for memberID, topics := range plan { + err := req.AddGroupAssignmentMember(memberID, &ConsumerGroupMemberAssignment{ + Topics: topics, + }) + if err != nil { + return nil, err + } + } + return coordinator.SyncGroup(req) +} + +func (c *consumerGroup) heartbeatRequest(coordinator *Broker, memberID string, generationID int32) (*HeartbeatResponse, error) { + req := &HeartbeatRequest{ + GroupId: c.groupID, + MemberId: memberID, + GenerationId: generationID, + } + + return coordinator.Heartbeat(req) +} + +func (c *consumerGroup) balance(members map[string]ConsumerGroupMemberMetadata) (BalanceStrategyPlan, error) { + topics := make(map[string][]int32) + for _, meta := range members { + for _, topic := range meta.Topics { + topics[topic] = nil + } + } + + for topic := range topics { + partitions, err := c.client.Partitions(topic) + if err != nil { + return nil, err + } + topics[topic] = partitions + } + + strategy := c.config.Consumer.Group.Rebalance.Strategy + return strategy.Plan(members, topics) +} + +// Leaves the cluster, called by Close, protected by lock. +func (c *consumerGroup) leave() error { + if c.memberID == "" { + return nil + } + + coordinator, err := c.client.Coordinator(c.groupID) + if err != nil { + return err + } + + resp, err := coordinator.LeaveGroup(&LeaveGroupRequest{ + GroupId: c.groupID, + MemberId: c.memberID, + }) + if err != nil { + _ = coordinator.Close() + return err + } + + // Unset memberID + c.memberID = "" + + // Check response + switch resp.Err { + case ErrRebalanceInProgress, ErrUnknownMemberId, ErrNoError: + return nil + default: + return resp.Err + } +} + +func (c *consumerGroup) handleError(err error, topic string, partition int32) { + select { + case <-c.closed: + return + default: + } + + if _, ok := err.(*ConsumerError); !ok && topic != "" && partition > -1 { + err = &ConsumerError{ + Topic: topic, + Partition: partition, + Err: err, + } + } + + if c.config.Consumer.Return.Errors { + select { + case c.errors <- err: + default: + } + } else { + Logger.Println(err) + } +} + +// -------------------------------------------------------------------- + +// ConsumerGroupSession represents a consumer group member session. +type ConsumerGroupSession interface { + // Claims returns information about the claimed partitions by topic. + Claims() map[string][]int32 + + // MemberID returns the cluster member ID. + MemberID() string + + // GenerationID returns the current generation ID. + GenerationID() int32 + + // MarkOffset marks the provided offset, alongside a metadata string + // that represents the state of the partition consumer at that point in time. The + // metadata string can be used by another consumer to restore that state, so it + // can resume consumption. + // + // To follow upstream conventions, you are expected to mark the offset of the + // next message to read, not the last message read. Thus, when calling `MarkOffset` + // you should typically add one to the offset of the last consumed message. + // + // Note: calling MarkOffset does not necessarily commit the offset to the backend + // store immediately for efficiency reasons, and it may never be committed if + // your application crashes. This means that you may end up processing the same + // message twice, and your processing should ideally be idempotent. + MarkOffset(topic string, partition int32, offset int64, metadata string) + + // ResetOffset resets to the provided offset, alongside a metadata string that + // represents the state of the partition consumer at that point in time. Reset + // acts as a counterpart to MarkOffset, the difference being that it allows to + // reset an offset to an earlier or smaller value, where MarkOffset only + // allows incrementing the offset. cf MarkOffset for more details. + ResetOffset(topic string, partition int32, offset int64, metadata string) + + // MarkMessage marks a message as consumed. + MarkMessage(msg *ConsumerMessage, metadata string) + + // Context returns the session context. + Context() context.Context +} + +type consumerGroupSession struct { + parent *consumerGroup + memberID string + generationID int32 + handler ConsumerGroupHandler + + claims map[string][]int32 + offsets *offsetManager + ctx context.Context + cancel func() + + waitGroup sync.WaitGroup + releaseOnce sync.Once + hbDying, hbDead chan none +} + +func newConsumerGroupSession(parent *consumerGroup, ctx context.Context, claims map[string][]int32, memberID string, generationID int32, handler ConsumerGroupHandler) (*consumerGroupSession, error) { + // init offset manager + offsets, err := newOffsetManagerFromClient(parent.groupID, memberID, generationID, parent.client) + if err != nil { + return nil, err + } + + // init context + ctx, cancel := context.WithCancel(ctx) + + // init session + sess := &consumerGroupSession{ + parent: parent, + memberID: memberID, + generationID: generationID, + handler: handler, + offsets: offsets, + claims: claims, + ctx: ctx, + cancel: cancel, + hbDying: make(chan none), + hbDead: make(chan none), + } + + // start heartbeat loop + go sess.heartbeatLoop() + + // create a POM for each claim + for topic, partitions := range claims { + for _, partition := range partitions { + pom, err := offsets.ManagePartition(topic, partition) + if err != nil { + _ = sess.release(false) + return nil, err + } + + // handle POM errors + go func(topic string, partition int32) { + for err := range pom.Errors() { + sess.parent.handleError(err, topic, partition) + } + }(topic, partition) + } + } + + // perform setup + if err := handler.Setup(sess); err != nil { + _ = sess.release(true) + return nil, err + } + + // start consuming + for topic, partitions := range claims { + for _, partition := range partitions { + sess.waitGroup.Add(1) + + go func(topic string, partition int32) { + defer sess.waitGroup.Done() + + // cancel the as session as soon as the first + // goroutine exits + defer sess.cancel() + + // consume a single topic/partition, blocking + sess.consume(topic, partition) + }(topic, partition) + } + } + return sess, nil +} + +func (s *consumerGroupSession) Claims() map[string][]int32 { return s.claims } +func (s *consumerGroupSession) MemberID() string { return s.memberID } +func (s *consumerGroupSession) GenerationID() int32 { return s.generationID } + +func (s *consumerGroupSession) MarkOffset(topic string, partition int32, offset int64, metadata string) { + if pom := s.offsets.findPOM(topic, partition); pom != nil { + pom.MarkOffset(offset, metadata) + } +} + +func (s *consumerGroupSession) ResetOffset(topic string, partition int32, offset int64, metadata string) { + if pom := s.offsets.findPOM(topic, partition); pom != nil { + pom.ResetOffset(offset, metadata) + } +} + +func (s *consumerGroupSession) MarkMessage(msg *ConsumerMessage, metadata string) { + s.MarkOffset(msg.Topic, msg.Partition, msg.Offset+1, metadata) +} + +func (s *consumerGroupSession) Context() context.Context { + return s.ctx +} + +func (s *consumerGroupSession) consume(topic string, partition int32) { + // quick exit if rebalance is due + select { + case <-s.ctx.Done(): + return + case <-s.parent.closed: + return + default: + } + + // get next offset + offset := s.parent.config.Consumer.Offsets.Initial + if pom := s.offsets.findPOM(topic, partition); pom != nil { + offset, _ = pom.NextOffset() + } + + // create new claim + claim, err := newConsumerGroupClaim(s, topic, partition, offset) + if err != nil { + s.parent.handleError(err, topic, partition) + return + } + + // handle errors + go func() { + for err := range claim.Errors() { + s.parent.handleError(err, topic, partition) + } + }() + + // trigger close when session is done + go func() { + select { + case <-s.ctx.Done(): + case <-s.parent.closed: + } + claim.AsyncClose() + }() + + // start processing + if err := s.handler.ConsumeClaim(s, claim); err != nil { + s.parent.handleError(err, topic, partition) + } + + // ensure consumer is clased & drained + claim.AsyncClose() + for _, err := range claim.waitClosed() { + s.parent.handleError(err, topic, partition) + } +} + +func (s *consumerGroupSession) release(withCleanup bool) (err error) { + // signal release, stop heartbeat + s.cancel() + + // wait for consumers to exit + s.waitGroup.Wait() + + // perform release + s.releaseOnce.Do(func() { + if withCleanup { + if e := s.handler.Cleanup(s); e != nil { + s.parent.handleError(err, "", -1) + err = e + } + } + + if e := s.offsets.Close(); e != nil { + err = e + } + + close(s.hbDying) + <-s.hbDead + }) + + return +} + +func (s *consumerGroupSession) heartbeatLoop() { + defer close(s.hbDead) + defer s.cancel() // trigger the end of the session on exit + + pause := time.NewTicker(s.parent.config.Consumer.Group.Heartbeat.Interval) + defer pause.Stop() + + retries := s.parent.config.Metadata.Retry.Max + for { + coordinator, err := s.parent.client.Coordinator(s.parent.groupID) + if err != nil { + if retries <= 0 { + s.parent.handleError(err, "", -1) + return + } + + select { + case <-s.hbDying: + return + case <-time.After(s.parent.config.Metadata.Retry.Backoff): + retries-- + } + continue + } + + resp, err := s.parent.heartbeatRequest(coordinator, s.memberID, s.generationID) + if err != nil { + _ = coordinator.Close() + retries-- + continue + } + + switch resp.Err { + case ErrNoError: + retries = s.parent.config.Metadata.Retry.Max + case ErrRebalanceInProgress, ErrUnknownMemberId, ErrIllegalGeneration: + return + default: + s.parent.handleError(err, "", -1) + return + } + + select { + case <-pause.C: + case <-s.hbDying: + return + } + } +} + +// -------------------------------------------------------------------- + +// ConsumerGroupHandler instances are used to handle individual topic/partition claims. +// It also provides hooks for your consumer group session life-cycle and allow you to +// trigger logic before or after the consume loop(s). +// +// PLEASE NOTE that handlers are likely be called from several goroutines concurrently, +// ensure that all state is safely protected against race conditions. +type ConsumerGroupHandler interface { + // Setup is run at the beginning of a new session, before ConsumeClaim. + Setup(ConsumerGroupSession) error + + // Cleanup is run at the end of a session, once all ConsumeClaim goroutines have exites + // but before the offsets are committed for the very last time. + Cleanup(ConsumerGroupSession) error + + // ConsumeClaim must start a consumer loop of ConsumerGroupClaim's Messages(). + // Once the Messages() channel is closed, the Handler must finish its processing + // loop and exit. + ConsumeClaim(ConsumerGroupSession, ConsumerGroupClaim) error +} + +// ConsumerGroupClaim processes Kafka messages from a given topic and partition within a consumer group. +type ConsumerGroupClaim interface { + // Topic returns the consumed topic name. + Topic() string + + // Partition returns the consumed partition. + Partition() int32 + + // InitialOffset returns the initial offset that was used as a starting point for this claim. + InitialOffset() int64 + + // HighWaterMarkOffset returns the high water mark offset of the partition, + // i.e. the offset that will be used for the next message that will be produced. + // You can use this to determine how far behind the processing is. + HighWaterMarkOffset() int64 + + // Messages returns the read channel for the messages that are returned by + // the broker. The messages channel will be closed when a new rebalance cycle + // is due. You must finish processing and mark offsets within + // Config.Consumer.Group.Session.Timeout before the topic/partition is eventually + // re-assigned to another group member. + Messages() <-chan *ConsumerMessage +} + +type consumerGroupClaim struct { + topic string + partition int32 + offset int64 + PartitionConsumer +} + +func newConsumerGroupClaim(sess *consumerGroupSession, topic string, partition int32, offset int64) (*consumerGroupClaim, error) { + pcm, err := sess.parent.consumer.ConsumePartition(topic, partition, offset) + if err == ErrOffsetOutOfRange { + offset = sess.parent.config.Consumer.Offsets.Initial + pcm, err = sess.parent.consumer.ConsumePartition(topic, partition, offset) + } + if err != nil { + return nil, err + } + + go func() { + for err := range pcm.Errors() { + sess.parent.handleError(err, topic, partition) + } + }() + + return &consumerGroupClaim{ + topic: topic, + partition: partition, + offset: offset, + PartitionConsumer: pcm, + }, nil +} + +func (c *consumerGroupClaim) Topic() string { return c.topic } +func (c *consumerGroupClaim) Partition() int32 { return c.partition } +func (c *consumerGroupClaim) InitialOffset() int64 { return c.offset } + +// Drains messages and errors, ensures the claim is fully closed. +func (c *consumerGroupClaim) waitClosed() (errs ConsumerErrors) { + go func() { + for range c.Messages() { + } + }() + + for err := range c.Errors() { + errs = append(errs, err) + } + return +} diff --git a/vendor/github.com/Shopify/sarama/consumer_group_members.go b/vendor/github.com/Shopify/sarama/consumer_group_members.go new file mode 100644 index 00000000000..9d92d350a5d --- /dev/null +++ b/vendor/github.com/Shopify/sarama/consumer_group_members.go @@ -0,0 +1,94 @@ +package sarama + +type ConsumerGroupMemberMetadata struct { + Version int16 + Topics []string + UserData []byte +} + +func (m *ConsumerGroupMemberMetadata) encode(pe packetEncoder) error { + pe.putInt16(m.Version) + + if err := pe.putStringArray(m.Topics); err != nil { + return err + } + + if err := pe.putBytes(m.UserData); err != nil { + return err + } + + return nil +} + +func (m *ConsumerGroupMemberMetadata) decode(pd packetDecoder) (err error) { + if m.Version, err = pd.getInt16(); err != nil { + return + } + + if m.Topics, err = pd.getStringArray(); err != nil { + return + } + + if m.UserData, err = pd.getBytes(); err != nil { + return + } + + return nil +} + +type ConsumerGroupMemberAssignment struct { + Version int16 + Topics map[string][]int32 + UserData []byte +} + +func (m *ConsumerGroupMemberAssignment) encode(pe packetEncoder) error { + pe.putInt16(m.Version) + + if err := pe.putArrayLength(len(m.Topics)); err != nil { + return err + } + + for topic, partitions := range m.Topics { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putInt32Array(partitions); err != nil { + return err + } + } + + if err := pe.putBytes(m.UserData); err != nil { + return err + } + + return nil +} + +func (m *ConsumerGroupMemberAssignment) decode(pd packetDecoder) (err error) { + if m.Version, err = pd.getInt16(); err != nil { + return + } + + var topicLen int + if topicLen, err = pd.getArrayLength(); err != nil { + return + } + + m.Topics = make(map[string][]int32, topicLen) + for i := 0; i < topicLen; i++ { + var topic string + if topic, err = pd.getString(); err != nil { + return + } + if m.Topics[topic], err = pd.getInt32Array(); err != nil { + return + } + } + + if m.UserData, err = pd.getBytes(); err != nil { + return + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/consumer_metadata_request.go b/vendor/github.com/Shopify/sarama/consumer_metadata_request.go new file mode 100644 index 00000000000..4de45e7bf50 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/consumer_metadata_request.go @@ -0,0 +1,33 @@ +package sarama + +type ConsumerMetadataRequest struct { + ConsumerGroup string +} + +func (r *ConsumerMetadataRequest) encode(pe packetEncoder) error { + tmp := new(FindCoordinatorRequest) + tmp.CoordinatorKey = r.ConsumerGroup + tmp.CoordinatorType = CoordinatorGroup + return tmp.encode(pe) +} + +func (r *ConsumerMetadataRequest) decode(pd packetDecoder, version int16) (err error) { + tmp := new(FindCoordinatorRequest) + if err := tmp.decode(pd, version); err != nil { + return err + } + r.ConsumerGroup = tmp.CoordinatorKey + return nil +} + +func (r *ConsumerMetadataRequest) key() int16 { + return 10 +} + +func (r *ConsumerMetadataRequest) version() int16 { + return 0 +} + +func (r *ConsumerMetadataRequest) requiredVersion() KafkaVersion { + return V0_8_2_0 +} diff --git a/vendor/github.com/Shopify/sarama/consumer_metadata_response.go b/vendor/github.com/Shopify/sarama/consumer_metadata_response.go new file mode 100644 index 00000000000..442cbde7ac0 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/consumer_metadata_response.go @@ -0,0 +1,77 @@ +package sarama + +import ( + "net" + "strconv" +) + +type ConsumerMetadataResponse struct { + Err KError + Coordinator *Broker + CoordinatorID int32 // deprecated: use Coordinator.ID() + CoordinatorHost string // deprecated: use Coordinator.Addr() + CoordinatorPort int32 // deprecated: use Coordinator.Addr() +} + +func (r *ConsumerMetadataResponse) decode(pd packetDecoder, version int16) (err error) { + tmp := new(FindCoordinatorResponse) + + if err := tmp.decode(pd, version); err != nil { + return err + } + + r.Err = tmp.Err + + r.Coordinator = tmp.Coordinator + if tmp.Coordinator == nil { + return nil + } + + // this can all go away in 2.0, but we have to fill in deprecated fields to maintain + // backwards compatibility + host, portstr, err := net.SplitHostPort(r.Coordinator.Addr()) + if err != nil { + return err + } + port, err := strconv.ParseInt(portstr, 10, 32) + if err != nil { + return err + } + r.CoordinatorID = r.Coordinator.ID() + r.CoordinatorHost = host + r.CoordinatorPort = int32(port) + + return nil +} + +func (r *ConsumerMetadataResponse) encode(pe packetEncoder) error { + if r.Coordinator == nil { + r.Coordinator = new(Broker) + r.Coordinator.id = r.CoordinatorID + r.Coordinator.addr = net.JoinHostPort(r.CoordinatorHost, strconv.Itoa(int(r.CoordinatorPort))) + } + + tmp := &FindCoordinatorResponse{ + Version: 0, + Err: r.Err, + Coordinator: r.Coordinator, + } + + if err := tmp.encode(pe); err != nil { + return err + } + + return nil +} + +func (r *ConsumerMetadataResponse) key() int16 { + return 10 +} + +func (r *ConsumerMetadataResponse) version() int16 { + return 0 +} + +func (r *ConsumerMetadataResponse) requiredVersion() KafkaVersion { + return V0_8_2_0 +} diff --git a/vendor/github.com/Shopify/sarama/crc32_field.go b/vendor/github.com/Shopify/sarama/crc32_field.go new file mode 100644 index 00000000000..1f144431a8b --- /dev/null +++ b/vendor/github.com/Shopify/sarama/crc32_field.go @@ -0,0 +1,69 @@ +package sarama + +import ( + "encoding/binary" + "fmt" + "hash/crc32" +) + +type crcPolynomial int8 + +const ( + crcIEEE crcPolynomial = iota + crcCastagnoli +) + +var castagnoliTable = crc32.MakeTable(crc32.Castagnoli) + +// crc32Field implements the pushEncoder and pushDecoder interfaces for calculating CRC32s. +type crc32Field struct { + startOffset int + polynomial crcPolynomial +} + +func (c *crc32Field) saveOffset(in int) { + c.startOffset = in +} + +func (c *crc32Field) reserveLength() int { + return 4 +} + +func newCRC32Field(polynomial crcPolynomial) *crc32Field { + return &crc32Field{polynomial: polynomial} +} + +func (c *crc32Field) run(curOffset int, buf []byte) error { + crc, err := c.crc(curOffset, buf) + if err != nil { + return err + } + binary.BigEndian.PutUint32(buf[c.startOffset:], crc) + return nil +} + +func (c *crc32Field) check(curOffset int, buf []byte) error { + crc, err := c.crc(curOffset, buf) + if err != nil { + return err + } + + expected := binary.BigEndian.Uint32(buf[c.startOffset:]) + if crc != expected { + return PacketDecodingError{fmt.Sprintf("CRC didn't match expected %#x got %#x", expected, crc)} + } + + return nil +} +func (c *crc32Field) crc(curOffset int, buf []byte) (uint32, error) { + var tab *crc32.Table + switch c.polynomial { + case crcIEEE: + tab = crc32.IEEETable + case crcCastagnoli: + tab = castagnoliTable + default: + return 0, PacketDecodingError{"invalid CRC type"} + } + return crc32.Checksum(buf[c.startOffset+4:curOffset], tab), nil +} diff --git a/vendor/github.com/Shopify/sarama/create_partitions_request.go b/vendor/github.com/Shopify/sarama/create_partitions_request.go new file mode 100644 index 00000000000..af321e99466 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/create_partitions_request.go @@ -0,0 +1,121 @@ +package sarama + +import "time" + +type CreatePartitionsRequest struct { + TopicPartitions map[string]*TopicPartition + Timeout time.Duration + ValidateOnly bool +} + +func (c *CreatePartitionsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(c.TopicPartitions)); err != nil { + return err + } + + for topic, partition := range c.TopicPartitions { + if err := pe.putString(topic); err != nil { + return err + } + if err := partition.encode(pe); err != nil { + return err + } + } + + pe.putInt32(int32(c.Timeout / time.Millisecond)) + + pe.putBool(c.ValidateOnly) + + return nil +} + +func (c *CreatePartitionsRequest) decode(pd packetDecoder, version int16) (err error) { + n, err := pd.getArrayLength() + if err != nil { + return err + } + c.TopicPartitions = make(map[string]*TopicPartition, n) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + c.TopicPartitions[topic] = new(TopicPartition) + if err := c.TopicPartitions[topic].decode(pd, version); err != nil { + return err + } + } + + timeout, err := pd.getInt32() + if err != nil { + return err + } + c.Timeout = time.Duration(timeout) * time.Millisecond + + if c.ValidateOnly, err = pd.getBool(); err != nil { + return err + } + + return nil +} + +func (r *CreatePartitionsRequest) key() int16 { + return 37 +} + +func (r *CreatePartitionsRequest) version() int16 { + return 0 +} + +func (r *CreatePartitionsRequest) requiredVersion() KafkaVersion { + return V1_0_0_0 +} + +type TopicPartition struct { + Count int32 + Assignment [][]int32 +} + +func (t *TopicPartition) encode(pe packetEncoder) error { + pe.putInt32(t.Count) + + if len(t.Assignment) == 0 { + pe.putInt32(-1) + return nil + } + + if err := pe.putArrayLength(len(t.Assignment)); err != nil { + return err + } + + for _, assign := range t.Assignment { + if err := pe.putInt32Array(assign); err != nil { + return err + } + } + + return nil +} + +func (t *TopicPartition) decode(pd packetDecoder, version int16) (err error) { + if t.Count, err = pd.getInt32(); err != nil { + return err + } + + n, err := pd.getInt32() + if err != nil { + return err + } + if n <= 0 { + return nil + } + t.Assignment = make([][]int32, n) + + for i := 0; i < int(n); i++ { + if t.Assignment[i], err = pd.getInt32Array(); err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/create_partitions_response.go b/vendor/github.com/Shopify/sarama/create_partitions_response.go new file mode 100644 index 00000000000..abd621c64ec --- /dev/null +++ b/vendor/github.com/Shopify/sarama/create_partitions_response.go @@ -0,0 +1,94 @@ +package sarama + +import "time" + +type CreatePartitionsResponse struct { + ThrottleTime time.Duration + TopicPartitionErrors map[string]*TopicPartitionError +} + +func (c *CreatePartitionsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(c.ThrottleTime / time.Millisecond)) + if err := pe.putArrayLength(len(c.TopicPartitionErrors)); err != nil { + return err + } + + for topic, partitionError := range c.TopicPartitionErrors { + if err := pe.putString(topic); err != nil { + return err + } + if err := partitionError.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (c *CreatePartitionsResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + c.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + c.TopicPartitionErrors = make(map[string]*TopicPartitionError, n) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + c.TopicPartitionErrors[topic] = new(TopicPartitionError) + if err := c.TopicPartitionErrors[topic].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (r *CreatePartitionsResponse) key() int16 { + return 37 +} + +func (r *CreatePartitionsResponse) version() int16 { + return 0 +} + +func (r *CreatePartitionsResponse) requiredVersion() KafkaVersion { + return V1_0_0_0 +} + +type TopicPartitionError struct { + Err KError + ErrMsg *string +} + +func (t *TopicPartitionError) encode(pe packetEncoder) error { + pe.putInt16(int16(t.Err)) + + if err := pe.putNullableString(t.ErrMsg); err != nil { + return err + } + + return nil +} + +func (t *TopicPartitionError) decode(pd packetDecoder, version int16) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + t.Err = KError(kerr) + + if t.ErrMsg, err = pd.getNullableString(); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/create_topics_request.go b/vendor/github.com/Shopify/sarama/create_topics_request.go new file mode 100644 index 00000000000..709c0a44e71 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/create_topics_request.go @@ -0,0 +1,174 @@ +package sarama + +import ( + "time" +) + +type CreateTopicsRequest struct { + Version int16 + + TopicDetails map[string]*TopicDetail + Timeout time.Duration + ValidateOnly bool +} + +func (c *CreateTopicsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(c.TopicDetails)); err != nil { + return err + } + for topic, detail := range c.TopicDetails { + if err := pe.putString(topic); err != nil { + return err + } + if err := detail.encode(pe); err != nil { + return err + } + } + + pe.putInt32(int32(c.Timeout / time.Millisecond)) + + if c.Version >= 1 { + pe.putBool(c.ValidateOnly) + } + + return nil +} + +func (c *CreateTopicsRequest) decode(pd packetDecoder, version int16) (err error) { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + c.TopicDetails = make(map[string]*TopicDetail, n) + + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + c.TopicDetails[topic] = new(TopicDetail) + if err = c.TopicDetails[topic].decode(pd, version); err != nil { + return err + } + } + + timeout, err := pd.getInt32() + if err != nil { + return err + } + c.Timeout = time.Duration(timeout) * time.Millisecond + + if version >= 1 { + c.ValidateOnly, err = pd.getBool() + if err != nil { + return err + } + + c.Version = version + } + + return nil +} + +func (c *CreateTopicsRequest) key() int16 { + return 19 +} + +func (c *CreateTopicsRequest) version() int16 { + return c.Version +} + +func (c *CreateTopicsRequest) requiredVersion() KafkaVersion { + switch c.Version { + case 2: + return V1_0_0_0 + case 1: + return V0_11_0_0 + default: + return V0_10_1_0 + } +} + +type TopicDetail struct { + NumPartitions int32 + ReplicationFactor int16 + ReplicaAssignment map[int32][]int32 + ConfigEntries map[string]*string +} + +func (t *TopicDetail) encode(pe packetEncoder) error { + pe.putInt32(t.NumPartitions) + pe.putInt16(t.ReplicationFactor) + + if err := pe.putArrayLength(len(t.ReplicaAssignment)); err != nil { + return err + } + for partition, assignment := range t.ReplicaAssignment { + pe.putInt32(partition) + if err := pe.putInt32Array(assignment); err != nil { + return err + } + } + + if err := pe.putArrayLength(len(t.ConfigEntries)); err != nil { + return err + } + for configKey, configValue := range t.ConfigEntries { + if err := pe.putString(configKey); err != nil { + return err + } + if err := pe.putNullableString(configValue); err != nil { + return err + } + } + + return nil +} + +func (t *TopicDetail) decode(pd packetDecoder, version int16) (err error) { + if t.NumPartitions, err = pd.getInt32(); err != nil { + return err + } + if t.ReplicationFactor, err = pd.getInt16(); err != nil { + return err + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + t.ReplicaAssignment = make(map[int32][]int32, n) + for i := 0; i < n; i++ { + replica, err := pd.getInt32() + if err != nil { + return err + } + if t.ReplicaAssignment[replica], err = pd.getInt32Array(); err != nil { + return err + } + } + } + + n, err = pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + t.ConfigEntries = make(map[string]*string, n) + for i := 0; i < n; i++ { + configKey, err := pd.getString() + if err != nil { + return err + } + if t.ConfigEntries[configKey], err = pd.getNullableString(); err != nil { + return err + } + } + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/create_topics_response.go b/vendor/github.com/Shopify/sarama/create_topics_response.go new file mode 100644 index 00000000000..66207e00c5d --- /dev/null +++ b/vendor/github.com/Shopify/sarama/create_topics_response.go @@ -0,0 +1,112 @@ +package sarama + +import "time" + +type CreateTopicsResponse struct { + Version int16 + ThrottleTime time.Duration + TopicErrors map[string]*TopicError +} + +func (c *CreateTopicsResponse) encode(pe packetEncoder) error { + if c.Version >= 2 { + pe.putInt32(int32(c.ThrottleTime / time.Millisecond)) + } + + if err := pe.putArrayLength(len(c.TopicErrors)); err != nil { + return err + } + for topic, topicError := range c.TopicErrors { + if err := pe.putString(topic); err != nil { + return err + } + if err := topicError.encode(pe, c.Version); err != nil { + return err + } + } + + return nil +} + +func (c *CreateTopicsResponse) decode(pd packetDecoder, version int16) (err error) { + c.Version = version + + if version >= 2 { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + c.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + c.TopicErrors = make(map[string]*TopicError, n) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + c.TopicErrors[topic] = new(TopicError) + if err := c.TopicErrors[topic].decode(pd, version); err != nil { + return err + } + } + + return nil +} + +func (c *CreateTopicsResponse) key() int16 { + return 19 +} + +func (c *CreateTopicsResponse) version() int16 { + return c.Version +} + +func (c *CreateTopicsResponse) requiredVersion() KafkaVersion { + switch c.Version { + case 2: + return V1_0_0_0 + case 1: + return V0_11_0_0 + default: + return V0_10_1_0 + } +} + +type TopicError struct { + Err KError + ErrMsg *string +} + +func (t *TopicError) encode(pe packetEncoder, version int16) error { + pe.putInt16(int16(t.Err)) + + if version >= 1 { + if err := pe.putNullableString(t.ErrMsg); err != nil { + return err + } + } + + return nil +} + +func (t *TopicError) decode(pd packetDecoder, version int16) (err error) { + kErr, err := pd.getInt16() + if err != nil { + return err + } + t.Err = KError(kErr) + + if version >= 1 { + if t.ErrMsg, err = pd.getNullableString(); err != nil { + return err + } + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/delete_groups_request.go b/vendor/github.com/Shopify/sarama/delete_groups_request.go new file mode 100644 index 00000000000..305a324ac2d --- /dev/null +++ b/vendor/github.com/Shopify/sarama/delete_groups_request.go @@ -0,0 +1,30 @@ +package sarama + +type DeleteGroupsRequest struct { + Groups []string +} + +func (r *DeleteGroupsRequest) encode(pe packetEncoder) error { + return pe.putStringArray(r.Groups) +} + +func (r *DeleteGroupsRequest) decode(pd packetDecoder, version int16) (err error) { + r.Groups, err = pd.getStringArray() + return +} + +func (r *DeleteGroupsRequest) key() int16 { + return 42 +} + +func (r *DeleteGroupsRequest) version() int16 { + return 0 +} + +func (r *DeleteGroupsRequest) requiredVersion() KafkaVersion { + return V1_1_0_0 +} + +func (r *DeleteGroupsRequest) AddGroup(group string) { + r.Groups = append(r.Groups, group) +} diff --git a/vendor/github.com/Shopify/sarama/delete_groups_response.go b/vendor/github.com/Shopify/sarama/delete_groups_response.go new file mode 100644 index 00000000000..c067ebb42b0 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/delete_groups_response.go @@ -0,0 +1,70 @@ +package sarama + +import ( + "time" +) + +type DeleteGroupsResponse struct { + ThrottleTime time.Duration + GroupErrorCodes map[string]KError +} + +func (r *DeleteGroupsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(r.ThrottleTime / time.Millisecond)) + + if err := pe.putArrayLength(len(r.GroupErrorCodes)); err != nil { + return err + } + for groupID, errorCode := range r.GroupErrorCodes { + if err := pe.putString(groupID); err != nil { + return err + } + pe.putInt16(int16(errorCode)) + } + + return nil +} + +func (r *DeleteGroupsResponse) decode(pd packetDecoder, version int16) error { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + r.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + if n == 0 { + return nil + } + + r.GroupErrorCodes = make(map[string]KError, n) + for i := 0; i < n; i++ { + groupID, err := pd.getString() + if err != nil { + return err + } + errorCode, err := pd.getInt16() + if err != nil { + return err + } + + r.GroupErrorCodes[groupID] = KError(errorCode) + } + + return nil +} + +func (r *DeleteGroupsResponse) key() int16 { + return 42 +} + +func (r *DeleteGroupsResponse) version() int16 { + return 0 +} + +func (r *DeleteGroupsResponse) requiredVersion() KafkaVersion { + return V1_1_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/delete_records_request.go b/vendor/github.com/Shopify/sarama/delete_records_request.go new file mode 100644 index 00000000000..93efafd4d0b --- /dev/null +++ b/vendor/github.com/Shopify/sarama/delete_records_request.go @@ -0,0 +1,126 @@ +package sarama + +import ( + "sort" + "time" +) + +// request message format is: +// [topic] timeout(int32) +// where topic is: +// name(string) [partition] +// where partition is: +// id(int32) offset(int64) + +type DeleteRecordsRequest struct { + Topics map[string]*DeleteRecordsRequestTopic + Timeout time.Duration +} + +func (d *DeleteRecordsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(d.Topics)); err != nil { + return err + } + keys := make([]string, 0, len(d.Topics)) + for topic := range d.Topics { + keys = append(keys, topic) + } + sort.Strings(keys) + for _, topic := range keys { + if err := pe.putString(topic); err != nil { + return err + } + if err := d.Topics[topic].encode(pe); err != nil { + return err + } + } + pe.putInt32(int32(d.Timeout / time.Millisecond)) + + return nil +} + +func (d *DeleteRecordsRequest) decode(pd packetDecoder, version int16) error { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + d.Topics = make(map[string]*DeleteRecordsRequestTopic, n) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + details := new(DeleteRecordsRequestTopic) + if err = details.decode(pd, version); err != nil { + return err + } + d.Topics[topic] = details + } + } + + timeout, err := pd.getInt32() + if err != nil { + return err + } + d.Timeout = time.Duration(timeout) * time.Millisecond + + return nil +} + +func (d *DeleteRecordsRequest) key() int16 { + return 21 +} + +func (d *DeleteRecordsRequest) version() int16 { + return 0 +} + +func (d *DeleteRecordsRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +type DeleteRecordsRequestTopic struct { + PartitionOffsets map[int32]int64 // partition => offset +} + +func (t *DeleteRecordsRequestTopic) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(t.PartitionOffsets)); err != nil { + return err + } + keys := make([]int32, 0, len(t.PartitionOffsets)) + for partition := range t.PartitionOffsets { + keys = append(keys, partition) + } + sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) + for _, partition := range keys { + pe.putInt32(partition) + pe.putInt64(t.PartitionOffsets[partition]) + } + return nil +} + +func (t *DeleteRecordsRequestTopic) decode(pd packetDecoder, version int16) error { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + t.PartitionOffsets = make(map[int32]int64, n) + for i := 0; i < n; i++ { + partition, err := pd.getInt32() + if err != nil { + return err + } + offset, err := pd.getInt64() + if err != nil { + return err + } + t.PartitionOffsets[partition] = offset + } + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/delete_records_response.go b/vendor/github.com/Shopify/sarama/delete_records_response.go new file mode 100644 index 00000000000..733a58b6bc3 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/delete_records_response.go @@ -0,0 +1,158 @@ +package sarama + +import ( + "sort" + "time" +) + +// response message format is: +// throttleMs(int32) [topic] +// where topic is: +// name(string) [partition] +// where partition is: +// id(int32) low_watermark(int64) error_code(int16) + +type DeleteRecordsResponse struct { + Version int16 + ThrottleTime time.Duration + Topics map[string]*DeleteRecordsResponseTopic +} + +func (d *DeleteRecordsResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(d.ThrottleTime / time.Millisecond)) + + if err := pe.putArrayLength(len(d.Topics)); err != nil { + return err + } + keys := make([]string, 0, len(d.Topics)) + for topic := range d.Topics { + keys = append(keys, topic) + } + sort.Strings(keys) + for _, topic := range keys { + if err := pe.putString(topic); err != nil { + return err + } + if err := d.Topics[topic].encode(pe); err != nil { + return err + } + } + return nil +} + +func (d *DeleteRecordsResponse) decode(pd packetDecoder, version int16) error { + d.Version = version + + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + d.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + d.Topics = make(map[string]*DeleteRecordsResponseTopic, n) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + details := new(DeleteRecordsResponseTopic) + if err = details.decode(pd, version); err != nil { + return err + } + d.Topics[topic] = details + } + } + + return nil +} + +func (d *DeleteRecordsResponse) key() int16 { + return 21 +} + +func (d *DeleteRecordsResponse) version() int16 { + return 0 +} + +func (d *DeleteRecordsResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +type DeleteRecordsResponseTopic struct { + Partitions map[int32]*DeleteRecordsResponsePartition +} + +func (t *DeleteRecordsResponseTopic) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(t.Partitions)); err != nil { + return err + } + keys := make([]int32, 0, len(t.Partitions)) + for partition := range t.Partitions { + keys = append(keys, partition) + } + sort.Slice(keys, func(i, j int) bool { return keys[i] < keys[j] }) + for _, partition := range keys { + pe.putInt32(partition) + if err := t.Partitions[partition].encode(pe); err != nil { + return err + } + } + return nil +} + +func (t *DeleteRecordsResponseTopic) decode(pd packetDecoder, version int16) error { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + if n > 0 { + t.Partitions = make(map[int32]*DeleteRecordsResponsePartition, n) + for i := 0; i < n; i++ { + partition, err := pd.getInt32() + if err != nil { + return err + } + details := new(DeleteRecordsResponsePartition) + if err = details.decode(pd, version); err != nil { + return err + } + t.Partitions[partition] = details + } + } + + return nil +} + +type DeleteRecordsResponsePartition struct { + LowWatermark int64 + Err KError +} + +func (t *DeleteRecordsResponsePartition) encode(pe packetEncoder) error { + pe.putInt64(t.LowWatermark) + pe.putInt16(int16(t.Err)) + return nil +} + +func (t *DeleteRecordsResponsePartition) decode(pd packetDecoder, version int16) error { + lowWatermark, err := pd.getInt64() + if err != nil { + return err + } + t.LowWatermark = lowWatermark + + kErr, err := pd.getInt16() + if err != nil { + return err + } + t.Err = KError(kErr) + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/delete_topics_request.go b/vendor/github.com/Shopify/sarama/delete_topics_request.go new file mode 100644 index 00000000000..911f67d31ba --- /dev/null +++ b/vendor/github.com/Shopify/sarama/delete_topics_request.go @@ -0,0 +1,48 @@ +package sarama + +import "time" + +type DeleteTopicsRequest struct { + Version int16 + Topics []string + Timeout time.Duration +} + +func (d *DeleteTopicsRequest) encode(pe packetEncoder) error { + if err := pe.putStringArray(d.Topics); err != nil { + return err + } + pe.putInt32(int32(d.Timeout / time.Millisecond)) + + return nil +} + +func (d *DeleteTopicsRequest) decode(pd packetDecoder, version int16) (err error) { + if d.Topics, err = pd.getStringArray(); err != nil { + return err + } + timeout, err := pd.getInt32() + if err != nil { + return err + } + d.Timeout = time.Duration(timeout) * time.Millisecond + d.Version = version + return nil +} + +func (d *DeleteTopicsRequest) key() int16 { + return 20 +} + +func (d *DeleteTopicsRequest) version() int16 { + return d.Version +} + +func (d *DeleteTopicsRequest) requiredVersion() KafkaVersion { + switch d.Version { + case 1: + return V0_11_0_0 + default: + return V0_10_1_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/delete_topics_response.go b/vendor/github.com/Shopify/sarama/delete_topics_response.go new file mode 100644 index 00000000000..34225460a31 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/delete_topics_response.go @@ -0,0 +1,78 @@ +package sarama + +import "time" + +type DeleteTopicsResponse struct { + Version int16 + ThrottleTime time.Duration + TopicErrorCodes map[string]KError +} + +func (d *DeleteTopicsResponse) encode(pe packetEncoder) error { + if d.Version >= 1 { + pe.putInt32(int32(d.ThrottleTime / time.Millisecond)) + } + + if err := pe.putArrayLength(len(d.TopicErrorCodes)); err != nil { + return err + } + for topic, errorCode := range d.TopicErrorCodes { + if err := pe.putString(topic); err != nil { + return err + } + pe.putInt16(int16(errorCode)) + } + + return nil +} + +func (d *DeleteTopicsResponse) decode(pd packetDecoder, version int16) (err error) { + if version >= 1 { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + d.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + d.Version = version + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + d.TopicErrorCodes = make(map[string]KError, n) + + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + errorCode, err := pd.getInt16() + if err != nil { + return err + } + + d.TopicErrorCodes[topic] = KError(errorCode) + } + + return nil +} + +func (d *DeleteTopicsResponse) key() int16 { + return 20 +} + +func (d *DeleteTopicsResponse) version() int16 { + return d.Version +} + +func (d *DeleteTopicsResponse) requiredVersion() KafkaVersion { + switch d.Version { + case 1: + return V0_11_0_0 + default: + return V0_10_1_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/describe_configs_request.go b/vendor/github.com/Shopify/sarama/describe_configs_request.go new file mode 100644 index 00000000000..7a7cffc3fb2 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/describe_configs_request.go @@ -0,0 +1,91 @@ +package sarama + +type ConfigResource struct { + Type ConfigResourceType + Name string + ConfigNames []string +} + +type DescribeConfigsRequest struct { + Resources []*ConfigResource +} + +func (r *DescribeConfigsRequest) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(r.Resources)); err != nil { + return err + } + + for _, c := range r.Resources { + pe.putInt8(int8(c.Type)) + if err := pe.putString(c.Name); err != nil { + return err + } + + if len(c.ConfigNames) == 0 { + pe.putInt32(-1) + continue + } + if err := pe.putStringArray(c.ConfigNames); err != nil { + return err + } + } + + return nil +} + +func (r *DescribeConfigsRequest) decode(pd packetDecoder, version int16) (err error) { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Resources = make([]*ConfigResource, n) + + for i := 0; i < n; i++ { + r.Resources[i] = &ConfigResource{} + t, err := pd.getInt8() + if err != nil { + return err + } + r.Resources[i].Type = ConfigResourceType(t) + name, err := pd.getString() + if err != nil { + return err + } + r.Resources[i].Name = name + + confLength, err := pd.getArrayLength() + + if err != nil { + return err + } + + if confLength == -1 { + continue + } + + cfnames := make([]string, confLength) + for i := 0; i < confLength; i++ { + s, err := pd.getString() + if err != nil { + return err + } + cfnames[i] = s + } + r.Resources[i].ConfigNames = cfnames + } + + return nil +} + +func (r *DescribeConfigsRequest) key() int16 { + return 32 +} + +func (r *DescribeConfigsRequest) version() int16 { + return 0 +} + +func (r *DescribeConfigsRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/describe_configs_response.go b/vendor/github.com/Shopify/sarama/describe_configs_response.go new file mode 100644 index 00000000000..6e5d30e4f09 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/describe_configs_response.go @@ -0,0 +1,188 @@ +package sarama + +import "time" + +type DescribeConfigsResponse struct { + ThrottleTime time.Duration + Resources []*ResourceResponse +} + +type ResourceResponse struct { + ErrorCode int16 + ErrorMsg string + Type ConfigResourceType + Name string + Configs []*ConfigEntry +} + +type ConfigEntry struct { + Name string + Value string + ReadOnly bool + Default bool + Sensitive bool +} + +func (r *DescribeConfigsResponse) encode(pe packetEncoder) (err error) { + pe.putInt32(int32(r.ThrottleTime / time.Millisecond)) + if err = pe.putArrayLength(len(r.Resources)); err != nil { + return err + } + + for _, c := range r.Resources { + if err = c.encode(pe); err != nil { + return err + } + } + return nil +} + +func (r *DescribeConfigsResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + r.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Resources = make([]*ResourceResponse, n) + for i := 0; i < n; i++ { + rr := &ResourceResponse{} + if err := rr.decode(pd, version); err != nil { + return err + } + r.Resources[i] = rr + } + + return nil +} + +func (r *DescribeConfigsResponse) key() int16 { + return 32 +} + +func (r *DescribeConfigsResponse) version() int16 { + return 0 +} + +func (r *DescribeConfigsResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +func (r *ResourceResponse) encode(pe packetEncoder) (err error) { + pe.putInt16(r.ErrorCode) + + if err = pe.putString(r.ErrorMsg); err != nil { + return err + } + + pe.putInt8(int8(r.Type)) + + if err = pe.putString(r.Name); err != nil { + return err + } + + if err = pe.putArrayLength(len(r.Configs)); err != nil { + return err + } + + for _, c := range r.Configs { + if err = c.encode(pe); err != nil { + return err + } + } + return nil +} + +func (r *ResourceResponse) decode(pd packetDecoder, version int16) (err error) { + ec, err := pd.getInt16() + if err != nil { + return err + } + r.ErrorCode = ec + + em, err := pd.getString() + if err != nil { + return err + } + r.ErrorMsg = em + + t, err := pd.getInt8() + if err != nil { + return err + } + r.Type = ConfigResourceType(t) + + name, err := pd.getString() + if err != nil { + return err + } + r.Name = name + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Configs = make([]*ConfigEntry, n) + for i := 0; i < n; i++ { + c := &ConfigEntry{} + if err := c.decode(pd, version); err != nil { + return err + } + r.Configs[i] = c + } + return nil +} + +func (r *ConfigEntry) encode(pe packetEncoder) (err error) { + if err = pe.putString(r.Name); err != nil { + return err + } + + if err = pe.putString(r.Value); err != nil { + return err + } + + pe.putBool(r.ReadOnly) + pe.putBool(r.Default) + pe.putBool(r.Sensitive) + return nil +} + +func (r *ConfigEntry) decode(pd packetDecoder, version int16) (err error) { + name, err := pd.getString() + if err != nil { + return err + } + r.Name = name + + value, err := pd.getString() + if err != nil { + return err + } + r.Value = value + + read, err := pd.getBool() + if err != nil { + return err + } + r.ReadOnly = read + + de, err := pd.getBool() + if err != nil { + return err + } + r.Default = de + + sensitive, err := pd.getBool() + if err != nil { + return err + } + r.Sensitive = sensitive + return nil +} diff --git a/vendor/github.com/Shopify/sarama/describe_groups_request.go b/vendor/github.com/Shopify/sarama/describe_groups_request.go new file mode 100644 index 00000000000..1fb35677708 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/describe_groups_request.go @@ -0,0 +1,30 @@ +package sarama + +type DescribeGroupsRequest struct { + Groups []string +} + +func (r *DescribeGroupsRequest) encode(pe packetEncoder) error { + return pe.putStringArray(r.Groups) +} + +func (r *DescribeGroupsRequest) decode(pd packetDecoder, version int16) (err error) { + r.Groups, err = pd.getStringArray() + return +} + +func (r *DescribeGroupsRequest) key() int16 { + return 15 +} + +func (r *DescribeGroupsRequest) version() int16 { + return 0 +} + +func (r *DescribeGroupsRequest) requiredVersion() KafkaVersion { + return V0_9_0_0 +} + +func (r *DescribeGroupsRequest) AddGroup(group string) { + r.Groups = append(r.Groups, group) +} diff --git a/vendor/github.com/Shopify/sarama/describe_groups_response.go b/vendor/github.com/Shopify/sarama/describe_groups_response.go new file mode 100644 index 00000000000..542b3a97170 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/describe_groups_response.go @@ -0,0 +1,187 @@ +package sarama + +type DescribeGroupsResponse struct { + Groups []*GroupDescription +} + +func (r *DescribeGroupsResponse) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(r.Groups)); err != nil { + return err + } + + for _, groupDescription := range r.Groups { + if err := groupDescription.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (r *DescribeGroupsResponse) decode(pd packetDecoder, version int16) (err error) { + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Groups = make([]*GroupDescription, n) + for i := 0; i < n; i++ { + r.Groups[i] = new(GroupDescription) + if err := r.Groups[i].decode(pd); err != nil { + return err + } + } + + return nil +} + +func (r *DescribeGroupsResponse) key() int16 { + return 15 +} + +func (r *DescribeGroupsResponse) version() int16 { + return 0 +} + +func (r *DescribeGroupsResponse) requiredVersion() KafkaVersion { + return V0_9_0_0 +} + +type GroupDescription struct { + Err KError + GroupId string + State string + ProtocolType string + Protocol string + Members map[string]*GroupMemberDescription +} + +func (gd *GroupDescription) encode(pe packetEncoder) error { + pe.putInt16(int16(gd.Err)) + + if err := pe.putString(gd.GroupId); err != nil { + return err + } + if err := pe.putString(gd.State); err != nil { + return err + } + if err := pe.putString(gd.ProtocolType); err != nil { + return err + } + if err := pe.putString(gd.Protocol); err != nil { + return err + } + + if err := pe.putArrayLength(len(gd.Members)); err != nil { + return err + } + + for memberId, groupMemberDescription := range gd.Members { + if err := pe.putString(memberId); err != nil { + return err + } + if err := groupMemberDescription.encode(pe); err != nil { + return err + } + } + + return nil +} + +func (gd *GroupDescription) decode(pd packetDecoder) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + + gd.Err = KError(kerr) + + if gd.GroupId, err = pd.getString(); err != nil { + return + } + if gd.State, err = pd.getString(); err != nil { + return + } + if gd.ProtocolType, err = pd.getString(); err != nil { + return + } + if gd.Protocol, err = pd.getString(); err != nil { + return + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + if n == 0 { + return nil + } + + gd.Members = make(map[string]*GroupMemberDescription) + for i := 0; i < n; i++ { + memberId, err := pd.getString() + if err != nil { + return err + } + + gd.Members[memberId] = new(GroupMemberDescription) + if err := gd.Members[memberId].decode(pd); err != nil { + return err + } + } + + return nil +} + +type GroupMemberDescription struct { + ClientId string + ClientHost string + MemberMetadata []byte + MemberAssignment []byte +} + +func (gmd *GroupMemberDescription) encode(pe packetEncoder) error { + if err := pe.putString(gmd.ClientId); err != nil { + return err + } + if err := pe.putString(gmd.ClientHost); err != nil { + return err + } + if err := pe.putBytes(gmd.MemberMetadata); err != nil { + return err + } + if err := pe.putBytes(gmd.MemberAssignment); err != nil { + return err + } + + return nil +} + +func (gmd *GroupMemberDescription) decode(pd packetDecoder) (err error) { + if gmd.ClientId, err = pd.getString(); err != nil { + return + } + if gmd.ClientHost, err = pd.getString(); err != nil { + return + } + if gmd.MemberMetadata, err = pd.getBytes(); err != nil { + return + } + if gmd.MemberAssignment, err = pd.getBytes(); err != nil { + return + } + + return nil +} + +func (gmd *GroupMemberDescription) GetMemberAssignment() (*ConsumerGroupMemberAssignment, error) { + assignment := new(ConsumerGroupMemberAssignment) + err := decode(gmd.MemberAssignment, assignment) + return assignment, err +} + +func (gmd *GroupMemberDescription) GetMemberMetadata() (*ConsumerGroupMemberMetadata, error) { + metadata := new(ConsumerGroupMemberMetadata) + err := decode(gmd.MemberMetadata, metadata) + return metadata, err +} diff --git a/vendor/github.com/Shopify/sarama/encoder_decoder.go b/vendor/github.com/Shopify/sarama/encoder_decoder.go new file mode 100644 index 00000000000..7ce3bc0f6e2 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/encoder_decoder.go @@ -0,0 +1,89 @@ +package sarama + +import ( + "fmt" + + "github.com/rcrowley/go-metrics" +) + +// Encoder is the interface that wraps the basic Encode method. +// Anything implementing Encoder can be turned into bytes using Kafka's encoding rules. +type encoder interface { + encode(pe packetEncoder) error +} + +// Encode takes an Encoder and turns it into bytes while potentially recording metrics. +func encode(e encoder, metricRegistry metrics.Registry) ([]byte, error) { + if e == nil { + return nil, nil + } + + var prepEnc prepEncoder + var realEnc realEncoder + + err := e.encode(&prepEnc) + if err != nil { + return nil, err + } + + if prepEnc.length < 0 || prepEnc.length > int(MaxRequestSize) { + return nil, PacketEncodingError{fmt.Sprintf("invalid request size (%d)", prepEnc.length)} + } + + realEnc.raw = make([]byte, prepEnc.length) + realEnc.registry = metricRegistry + err = e.encode(&realEnc) + if err != nil { + return nil, err + } + + return realEnc.raw, nil +} + +// Decoder is the interface that wraps the basic Decode method. +// Anything implementing Decoder can be extracted from bytes using Kafka's encoding rules. +type decoder interface { + decode(pd packetDecoder) error +} + +type versionedDecoder interface { + decode(pd packetDecoder, version int16) error +} + +// Decode takes bytes and a Decoder and fills the fields of the decoder from the bytes, +// interpreted using Kafka's encoding rules. +func decode(buf []byte, in decoder) error { + if buf == nil { + return nil + } + + helper := realDecoder{raw: buf} + err := in.decode(&helper) + if err != nil { + return err + } + + if helper.off != len(buf) { + return PacketDecodingError{"invalid length"} + } + + return nil +} + +func versionedDecode(buf []byte, in versionedDecoder, version int16) error { + if buf == nil { + return nil + } + + helper := realDecoder{raw: buf} + err := in.decode(&helper, version) + if err != nil { + return err + } + + if helper.off != len(buf) { + return PacketDecodingError{"invalid length"} + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/end_txn_request.go b/vendor/github.com/Shopify/sarama/end_txn_request.go new file mode 100644 index 00000000000..2cd9b506d3f --- /dev/null +++ b/vendor/github.com/Shopify/sarama/end_txn_request.go @@ -0,0 +1,50 @@ +package sarama + +type EndTxnRequest struct { + TransactionalID string + ProducerID int64 + ProducerEpoch int16 + TransactionResult bool +} + +func (a *EndTxnRequest) encode(pe packetEncoder) error { + if err := pe.putString(a.TransactionalID); err != nil { + return err + } + + pe.putInt64(a.ProducerID) + + pe.putInt16(a.ProducerEpoch) + + pe.putBool(a.TransactionResult) + + return nil +} + +func (a *EndTxnRequest) decode(pd packetDecoder, version int16) (err error) { + if a.TransactionalID, err = pd.getString(); err != nil { + return err + } + if a.ProducerID, err = pd.getInt64(); err != nil { + return err + } + if a.ProducerEpoch, err = pd.getInt16(); err != nil { + return err + } + if a.TransactionResult, err = pd.getBool(); err != nil { + return err + } + return nil +} + +func (a *EndTxnRequest) key() int16 { + return 26 +} + +func (a *EndTxnRequest) version() int16 { + return 0 +} + +func (a *EndTxnRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/end_txn_response.go b/vendor/github.com/Shopify/sarama/end_txn_response.go new file mode 100644 index 00000000000..33b27e33d49 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/end_txn_response.go @@ -0,0 +1,44 @@ +package sarama + +import ( + "time" +) + +type EndTxnResponse struct { + ThrottleTime time.Duration + Err KError +} + +func (e *EndTxnResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(e.ThrottleTime / time.Millisecond)) + pe.putInt16(int16(e.Err)) + return nil +} + +func (e *EndTxnResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + e.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + kerr, err := pd.getInt16() + if err != nil { + return err + } + e.Err = KError(kerr) + + return nil +} + +func (e *EndTxnResponse) key() int16 { + return 25 +} + +func (e *EndTxnResponse) version() int16 { + return 0 +} + +func (e *EndTxnResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/errors.go b/vendor/github.com/Shopify/sarama/errors.go new file mode 100644 index 00000000000..c578ef5fb43 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/errors.go @@ -0,0 +1,281 @@ +package sarama + +import ( + "errors" + "fmt" +) + +// ErrOutOfBrokers is the error returned when the client has run out of brokers to talk to because all of them errored +// or otherwise failed to respond. +var ErrOutOfBrokers = errors.New("kafka: client has run out of available brokers to talk to (Is your cluster reachable?)") + +// ErrClosedClient is the error returned when a method is called on a client that has been closed. +var ErrClosedClient = errors.New("kafka: tried to use a client that was closed") + +// ErrIncompleteResponse is the error returned when the server returns a syntactically valid response, but it does +// not contain the expected information. +var ErrIncompleteResponse = errors.New("kafka: response did not contain all the expected topic/partition blocks") + +// ErrInvalidPartition is the error returned when a partitioner returns an invalid partition index +// (meaning one outside of the range [0...numPartitions-1]). +var ErrInvalidPartition = errors.New("kafka: partitioner returned an invalid partition index") + +// ErrAlreadyConnected is the error returned when calling Open() on a Broker that is already connected or connecting. +var ErrAlreadyConnected = errors.New("kafka: broker connection already initiated") + +// ErrNotConnected is the error returned when trying to send or call Close() on a Broker that is not connected. +var ErrNotConnected = errors.New("kafka: broker not connected") + +// ErrInsufficientData is returned when decoding and the packet is truncated. This can be expected +// when requesting messages, since as an optimization the server is allowed to return a partial message at the end +// of the message set. +var ErrInsufficientData = errors.New("kafka: insufficient data to decode packet, more bytes expected") + +// ErrShuttingDown is returned when a producer receives a message during shutdown. +var ErrShuttingDown = errors.New("kafka: message received by producer in process of shutting down") + +// ErrMessageTooLarge is returned when the next message to consume is larger than the configured Consumer.Fetch.Max +var ErrMessageTooLarge = errors.New("kafka: message is larger than Consumer.Fetch.Max") + +// ErrConsumerOffsetNotAdvanced is returned when a partition consumer didn't advance its offset after parsing +// a RecordBatch. +var ErrConsumerOffsetNotAdvanced = errors.New("kafka: consumer offset was not advanced after a RecordBatch") + +// ErrControllerNotAvailable is returned when server didn't give correct controller id. May be kafka server's version +// is lower than 0.10.0.0. +var ErrControllerNotAvailable = errors.New("kafka: controller is not available") + +// ErrNoTopicsToUpdateMetadata is returned when Meta.Full is set to false but no specific topics were found to update +// the metadata. +var ErrNoTopicsToUpdateMetadata = errors.New("kafka: no specific topics to update metadata") + +// PacketEncodingError is returned from a failure while encoding a Kafka packet. This can happen, for example, +// if you try to encode a string over 2^15 characters in length, since Kafka's encoding rules do not permit that. +type PacketEncodingError struct { + Info string +} + +func (err PacketEncodingError) Error() string { + return fmt.Sprintf("kafka: error encoding packet: %s", err.Info) +} + +// PacketDecodingError is returned when there was an error (other than truncated data) decoding the Kafka broker's response. +// This can be a bad CRC or length field, or any other invalid value. +type PacketDecodingError struct { + Info string +} + +func (err PacketDecodingError) Error() string { + return fmt.Sprintf("kafka: error decoding packet: %s", err.Info) +} + +// ConfigurationError is the type of error returned from a constructor (e.g. NewClient, or NewConsumer) +// when the specified configuration is invalid. +type ConfigurationError string + +func (err ConfigurationError) Error() string { + return "kafka: invalid configuration (" + string(err) + ")" +} + +// KError is the type of error that can be returned directly by the Kafka broker. +// See https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol#AGuideToTheKafkaProtocol-ErrorCodes +type KError int16 + +// Numeric error codes returned by the Kafka server. +const ( + ErrNoError KError = 0 + ErrUnknown KError = -1 + ErrOffsetOutOfRange KError = 1 + ErrInvalidMessage KError = 2 + ErrUnknownTopicOrPartition KError = 3 + ErrInvalidMessageSize KError = 4 + ErrLeaderNotAvailable KError = 5 + ErrNotLeaderForPartition KError = 6 + ErrRequestTimedOut KError = 7 + ErrBrokerNotAvailable KError = 8 + ErrReplicaNotAvailable KError = 9 + ErrMessageSizeTooLarge KError = 10 + ErrStaleControllerEpochCode KError = 11 + ErrOffsetMetadataTooLarge KError = 12 + ErrNetworkException KError = 13 + ErrOffsetsLoadInProgress KError = 14 + ErrConsumerCoordinatorNotAvailable KError = 15 + ErrNotCoordinatorForConsumer KError = 16 + ErrInvalidTopic KError = 17 + ErrMessageSetSizeTooLarge KError = 18 + ErrNotEnoughReplicas KError = 19 + ErrNotEnoughReplicasAfterAppend KError = 20 + ErrInvalidRequiredAcks KError = 21 + ErrIllegalGeneration KError = 22 + ErrInconsistentGroupProtocol KError = 23 + ErrInvalidGroupId KError = 24 + ErrUnknownMemberId KError = 25 + ErrInvalidSessionTimeout KError = 26 + ErrRebalanceInProgress KError = 27 + ErrInvalidCommitOffsetSize KError = 28 + ErrTopicAuthorizationFailed KError = 29 + ErrGroupAuthorizationFailed KError = 30 + ErrClusterAuthorizationFailed KError = 31 + ErrInvalidTimestamp KError = 32 + ErrUnsupportedSASLMechanism KError = 33 + ErrIllegalSASLState KError = 34 + ErrUnsupportedVersion KError = 35 + ErrTopicAlreadyExists KError = 36 + ErrInvalidPartitions KError = 37 + ErrInvalidReplicationFactor KError = 38 + ErrInvalidReplicaAssignment KError = 39 + ErrInvalidConfig KError = 40 + ErrNotController KError = 41 + ErrInvalidRequest KError = 42 + ErrUnsupportedForMessageFormat KError = 43 + ErrPolicyViolation KError = 44 + ErrOutOfOrderSequenceNumber KError = 45 + ErrDuplicateSequenceNumber KError = 46 + ErrInvalidProducerEpoch KError = 47 + ErrInvalidTxnState KError = 48 + ErrInvalidProducerIDMapping KError = 49 + ErrInvalidTransactionTimeout KError = 50 + ErrConcurrentTransactions KError = 51 + ErrTransactionCoordinatorFenced KError = 52 + ErrTransactionalIDAuthorizationFailed KError = 53 + ErrSecurityDisabled KError = 54 + ErrOperationNotAttempted KError = 55 + ErrKafkaStorageError KError = 56 + ErrLogDirNotFound KError = 57 + ErrSASLAuthenticationFailed KError = 58 + ErrUnknownProducerID KError = 59 + ErrReassignmentInProgress KError = 60 +) + +func (err KError) Error() string { + // Error messages stolen/adapted from + // https://kafka.apache.org/protocol#protocol_error_codes + switch err { + case ErrNoError: + return "kafka server: Not an error, why are you printing me?" + case ErrUnknown: + return "kafka server: Unexpected (unknown?) server error." + case ErrOffsetOutOfRange: + return "kafka server: The requested offset is outside the range of offsets maintained by the server for the given topic/partition." + case ErrInvalidMessage: + return "kafka server: Message contents does not match its CRC." + case ErrUnknownTopicOrPartition: + return "kafka server: Request was for a topic or partition that does not exist on this broker." + case ErrInvalidMessageSize: + return "kafka server: The message has a negative size." + case ErrLeaderNotAvailable: + return "kafka server: In the middle of a leadership election, there is currently no leader for this partition and hence it is unavailable for writes." + case ErrNotLeaderForPartition: + return "kafka server: Tried to send a message to a replica that is not the leader for some partition. Your metadata is out of date." + case ErrRequestTimedOut: + return "kafka server: Request exceeded the user-specified time limit in the request." + case ErrBrokerNotAvailable: + return "kafka server: Broker not available. Not a client facing error, we should never receive this!!!" + case ErrReplicaNotAvailable: + return "kafka server: Replica information not available, one or more brokers are down." + case ErrMessageSizeTooLarge: + return "kafka server: Message was too large, server rejected it to avoid allocation error." + case ErrStaleControllerEpochCode: + return "kafka server: StaleControllerEpochCode (internal error code for broker-to-broker communication)." + case ErrOffsetMetadataTooLarge: + return "kafka server: Specified a string larger than the configured maximum for offset metadata." + case ErrNetworkException: + return "kafka server: The server disconnected before a response was received." + case ErrOffsetsLoadInProgress: + return "kafka server: The broker is still loading offsets after a leader change for that offset's topic partition." + case ErrConsumerCoordinatorNotAvailable: + return "kafka server: Offset's topic has not yet been created." + case ErrNotCoordinatorForConsumer: + return "kafka server: Request was for a consumer group that is not coordinated by this broker." + case ErrInvalidTopic: + return "kafka server: The request attempted to perform an operation on an invalid topic." + case ErrMessageSetSizeTooLarge: + return "kafka server: The request included message batch larger than the configured segment size on the server." + case ErrNotEnoughReplicas: + return "kafka server: Messages are rejected since there are fewer in-sync replicas than required." + case ErrNotEnoughReplicasAfterAppend: + return "kafka server: Messages are written to the log, but to fewer in-sync replicas than required." + case ErrInvalidRequiredAcks: + return "kafka server: The number of required acks is invalid (should be either -1, 0, or 1)." + case ErrIllegalGeneration: + return "kafka server: The provided generation id is not the current generation." + case ErrInconsistentGroupProtocol: + return "kafka server: The provider group protocol type is incompatible with the other members." + case ErrInvalidGroupId: + return "kafka server: The provided group id was empty." + case ErrUnknownMemberId: + return "kafka server: The provided member is not known in the current generation." + case ErrInvalidSessionTimeout: + return "kafka server: The provided session timeout is outside the allowed range." + case ErrRebalanceInProgress: + return "kafka server: A rebalance for the group is in progress. Please re-join the group." + case ErrInvalidCommitOffsetSize: + return "kafka server: The provided commit metadata was too large." + case ErrTopicAuthorizationFailed: + return "kafka server: The client is not authorized to access this topic." + case ErrGroupAuthorizationFailed: + return "kafka server: The client is not authorized to access this group." + case ErrClusterAuthorizationFailed: + return "kafka server: The client is not authorized to send this request type." + case ErrInvalidTimestamp: + return "kafka server: The timestamp of the message is out of acceptable range." + case ErrUnsupportedSASLMechanism: + return "kafka server: The broker does not support the requested SASL mechanism." + case ErrIllegalSASLState: + return "kafka server: Request is not valid given the current SASL state." + case ErrUnsupportedVersion: + return "kafka server: The version of API is not supported." + case ErrTopicAlreadyExists: + return "kafka server: Topic with this name already exists." + case ErrInvalidPartitions: + return "kafka server: Number of partitions is invalid." + case ErrInvalidReplicationFactor: + return "kafka server: Replication-factor is invalid." + case ErrInvalidReplicaAssignment: + return "kafka server: Replica assignment is invalid." + case ErrInvalidConfig: + return "kafka server: Configuration is invalid." + case ErrNotController: + return "kafka server: This is not the correct controller for this cluster." + case ErrInvalidRequest: + return "kafka server: This most likely occurs because of a request being malformed by the client library or the message was sent to an incompatible broker. See the broker logs for more details." + case ErrUnsupportedForMessageFormat: + return "kafka server: The requested operation is not supported by the message format version." + case ErrPolicyViolation: + return "kafka server: Request parameters do not satisfy the configured policy." + case ErrOutOfOrderSequenceNumber: + return "kafka server: The broker received an out of order sequence number." + case ErrDuplicateSequenceNumber: + return "kafka server: The broker received a duplicate sequence number." + case ErrInvalidProducerEpoch: + return "kafka server: Producer attempted an operation with an old epoch." + case ErrInvalidTxnState: + return "kafka server: The producer attempted a transactional operation in an invalid state." + case ErrInvalidProducerIDMapping: + return "kafka server: The producer attempted to use a producer id which is not currently assigned to its transactional id." + case ErrInvalidTransactionTimeout: + return "kafka server: The transaction timeout is larger than the maximum value allowed by the broker (as configured by max.transaction.timeout.ms)." + case ErrConcurrentTransactions: + return "kafka server: The producer attempted to update a transaction while another concurrent operation on the same transaction was ongoing." + case ErrTransactionCoordinatorFenced: + return "kafka server: The transaction coordinator sending a WriteTxnMarker is no longer the current coordinator for a given producer." + case ErrTransactionalIDAuthorizationFailed: + return "kafka server: Transactional ID authorization failed." + case ErrSecurityDisabled: + return "kafka server: Security features are disabled." + case ErrOperationNotAttempted: + return "kafka server: The broker did not attempt to execute this operation." + case ErrKafkaStorageError: + return "kafka server: Disk error when trying to access log file on the disk." + case ErrLogDirNotFound: + return "kafka server: The specified log directory is not found in the broker config." + case ErrSASLAuthenticationFailed: + return "kafka server: SASL Authentication failed." + case ErrUnknownProducerID: + return "kafka server: The broker could not locate the producer metadata associated with the Producer ID." + case ErrReassignmentInProgress: + return "kafka server: A partition reassignment is in progress." + } + + return fmt.Sprintf("Unknown error, how did this happen? Error code = %d", err) +} diff --git a/vendor/github.com/Shopify/sarama/fetch_request.go b/vendor/github.com/Shopify/sarama/fetch_request.go new file mode 100644 index 00000000000..462ab8afbb8 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/fetch_request.go @@ -0,0 +1,170 @@ +package sarama + +type fetchRequestBlock struct { + fetchOffset int64 + maxBytes int32 +} + +func (b *fetchRequestBlock) encode(pe packetEncoder) error { + pe.putInt64(b.fetchOffset) + pe.putInt32(b.maxBytes) + return nil +} + +func (b *fetchRequestBlock) decode(pd packetDecoder) (err error) { + if b.fetchOffset, err = pd.getInt64(); err != nil { + return err + } + if b.maxBytes, err = pd.getInt32(); err != nil { + return err + } + return nil +} + +// FetchRequest (API key 1) will fetch Kafka messages. Version 3 introduced the MaxBytes field. See +// https://issues.apache.org/jira/browse/KAFKA-2063 for a discussion of the issues leading up to that. The KIP is at +// https://cwiki.apache.org/confluence/display/KAFKA/KIP-74%3A+Add+Fetch+Response+Size+Limit+in+Bytes +type FetchRequest struct { + MaxWaitTime int32 + MinBytes int32 + MaxBytes int32 + Version int16 + Isolation IsolationLevel + blocks map[string]map[int32]*fetchRequestBlock +} + +type IsolationLevel int8 + +const ( + ReadUncommitted IsolationLevel = 0 + ReadCommitted IsolationLevel = 1 +) + +func (r *FetchRequest) encode(pe packetEncoder) (err error) { + pe.putInt32(-1) // replica ID is always -1 for clients + pe.putInt32(r.MaxWaitTime) + pe.putInt32(r.MinBytes) + if r.Version >= 3 { + pe.putInt32(r.MaxBytes) + } + if r.Version >= 4 { + pe.putInt8(int8(r.Isolation)) + } + err = pe.putArrayLength(len(r.blocks)) + if err != nil { + return err + } + for topic, blocks := range r.blocks { + err = pe.putString(topic) + if err != nil { + return err + } + err = pe.putArrayLength(len(blocks)) + if err != nil { + return err + } + for partition, block := range blocks { + pe.putInt32(partition) + err = block.encode(pe) + if err != nil { + return err + } + } + } + return nil +} + +func (r *FetchRequest) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + if _, err = pd.getInt32(); err != nil { + return err + } + if r.MaxWaitTime, err = pd.getInt32(); err != nil { + return err + } + if r.MinBytes, err = pd.getInt32(); err != nil { + return err + } + if r.Version >= 3 { + if r.MaxBytes, err = pd.getInt32(); err != nil { + return err + } + } + if r.Version >= 4 { + isolation, err := pd.getInt8() + if err != nil { + return err + } + r.Isolation = IsolationLevel(isolation) + } + topicCount, err := pd.getArrayLength() + if err != nil { + return err + } + if topicCount == 0 { + return nil + } + r.blocks = make(map[string]map[int32]*fetchRequestBlock) + for i := 0; i < topicCount; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + partitionCount, err := pd.getArrayLength() + if err != nil { + return err + } + r.blocks[topic] = make(map[int32]*fetchRequestBlock) + for j := 0; j < partitionCount; j++ { + partition, err := pd.getInt32() + if err != nil { + return err + } + fetchBlock := &fetchRequestBlock{} + if err = fetchBlock.decode(pd); err != nil { + return err + } + r.blocks[topic][partition] = fetchBlock + } + } + return nil +} + +func (r *FetchRequest) key() int16 { + return 1 +} + +func (r *FetchRequest) version() int16 { + return r.Version +} + +func (r *FetchRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_9_0_0 + case 2: + return V0_10_0_0 + case 3: + return V0_10_1_0 + case 4: + return V0_11_0_0 + default: + return MinVersion + } +} + +func (r *FetchRequest) AddBlock(topic string, partitionID int32, fetchOffset int64, maxBytes int32) { + if r.blocks == nil { + r.blocks = make(map[string]map[int32]*fetchRequestBlock) + } + + if r.blocks[topic] == nil { + r.blocks[topic] = make(map[int32]*fetchRequestBlock) + } + + tmp := new(fetchRequestBlock) + tmp.maxBytes = maxBytes + tmp.fetchOffset = fetchOffset + + r.blocks[topic][partitionID] = tmp +} diff --git a/vendor/github.com/Shopify/sarama/fetch_response.go b/vendor/github.com/Shopify/sarama/fetch_response.go new file mode 100644 index 00000000000..dade1c47dac --- /dev/null +++ b/vendor/github.com/Shopify/sarama/fetch_response.go @@ -0,0 +1,396 @@ +package sarama + +import ( + "time" +) + +type AbortedTransaction struct { + ProducerID int64 + FirstOffset int64 +} + +func (t *AbortedTransaction) decode(pd packetDecoder) (err error) { + if t.ProducerID, err = pd.getInt64(); err != nil { + return err + } + + if t.FirstOffset, err = pd.getInt64(); err != nil { + return err + } + + return nil +} + +func (t *AbortedTransaction) encode(pe packetEncoder) (err error) { + pe.putInt64(t.ProducerID) + pe.putInt64(t.FirstOffset) + + return nil +} + +type FetchResponseBlock struct { + Err KError + HighWaterMarkOffset int64 + LastStableOffset int64 + AbortedTransactions []*AbortedTransaction + Records *Records // deprecated: use FetchResponseBlock.Records + RecordsSet []*Records + Partial bool +} + +func (b *FetchResponseBlock) decode(pd packetDecoder, version int16) (err error) { + tmp, err := pd.getInt16() + if err != nil { + return err + } + b.Err = KError(tmp) + + b.HighWaterMarkOffset, err = pd.getInt64() + if err != nil { + return err + } + + if version >= 4 { + b.LastStableOffset, err = pd.getInt64() + if err != nil { + return err + } + + numTransact, err := pd.getArrayLength() + if err != nil { + return err + } + + if numTransact >= 0 { + b.AbortedTransactions = make([]*AbortedTransaction, numTransact) + } + + for i := 0; i < numTransact; i++ { + transact := new(AbortedTransaction) + if err = transact.decode(pd); err != nil { + return err + } + b.AbortedTransactions[i] = transact + } + } + + recordsSize, err := pd.getInt32() + if err != nil { + return err + } + + recordsDecoder, err := pd.getSubset(int(recordsSize)) + if err != nil { + return err + } + + b.RecordsSet = []*Records{} + + for recordsDecoder.remaining() > 0 { + records := &Records{} + if err := records.decode(recordsDecoder); err != nil { + // If we have at least one decoded records, this is not an error + if err == ErrInsufficientData { + if len(b.RecordsSet) == 0 { + b.Partial = true + } + break + } + return err + } + + partial, err := records.isPartial() + if err != nil { + return err + } + + n, err := records.numRecords() + if err != nil { + return err + } + + if n > 0 || (partial && len(b.RecordsSet) == 0) { + b.RecordsSet = append(b.RecordsSet, records) + + if b.Records == nil { + b.Records = records + } + } + + overflow, err := records.isOverflow() + if err != nil { + return err + } + + if partial || overflow { + break + } + } + + return nil +} + +func (b *FetchResponseBlock) numRecords() (int, error) { + sum := 0 + + for _, records := range b.RecordsSet { + count, err := records.numRecords() + if err != nil { + return 0, err + } + + sum += count + } + + return sum, nil +} + +func (b *FetchResponseBlock) isPartial() (bool, error) { + if b.Partial { + return true, nil + } + + if len(b.RecordsSet) == 1 { + return b.RecordsSet[0].isPartial() + } + + return false, nil +} + +func (b *FetchResponseBlock) encode(pe packetEncoder, version int16) (err error) { + pe.putInt16(int16(b.Err)) + + pe.putInt64(b.HighWaterMarkOffset) + + if version >= 4 { + pe.putInt64(b.LastStableOffset) + + if err = pe.putArrayLength(len(b.AbortedTransactions)); err != nil { + return err + } + for _, transact := range b.AbortedTransactions { + if err = transact.encode(pe); err != nil { + return err + } + } + } + + pe.push(&lengthField{}) + for _, records := range b.RecordsSet { + err = records.encode(pe) + if err != nil { + return err + } + } + return pe.pop() +} + +type FetchResponse struct { + Blocks map[string]map[int32]*FetchResponseBlock + ThrottleTime time.Duration + Version int16 // v1 requires 0.9+, v2 requires 0.10+ +} + +func (r *FetchResponse) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + if r.Version >= 1 { + throttle, err := pd.getInt32() + if err != nil { + return err + } + r.ThrottleTime = time.Duration(throttle) * time.Millisecond + } + + numTopics, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Blocks = make(map[string]map[int32]*FetchResponseBlock, numTopics) + for i := 0; i < numTopics; i++ { + name, err := pd.getString() + if err != nil { + return err + } + + numBlocks, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Blocks[name] = make(map[int32]*FetchResponseBlock, numBlocks) + + for j := 0; j < numBlocks; j++ { + id, err := pd.getInt32() + if err != nil { + return err + } + + block := new(FetchResponseBlock) + err = block.decode(pd, version) + if err != nil { + return err + } + r.Blocks[name][id] = block + } + } + + return nil +} + +func (r *FetchResponse) encode(pe packetEncoder) (err error) { + if r.Version >= 1 { + pe.putInt32(int32(r.ThrottleTime / time.Millisecond)) + } + + err = pe.putArrayLength(len(r.Blocks)) + if err != nil { + return err + } + + for topic, partitions := range r.Blocks { + err = pe.putString(topic) + if err != nil { + return err + } + + err = pe.putArrayLength(len(partitions)) + if err != nil { + return err + } + + for id, block := range partitions { + pe.putInt32(id) + err = block.encode(pe, r.Version) + if err != nil { + return err + } + } + + } + return nil +} + +func (r *FetchResponse) key() int16 { + return 1 +} + +func (r *FetchResponse) version() int16 { + return r.Version +} + +func (r *FetchResponse) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_9_0_0 + case 2: + return V0_10_0_0 + case 3: + return V0_10_1_0 + case 4: + return V0_11_0_0 + default: + return MinVersion + } +} + +func (r *FetchResponse) GetBlock(topic string, partition int32) *FetchResponseBlock { + if r.Blocks == nil { + return nil + } + + if r.Blocks[topic] == nil { + return nil + } + + return r.Blocks[topic][partition] +} + +func (r *FetchResponse) AddError(topic string, partition int32, err KError) { + if r.Blocks == nil { + r.Blocks = make(map[string]map[int32]*FetchResponseBlock) + } + partitions, ok := r.Blocks[topic] + if !ok { + partitions = make(map[int32]*FetchResponseBlock) + r.Blocks[topic] = partitions + } + frb, ok := partitions[partition] + if !ok { + frb = new(FetchResponseBlock) + partitions[partition] = frb + } + frb.Err = err +} + +func (r *FetchResponse) getOrCreateBlock(topic string, partition int32) *FetchResponseBlock { + if r.Blocks == nil { + r.Blocks = make(map[string]map[int32]*FetchResponseBlock) + } + partitions, ok := r.Blocks[topic] + if !ok { + partitions = make(map[int32]*FetchResponseBlock) + r.Blocks[topic] = partitions + } + frb, ok := partitions[partition] + if !ok { + frb = new(FetchResponseBlock) + partitions[partition] = frb + } + + return frb +} + +func encodeKV(key, value Encoder) ([]byte, []byte) { + var kb []byte + var vb []byte + if key != nil { + kb, _ = key.Encode() + } + if value != nil { + vb, _ = value.Encode() + } + + return kb, vb +} + +func (r *FetchResponse) AddMessage(topic string, partition int32, key, value Encoder, offset int64) { + frb := r.getOrCreateBlock(topic, partition) + kb, vb := encodeKV(key, value) + msg := &Message{Key: kb, Value: vb} + msgBlock := &MessageBlock{Msg: msg, Offset: offset} + if len(frb.RecordsSet) == 0 { + records := newLegacyRecords(&MessageSet{}) + frb.RecordsSet = []*Records{&records} + } + set := frb.RecordsSet[0].MsgSet + set.Messages = append(set.Messages, msgBlock) +} + +func (r *FetchResponse) AddRecord(topic string, partition int32, key, value Encoder, offset int64) { + frb := r.getOrCreateBlock(topic, partition) + kb, vb := encodeKV(key, value) + rec := &Record{Key: kb, Value: vb, OffsetDelta: offset} + if len(frb.RecordsSet) == 0 { + records := newDefaultRecords(&RecordBatch{Version: 2}) + frb.RecordsSet = []*Records{&records} + } + batch := frb.RecordsSet[0].RecordBatch + batch.addRecord(rec) +} + +func (r *FetchResponse) SetLastOffsetDelta(topic string, partition int32, offset int32) { + frb := r.getOrCreateBlock(topic, partition) + if len(frb.RecordsSet) == 0 { + records := newDefaultRecords(&RecordBatch{Version: 2}) + frb.RecordsSet = []*Records{&records} + } + batch := frb.RecordsSet[0].RecordBatch + batch.LastOffsetDelta = offset +} + +func (r *FetchResponse) SetLastStableOffset(topic string, partition int32, offset int64) { + frb := r.getOrCreateBlock(topic, partition) + frb.LastStableOffset = offset +} diff --git a/vendor/github.com/Shopify/sarama/find_coordinator_request.go b/vendor/github.com/Shopify/sarama/find_coordinator_request.go new file mode 100644 index 00000000000..0ab5cb5ff57 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/find_coordinator_request.go @@ -0,0 +1,61 @@ +package sarama + +type CoordinatorType int8 + +const ( + CoordinatorGroup CoordinatorType = 0 + CoordinatorTransaction CoordinatorType = 1 +) + +type FindCoordinatorRequest struct { + Version int16 + CoordinatorKey string + CoordinatorType CoordinatorType +} + +func (f *FindCoordinatorRequest) encode(pe packetEncoder) error { + if err := pe.putString(f.CoordinatorKey); err != nil { + return err + } + + if f.Version >= 1 { + pe.putInt8(int8(f.CoordinatorType)) + } + + return nil +} + +func (f *FindCoordinatorRequest) decode(pd packetDecoder, version int16) (err error) { + if f.CoordinatorKey, err = pd.getString(); err != nil { + return err + } + + if version >= 1 { + f.Version = version + coordinatorType, err := pd.getInt8() + if err != nil { + return err + } + + f.CoordinatorType = CoordinatorType(coordinatorType) + } + + return nil +} + +func (f *FindCoordinatorRequest) key() int16 { + return 10 +} + +func (f *FindCoordinatorRequest) version() int16 { + return f.Version +} + +func (f *FindCoordinatorRequest) requiredVersion() KafkaVersion { + switch f.Version { + case 1: + return V0_11_0_0 + default: + return V0_8_2_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/find_coordinator_response.go b/vendor/github.com/Shopify/sarama/find_coordinator_response.go new file mode 100644 index 00000000000..9c900e8b774 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/find_coordinator_response.go @@ -0,0 +1,92 @@ +package sarama + +import ( + "time" +) + +var NoNode = &Broker{id: -1, addr: ":-1"} + +type FindCoordinatorResponse struct { + Version int16 + ThrottleTime time.Duration + Err KError + ErrMsg *string + Coordinator *Broker +} + +func (f *FindCoordinatorResponse) decode(pd packetDecoder, version int16) (err error) { + if version >= 1 { + f.Version = version + + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + f.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + } + + tmp, err := pd.getInt16() + if err != nil { + return err + } + f.Err = KError(tmp) + + if version >= 1 { + if f.ErrMsg, err = pd.getNullableString(); err != nil { + return err + } + } + + coordinator := new(Broker) + // The version is hardcoded to 0, as version 1 of the Broker-decode + // contains the rack-field which is not present in the FindCoordinatorResponse. + if err := coordinator.decode(pd, 0); err != nil { + return err + } + if coordinator.addr == ":0" { + return nil + } + f.Coordinator = coordinator + + return nil +} + +func (f *FindCoordinatorResponse) encode(pe packetEncoder) error { + if f.Version >= 1 { + pe.putInt32(int32(f.ThrottleTime / time.Millisecond)) + } + + pe.putInt16(int16(f.Err)) + + if f.Version >= 1 { + if err := pe.putNullableString(f.ErrMsg); err != nil { + return err + } + } + + coordinator := f.Coordinator + if coordinator == nil { + coordinator = NoNode + } + if err := coordinator.encode(pe, 0); err != nil { + return err + } + return nil +} + +func (f *FindCoordinatorResponse) key() int16 { + return 10 +} + +func (f *FindCoordinatorResponse) version() int16 { + return f.Version +} + +func (f *FindCoordinatorResponse) requiredVersion() KafkaVersion { + switch f.Version { + case 1: + return V0_11_0_0 + default: + return V0_8_2_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/heartbeat_request.go b/vendor/github.com/Shopify/sarama/heartbeat_request.go new file mode 100644 index 00000000000..ce49c473972 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/heartbeat_request.go @@ -0,0 +1,47 @@ +package sarama + +type HeartbeatRequest struct { + GroupId string + GenerationId int32 + MemberId string +} + +func (r *HeartbeatRequest) encode(pe packetEncoder) error { + if err := pe.putString(r.GroupId); err != nil { + return err + } + + pe.putInt32(r.GenerationId) + + if err := pe.putString(r.MemberId); err != nil { + return err + } + + return nil +} + +func (r *HeartbeatRequest) decode(pd packetDecoder, version int16) (err error) { + if r.GroupId, err = pd.getString(); err != nil { + return + } + if r.GenerationId, err = pd.getInt32(); err != nil { + return + } + if r.MemberId, err = pd.getString(); err != nil { + return + } + + return nil +} + +func (r *HeartbeatRequest) key() int16 { + return 12 +} + +func (r *HeartbeatRequest) version() int16 { + return 0 +} + +func (r *HeartbeatRequest) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/heartbeat_response.go b/vendor/github.com/Shopify/sarama/heartbeat_response.go new file mode 100644 index 00000000000..766f5fdec6f --- /dev/null +++ b/vendor/github.com/Shopify/sarama/heartbeat_response.go @@ -0,0 +1,32 @@ +package sarama + +type HeartbeatResponse struct { + Err KError +} + +func (r *HeartbeatResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(r.Err)) + return nil +} + +func (r *HeartbeatResponse) decode(pd packetDecoder, version int16) error { + kerr, err := pd.getInt16() + if err != nil { + return err + } + r.Err = KError(kerr) + + return nil +} + +func (r *HeartbeatResponse) key() int16 { + return 12 +} + +func (r *HeartbeatResponse) version() int16 { + return 0 +} + +func (r *HeartbeatResponse) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/init_producer_id_request.go b/vendor/github.com/Shopify/sarama/init_producer_id_request.go new file mode 100644 index 00000000000..8ceb6c23255 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/init_producer_id_request.go @@ -0,0 +1,43 @@ +package sarama + +import "time" + +type InitProducerIDRequest struct { + TransactionalID *string + TransactionTimeout time.Duration +} + +func (i *InitProducerIDRequest) encode(pe packetEncoder) error { + if err := pe.putNullableString(i.TransactionalID); err != nil { + return err + } + pe.putInt32(int32(i.TransactionTimeout / time.Millisecond)) + + return nil +} + +func (i *InitProducerIDRequest) decode(pd packetDecoder, version int16) (err error) { + if i.TransactionalID, err = pd.getNullableString(); err != nil { + return err + } + + timeout, err := pd.getInt32() + if err != nil { + return err + } + i.TransactionTimeout = time.Duration(timeout) * time.Millisecond + + return nil +} + +func (i *InitProducerIDRequest) key() int16 { + return 22 +} + +func (i *InitProducerIDRequest) version() int16 { + return 0 +} + +func (i *InitProducerIDRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/init_producer_id_response.go b/vendor/github.com/Shopify/sarama/init_producer_id_response.go new file mode 100644 index 00000000000..1b32eb085b2 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/init_producer_id_response.go @@ -0,0 +1,55 @@ +package sarama + +import "time" + +type InitProducerIDResponse struct { + ThrottleTime time.Duration + Err KError + ProducerID int64 + ProducerEpoch int16 +} + +func (i *InitProducerIDResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(i.ThrottleTime / time.Millisecond)) + pe.putInt16(int16(i.Err)) + pe.putInt64(i.ProducerID) + pe.putInt16(i.ProducerEpoch) + + return nil +} + +func (i *InitProducerIDResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + i.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + kerr, err := pd.getInt16() + if err != nil { + return err + } + i.Err = KError(kerr) + + if i.ProducerID, err = pd.getInt64(); err != nil { + return err + } + + if i.ProducerEpoch, err = pd.getInt16(); err != nil { + return err + } + + return nil +} + +func (i *InitProducerIDResponse) key() int16 { + return 22 +} + +func (i *InitProducerIDResponse) version() int16 { + return 0 +} + +func (i *InitProducerIDResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/join_group_request.go b/vendor/github.com/Shopify/sarama/join_group_request.go new file mode 100644 index 00000000000..97e9299ea1a --- /dev/null +++ b/vendor/github.com/Shopify/sarama/join_group_request.go @@ -0,0 +1,163 @@ +package sarama + +type GroupProtocol struct { + Name string + Metadata []byte +} + +func (p *GroupProtocol) decode(pd packetDecoder) (err error) { + p.Name, err = pd.getString() + if err != nil { + return err + } + p.Metadata, err = pd.getBytes() + return err +} + +func (p *GroupProtocol) encode(pe packetEncoder) (err error) { + if err := pe.putString(p.Name); err != nil { + return err + } + if err := pe.putBytes(p.Metadata); err != nil { + return err + } + return nil +} + +type JoinGroupRequest struct { + Version int16 + GroupId string + SessionTimeout int32 + RebalanceTimeout int32 + MemberId string + ProtocolType string + GroupProtocols map[string][]byte // deprecated; use OrderedGroupProtocols + OrderedGroupProtocols []*GroupProtocol +} + +func (r *JoinGroupRequest) encode(pe packetEncoder) error { + if err := pe.putString(r.GroupId); err != nil { + return err + } + pe.putInt32(r.SessionTimeout) + if r.Version >= 1 { + pe.putInt32(r.RebalanceTimeout) + } + if err := pe.putString(r.MemberId); err != nil { + return err + } + if err := pe.putString(r.ProtocolType); err != nil { + return err + } + + if len(r.GroupProtocols) > 0 { + if len(r.OrderedGroupProtocols) > 0 { + return PacketDecodingError{"cannot specify both GroupProtocols and OrderedGroupProtocols on JoinGroupRequest"} + } + + if err := pe.putArrayLength(len(r.GroupProtocols)); err != nil { + return err + } + for name, metadata := range r.GroupProtocols { + if err := pe.putString(name); err != nil { + return err + } + if err := pe.putBytes(metadata); err != nil { + return err + } + } + } else { + if err := pe.putArrayLength(len(r.OrderedGroupProtocols)); err != nil { + return err + } + for _, protocol := range r.OrderedGroupProtocols { + if err := protocol.encode(pe); err != nil { + return err + } + } + } + + return nil +} + +func (r *JoinGroupRequest) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + if r.GroupId, err = pd.getString(); err != nil { + return + } + + if r.SessionTimeout, err = pd.getInt32(); err != nil { + return + } + + if version >= 1 { + if r.RebalanceTimeout, err = pd.getInt32(); err != nil { + return err + } + } + + if r.MemberId, err = pd.getString(); err != nil { + return + } + + if r.ProtocolType, err = pd.getString(); err != nil { + return + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + if n == 0 { + return nil + } + + r.GroupProtocols = make(map[string][]byte) + for i := 0; i < n; i++ { + protocol := &GroupProtocol{} + if err := protocol.decode(pd); err != nil { + return err + } + r.GroupProtocols[protocol.Name] = protocol.Metadata + r.OrderedGroupProtocols = append(r.OrderedGroupProtocols, protocol) + } + + return nil +} + +func (r *JoinGroupRequest) key() int16 { + return 11 +} + +func (r *JoinGroupRequest) version() int16 { + return r.Version +} + +func (r *JoinGroupRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 2: + return V0_11_0_0 + case 1: + return V0_10_1_0 + default: + return V0_9_0_0 + } +} + +func (r *JoinGroupRequest) AddGroupProtocol(name string, metadata []byte) { + r.OrderedGroupProtocols = append(r.OrderedGroupProtocols, &GroupProtocol{ + Name: name, + Metadata: metadata, + }) +} + +func (r *JoinGroupRequest) AddGroupProtocolMetadata(name string, metadata *ConsumerGroupMemberMetadata) error { + bin, err := encode(metadata, nil) + if err != nil { + return err + } + + r.AddGroupProtocol(name, bin) + return nil +} diff --git a/vendor/github.com/Shopify/sarama/join_group_response.go b/vendor/github.com/Shopify/sarama/join_group_response.go new file mode 100644 index 00000000000..5752acc8aeb --- /dev/null +++ b/vendor/github.com/Shopify/sarama/join_group_response.go @@ -0,0 +1,135 @@ +package sarama + +type JoinGroupResponse struct { + Version int16 + ThrottleTime int32 + Err KError + GenerationId int32 + GroupProtocol string + LeaderId string + MemberId string + Members map[string][]byte +} + +func (r *JoinGroupResponse) GetMembers() (map[string]ConsumerGroupMemberMetadata, error) { + members := make(map[string]ConsumerGroupMemberMetadata, len(r.Members)) + for id, bin := range r.Members { + meta := new(ConsumerGroupMemberMetadata) + if err := decode(bin, meta); err != nil { + return nil, err + } + members[id] = *meta + } + return members, nil +} + +func (r *JoinGroupResponse) encode(pe packetEncoder) error { + if r.Version >= 2 { + pe.putInt32(r.ThrottleTime) + } + pe.putInt16(int16(r.Err)) + pe.putInt32(r.GenerationId) + + if err := pe.putString(r.GroupProtocol); err != nil { + return err + } + if err := pe.putString(r.LeaderId); err != nil { + return err + } + if err := pe.putString(r.MemberId); err != nil { + return err + } + + if err := pe.putArrayLength(len(r.Members)); err != nil { + return err + } + + for memberId, memberMetadata := range r.Members { + if err := pe.putString(memberId); err != nil { + return err + } + + if err := pe.putBytes(memberMetadata); err != nil { + return err + } + } + + return nil +} + +func (r *JoinGroupResponse) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + if version >= 2 { + if r.ThrottleTime, err = pd.getInt32(); err != nil { + return + } + } + + kerr, err := pd.getInt16() + if err != nil { + return err + } + + r.Err = KError(kerr) + + if r.GenerationId, err = pd.getInt32(); err != nil { + return + } + + if r.GroupProtocol, err = pd.getString(); err != nil { + return + } + + if r.LeaderId, err = pd.getString(); err != nil { + return + } + + if r.MemberId, err = pd.getString(); err != nil { + return + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + if n == 0 { + return nil + } + + r.Members = make(map[string][]byte) + for i := 0; i < n; i++ { + memberId, err := pd.getString() + if err != nil { + return err + } + + memberMetadata, err := pd.getBytes() + if err != nil { + return err + } + + r.Members[memberId] = memberMetadata + } + + return nil +} + +func (r *JoinGroupResponse) key() int16 { + return 11 +} + +func (r *JoinGroupResponse) version() int16 { + return r.Version +} + +func (r *JoinGroupResponse) requiredVersion() KafkaVersion { + switch r.Version { + case 2: + return V0_11_0_0 + case 1: + return V0_10_1_0 + default: + return V0_9_0_0 + } +} diff --git a/vendor/github.com/Shopify/sarama/leave_group_request.go b/vendor/github.com/Shopify/sarama/leave_group_request.go new file mode 100644 index 00000000000..e177427482f --- /dev/null +++ b/vendor/github.com/Shopify/sarama/leave_group_request.go @@ -0,0 +1,40 @@ +package sarama + +type LeaveGroupRequest struct { + GroupId string + MemberId string +} + +func (r *LeaveGroupRequest) encode(pe packetEncoder) error { + if err := pe.putString(r.GroupId); err != nil { + return err + } + if err := pe.putString(r.MemberId); err != nil { + return err + } + + return nil +} + +func (r *LeaveGroupRequest) decode(pd packetDecoder, version int16) (err error) { + if r.GroupId, err = pd.getString(); err != nil { + return + } + if r.MemberId, err = pd.getString(); err != nil { + return + } + + return nil +} + +func (r *LeaveGroupRequest) key() int16 { + return 13 +} + +func (r *LeaveGroupRequest) version() int16 { + return 0 +} + +func (r *LeaveGroupRequest) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/leave_group_response.go b/vendor/github.com/Shopify/sarama/leave_group_response.go new file mode 100644 index 00000000000..d60c626da01 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/leave_group_response.go @@ -0,0 +1,32 @@ +package sarama + +type LeaveGroupResponse struct { + Err KError +} + +func (r *LeaveGroupResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(r.Err)) + return nil +} + +func (r *LeaveGroupResponse) decode(pd packetDecoder, version int16) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + r.Err = KError(kerr) + + return nil +} + +func (r *LeaveGroupResponse) key() int16 { + return 13 +} + +func (r *LeaveGroupResponse) version() int16 { + return 0 +} + +func (r *LeaveGroupResponse) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/length_field.go b/vendor/github.com/Shopify/sarama/length_field.go new file mode 100644 index 00000000000..da199a70a0e --- /dev/null +++ b/vendor/github.com/Shopify/sarama/length_field.go @@ -0,0 +1,82 @@ +package sarama + +import "encoding/binary" + +// LengthField implements the PushEncoder and PushDecoder interfaces for calculating 4-byte lengths. +type lengthField struct { + startOffset int + length int32 +} + +func (l *lengthField) decode(pd packetDecoder) error { + var err error + l.length, err = pd.getInt32() + if err != nil { + return err + } + if l.length > int32(pd.remaining()) { + return ErrInsufficientData + } + return nil +} + +func (l *lengthField) saveOffset(in int) { + l.startOffset = in +} + +func (l *lengthField) reserveLength() int { + return 4 +} + +func (l *lengthField) run(curOffset int, buf []byte) error { + binary.BigEndian.PutUint32(buf[l.startOffset:], uint32(curOffset-l.startOffset-4)) + return nil +} + +func (l *lengthField) check(curOffset int, buf []byte) error { + if int32(curOffset-l.startOffset-4) != l.length { + return PacketDecodingError{"length field invalid"} + } + + return nil +} + +type varintLengthField struct { + startOffset int + length int64 +} + +func (l *varintLengthField) decode(pd packetDecoder) error { + var err error + l.length, err = pd.getVarint() + return err +} + +func (l *varintLengthField) saveOffset(in int) { + l.startOffset = in +} + +func (l *varintLengthField) adjustLength(currOffset int) int { + oldFieldSize := l.reserveLength() + l.length = int64(currOffset - l.startOffset - oldFieldSize) + + return l.reserveLength() - oldFieldSize +} + +func (l *varintLengthField) reserveLength() int { + var tmp [binary.MaxVarintLen64]byte + return binary.PutVarint(tmp[:], l.length) +} + +func (l *varintLengthField) run(curOffset int, buf []byte) error { + binary.PutVarint(buf[l.startOffset:], l.length) + return nil +} + +func (l *varintLengthField) check(curOffset int, buf []byte) error { + if int64(curOffset-l.startOffset-l.reserveLength()) != l.length { + return PacketDecodingError{"length field invalid"} + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/list_groups_request.go b/vendor/github.com/Shopify/sarama/list_groups_request.go new file mode 100644 index 00000000000..3b16abf7fa8 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/list_groups_request.go @@ -0,0 +1,24 @@ +package sarama + +type ListGroupsRequest struct { +} + +func (r *ListGroupsRequest) encode(pe packetEncoder) error { + return nil +} + +func (r *ListGroupsRequest) decode(pd packetDecoder, version int16) (err error) { + return nil +} + +func (r *ListGroupsRequest) key() int16 { + return 16 +} + +func (r *ListGroupsRequest) version() int16 { + return 0 +} + +func (r *ListGroupsRequest) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/list_groups_response.go b/vendor/github.com/Shopify/sarama/list_groups_response.go new file mode 100644 index 00000000000..56115d4c75a --- /dev/null +++ b/vendor/github.com/Shopify/sarama/list_groups_response.go @@ -0,0 +1,69 @@ +package sarama + +type ListGroupsResponse struct { + Err KError + Groups map[string]string +} + +func (r *ListGroupsResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(r.Err)) + + if err := pe.putArrayLength(len(r.Groups)); err != nil { + return err + } + for groupId, protocolType := range r.Groups { + if err := pe.putString(groupId); err != nil { + return err + } + if err := pe.putString(protocolType); err != nil { + return err + } + } + + return nil +} + +func (r *ListGroupsResponse) decode(pd packetDecoder, version int16) error { + kerr, err := pd.getInt16() + if err != nil { + return err + } + + r.Err = KError(kerr) + + n, err := pd.getArrayLength() + if err != nil { + return err + } + if n == 0 { + return nil + } + + r.Groups = make(map[string]string) + for i := 0; i < n; i++ { + groupId, err := pd.getString() + if err != nil { + return err + } + protocolType, err := pd.getString() + if err != nil { + return err + } + + r.Groups[groupId] = protocolType + } + + return nil +} + +func (r *ListGroupsResponse) key() int16 { + return 16 +} + +func (r *ListGroupsResponse) version() int16 { + return 0 +} + +func (r *ListGroupsResponse) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/message.go b/vendor/github.com/Shopify/sarama/message.go new file mode 100644 index 00000000000..fecdbfdef75 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/message.go @@ -0,0 +1,223 @@ +package sarama + +import ( + "bytes" + "compress/gzip" + "fmt" + "io/ioutil" + "time" + + "github.com/eapache/go-xerial-snappy" + "github.com/pierrec/lz4" +) + +// CompressionCodec represents the various compression codecs recognized by Kafka in messages. +type CompressionCodec int8 + +// only the last two bits are really used +const compressionCodecMask int8 = 0x03 + +const ( + CompressionNone CompressionCodec = 0 + CompressionGZIP CompressionCodec = 1 + CompressionSnappy CompressionCodec = 2 + CompressionLZ4 CompressionCodec = 3 +) + +func (cc CompressionCodec) String() string { + return []string{ + "none", + "gzip", + "snappy", + "lz4", + }[int(cc)] +} + +// CompressionLevelDefault is the constant to use in CompressionLevel +// to have the default compression level for any codec. The value is picked +// that we don't use any existing compression levels. +const CompressionLevelDefault = -1000 + +type Message struct { + Codec CompressionCodec // codec used to compress the message contents + CompressionLevel int // compression level + Key []byte // the message key, may be nil + Value []byte // the message contents + Set *MessageSet // the message set a message might wrap + Version int8 // v1 requires Kafka 0.10 + Timestamp time.Time // the timestamp of the message (version 1+ only) + + compressedCache []byte + compressedSize int // used for computing the compression ratio metrics +} + +func (m *Message) encode(pe packetEncoder) error { + pe.push(newCRC32Field(crcIEEE)) + + pe.putInt8(m.Version) + + attributes := int8(m.Codec) & compressionCodecMask + pe.putInt8(attributes) + + if m.Version >= 1 { + if err := (Timestamp{&m.Timestamp}).encode(pe); err != nil { + return err + } + } + + err := pe.putBytes(m.Key) + if err != nil { + return err + } + + var payload []byte + + if m.compressedCache != nil { + payload = m.compressedCache + m.compressedCache = nil + } else if m.Value != nil { + switch m.Codec { + case CompressionNone: + payload = m.Value + case CompressionGZIP: + var buf bytes.Buffer + var writer *gzip.Writer + if m.CompressionLevel != CompressionLevelDefault { + writer, err = gzip.NewWriterLevel(&buf, m.CompressionLevel) + if err != nil { + return err + } + } else { + writer = gzip.NewWriter(&buf) + } + if _, err = writer.Write(m.Value); err != nil { + return err + } + if err = writer.Close(); err != nil { + return err + } + m.compressedCache = buf.Bytes() + payload = m.compressedCache + case CompressionSnappy: + tmp := snappy.Encode(m.Value) + m.compressedCache = tmp + payload = m.compressedCache + case CompressionLZ4: + var buf bytes.Buffer + writer := lz4.NewWriter(&buf) + if _, err = writer.Write(m.Value); err != nil { + return err + } + if err = writer.Close(); err != nil { + return err + } + m.compressedCache = buf.Bytes() + payload = m.compressedCache + + default: + return PacketEncodingError{fmt.Sprintf("unsupported compression codec (%d)", m.Codec)} + } + // Keep in mind the compressed payload size for metric gathering + m.compressedSize = len(payload) + } + + if err = pe.putBytes(payload); err != nil { + return err + } + + return pe.pop() +} + +func (m *Message) decode(pd packetDecoder) (err error) { + err = pd.push(newCRC32Field(crcIEEE)) + if err != nil { + return err + } + + m.Version, err = pd.getInt8() + if err != nil { + return err + } + + if m.Version > 1 { + return PacketDecodingError{fmt.Sprintf("unknown magic byte (%v)", m.Version)} + } + + attribute, err := pd.getInt8() + if err != nil { + return err + } + m.Codec = CompressionCodec(attribute & compressionCodecMask) + + if m.Version == 1 { + if err := (Timestamp{&m.Timestamp}).decode(pd); err != nil { + return err + } + } + + m.Key, err = pd.getBytes() + if err != nil { + return err + } + + m.Value, err = pd.getBytes() + if err != nil { + return err + } + + // Required for deep equal assertion during tests but might be useful + // for future metrics about the compression ratio in fetch requests + m.compressedSize = len(m.Value) + + switch m.Codec { + case CompressionNone: + // nothing to do + case CompressionGZIP: + if m.Value == nil { + break + } + reader, err := gzip.NewReader(bytes.NewReader(m.Value)) + if err != nil { + return err + } + if m.Value, err = ioutil.ReadAll(reader); err != nil { + return err + } + if err := m.decodeSet(); err != nil { + return err + } + case CompressionSnappy: + if m.Value == nil { + break + } + if m.Value, err = snappy.Decode(m.Value); err != nil { + return err + } + if err := m.decodeSet(); err != nil { + return err + } + case CompressionLZ4: + if m.Value == nil { + break + } + reader := lz4.NewReader(bytes.NewReader(m.Value)) + if m.Value, err = ioutil.ReadAll(reader); err != nil { + return err + } + if err := m.decodeSet(); err != nil { + return err + } + + default: + return PacketDecodingError{fmt.Sprintf("invalid compression specified (%d)", m.Codec)} + } + + return pd.pop() +} + +// decodes a message set from a previousy encoded bulk-message +func (m *Message) decodeSet() (err error) { + pd := realDecoder{raw: m.Value} + m.Set = &MessageSet{} + return m.Set.decode(&pd) +} diff --git a/vendor/github.com/Shopify/sarama/message_set.go b/vendor/github.com/Shopify/sarama/message_set.go new file mode 100644 index 00000000000..600c7c4dfb7 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/message_set.go @@ -0,0 +1,108 @@ +package sarama + +type MessageBlock struct { + Offset int64 + Msg *Message +} + +// Messages convenience helper which returns either all the +// messages that are wrapped in this block +func (msb *MessageBlock) Messages() []*MessageBlock { + if msb.Msg.Set != nil { + return msb.Msg.Set.Messages + } + return []*MessageBlock{msb} +} + +func (msb *MessageBlock) encode(pe packetEncoder) error { + pe.putInt64(msb.Offset) + pe.push(&lengthField{}) + err := msb.Msg.encode(pe) + if err != nil { + return err + } + return pe.pop() +} + +func (msb *MessageBlock) decode(pd packetDecoder) (err error) { + if msb.Offset, err = pd.getInt64(); err != nil { + return err + } + + if err = pd.push(&lengthField{}); err != nil { + return err + } + + msb.Msg = new(Message) + if err = msb.Msg.decode(pd); err != nil { + return err + } + + if err = pd.pop(); err != nil { + return err + } + + return nil +} + +type MessageSet struct { + PartialTrailingMessage bool // whether the set on the wire contained an incomplete trailing MessageBlock + OverflowMessage bool // whether the set on the wire contained an overflow message + Messages []*MessageBlock +} + +func (ms *MessageSet) encode(pe packetEncoder) error { + for i := range ms.Messages { + err := ms.Messages[i].encode(pe) + if err != nil { + return err + } + } + return nil +} + +func (ms *MessageSet) decode(pd packetDecoder) (err error) { + ms.Messages = nil + + for pd.remaining() > 0 { + magic, err := magicValue(pd) + if err != nil { + if err == ErrInsufficientData { + ms.PartialTrailingMessage = true + return nil + } + return err + } + + if magic > 1 { + return nil + } + + msb := new(MessageBlock) + err = msb.decode(pd) + switch err { + case nil: + ms.Messages = append(ms.Messages, msb) + case ErrInsufficientData: + // As an optimization the server is allowed to return a partial message at the + // end of the message set. Clients should handle this case. So we just ignore such things. + if msb.Offset == -1 { + // This is an overflow message caused by chunked down conversion + ms.OverflowMessage = true + } else { + ms.PartialTrailingMessage = true + } + return nil + default: + return err + } + } + + return nil +} + +func (ms *MessageSet) addMessage(msg *Message) { + block := new(MessageBlock) + block.Msg = msg + ms.Messages = append(ms.Messages, block) +} diff --git a/vendor/github.com/Shopify/sarama/metadata_request.go b/vendor/github.com/Shopify/sarama/metadata_request.go new file mode 100644 index 00000000000..17dc4289a3a --- /dev/null +++ b/vendor/github.com/Shopify/sarama/metadata_request.go @@ -0,0 +1,88 @@ +package sarama + +type MetadataRequest struct { + Version int16 + Topics []string + AllowAutoTopicCreation bool +} + +func (r *MetadataRequest) encode(pe packetEncoder) error { + if r.Version < 0 || r.Version > 5 { + return PacketEncodingError{"invalid or unsupported MetadataRequest version field"} + } + if r.Version == 0 || len(r.Topics) > 0 { + err := pe.putArrayLength(len(r.Topics)) + if err != nil { + return err + } + + for i := range r.Topics { + err = pe.putString(r.Topics[i]) + if err != nil { + return err + } + } + } else { + pe.putInt32(-1) + } + if r.Version > 3 { + pe.putBool(r.AllowAutoTopicCreation) + } + return nil +} + +func (r *MetadataRequest) decode(pd packetDecoder, version int16) error { + r.Version = version + size, err := pd.getInt32() + if err != nil { + return err + } + if size < 0 { + return nil + } else { + topicCount := size + if topicCount == 0 { + return nil + } + + r.Topics = make([]string, topicCount) + for i := range r.Topics { + topic, err := pd.getString() + if err != nil { + return err + } + r.Topics[i] = topic + } + } + if r.Version > 3 { + autoCreation, err := pd.getBool() + if err != nil { + return err + } + r.AllowAutoTopicCreation = autoCreation + } + return nil +} + +func (r *MetadataRequest) key() int16 { + return 3 +} + +func (r *MetadataRequest) version() int16 { + return r.Version +} + +func (r *MetadataRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_10_0_0 + case 2: + return V0_10_1_0 + case 3, 4: + return V0_11_0_0 + case 5: + return V1_0_0_0 + default: + return MinVersion + } +} diff --git a/vendor/github.com/Shopify/sarama/metadata_response.go b/vendor/github.com/Shopify/sarama/metadata_response.go new file mode 100644 index 00000000000..c402d05fa37 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/metadata_response.go @@ -0,0 +1,321 @@ +package sarama + +type PartitionMetadata struct { + Err KError + ID int32 + Leader int32 + Replicas []int32 + Isr []int32 + OfflineReplicas []int32 +} + +func (pm *PartitionMetadata) decode(pd packetDecoder, version int16) (err error) { + tmp, err := pd.getInt16() + if err != nil { + return err + } + pm.Err = KError(tmp) + + pm.ID, err = pd.getInt32() + if err != nil { + return err + } + + pm.Leader, err = pd.getInt32() + if err != nil { + return err + } + + pm.Replicas, err = pd.getInt32Array() + if err != nil { + return err + } + + pm.Isr, err = pd.getInt32Array() + if err != nil { + return err + } + + if version >= 5 { + pm.OfflineReplicas, err = pd.getInt32Array() + if err != nil { + return err + } + } + + return nil +} + +func (pm *PartitionMetadata) encode(pe packetEncoder, version int16) (err error) { + pe.putInt16(int16(pm.Err)) + pe.putInt32(pm.ID) + pe.putInt32(pm.Leader) + + err = pe.putInt32Array(pm.Replicas) + if err != nil { + return err + } + + err = pe.putInt32Array(pm.Isr) + if err != nil { + return err + } + + if version >= 5 { + err = pe.putInt32Array(pm.OfflineReplicas) + if err != nil { + return err + } + } + + return nil +} + +type TopicMetadata struct { + Err KError + Name string + IsInternal bool // Only valid for Version >= 1 + Partitions []*PartitionMetadata +} + +func (tm *TopicMetadata) decode(pd packetDecoder, version int16) (err error) { + tmp, err := pd.getInt16() + if err != nil { + return err + } + tm.Err = KError(tmp) + + tm.Name, err = pd.getString() + if err != nil { + return err + } + + if version >= 1 { + tm.IsInternal, err = pd.getBool() + if err != nil { + return err + } + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + tm.Partitions = make([]*PartitionMetadata, n) + for i := 0; i < n; i++ { + tm.Partitions[i] = new(PartitionMetadata) + err = tm.Partitions[i].decode(pd, version) + if err != nil { + return err + } + } + + return nil +} + +func (tm *TopicMetadata) encode(pe packetEncoder, version int16) (err error) { + pe.putInt16(int16(tm.Err)) + + err = pe.putString(tm.Name) + if err != nil { + return err + } + + if version >= 1 { + pe.putBool(tm.IsInternal) + } + + err = pe.putArrayLength(len(tm.Partitions)) + if err != nil { + return err + } + + for _, pm := range tm.Partitions { + err = pm.encode(pe, version) + if err != nil { + return err + } + } + + return nil +} + +type MetadataResponse struct { + Version int16 + ThrottleTimeMs int32 + Brokers []*Broker + ClusterID *string + ControllerID int32 + Topics []*TopicMetadata +} + +func (r *MetadataResponse) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + if version >= 3 { + r.ThrottleTimeMs, err = pd.getInt32() + if err != nil { + return err + } + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Brokers = make([]*Broker, n) + for i := 0; i < n; i++ { + r.Brokers[i] = new(Broker) + err = r.Brokers[i].decode(pd, version) + if err != nil { + return err + } + } + + if version >= 2 { + r.ClusterID, err = pd.getNullableString() + if err != nil { + return err + } + } + + if version >= 1 { + r.ControllerID, err = pd.getInt32() + if err != nil { + return err + } + } else { + r.ControllerID = -1 + } + + n, err = pd.getArrayLength() + if err != nil { + return err + } + + r.Topics = make([]*TopicMetadata, n) + for i := 0; i < n; i++ { + r.Topics[i] = new(TopicMetadata) + err = r.Topics[i].decode(pd, version) + if err != nil { + return err + } + } + + return nil +} + +func (r *MetadataResponse) encode(pe packetEncoder) error { + if r.Version >= 3 { + pe.putInt32(r.ThrottleTimeMs) + } + + err := pe.putArrayLength(len(r.Brokers)) + if err != nil { + return err + } + for _, broker := range r.Brokers { + err = broker.encode(pe, r.Version) + if err != nil { + return err + } + } + + if r.Version >= 2 { + err := pe.putNullableString(r.ClusterID) + if err != nil { + return err + } + } + + if r.Version >= 1 { + pe.putInt32(r.ControllerID) + } + + err = pe.putArrayLength(len(r.Topics)) + if err != nil { + return err + } + for _, tm := range r.Topics { + err = tm.encode(pe, r.Version) + if err != nil { + return err + } + } + + return nil +} + +func (r *MetadataResponse) key() int16 { + return 3 +} + +func (r *MetadataResponse) version() int16 { + return r.Version +} + +func (r *MetadataResponse) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_10_0_0 + case 2: + return V0_10_1_0 + case 3, 4: + return V0_11_0_0 + case 5: + return V1_0_0_0 + default: + return MinVersion + } +} + +// testing API + +func (r *MetadataResponse) AddBroker(addr string, id int32) { + r.Brokers = append(r.Brokers, &Broker{id: id, addr: addr}) +} + +func (r *MetadataResponse) AddTopic(topic string, err KError) *TopicMetadata { + var tmatch *TopicMetadata + + for _, tm := range r.Topics { + if tm.Name == topic { + tmatch = tm + goto foundTopic + } + } + + tmatch = new(TopicMetadata) + tmatch.Name = topic + r.Topics = append(r.Topics, tmatch) + +foundTopic: + + tmatch.Err = err + return tmatch +} + +func (r *MetadataResponse) AddTopicPartition(topic string, partition, brokerID int32, replicas, isr []int32, err KError) { + tmatch := r.AddTopic(topic, ErrNoError) + var pmatch *PartitionMetadata + + for _, pm := range tmatch.Partitions { + if pm.ID == partition { + pmatch = pm + goto foundPartition + } + } + + pmatch = new(PartitionMetadata) + pmatch.ID = partition + tmatch.Partitions = append(tmatch.Partitions, pmatch) + +foundPartition: + + pmatch.Leader = brokerID + pmatch.Replicas = replicas + pmatch.Isr = isr + pmatch.Err = err + +} diff --git a/vendor/github.com/Shopify/sarama/metrics.go b/vendor/github.com/Shopify/sarama/metrics.go new file mode 100644 index 00000000000..4869708e944 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/metrics.go @@ -0,0 +1,51 @@ +package sarama + +import ( + "fmt" + "strings" + + "github.com/rcrowley/go-metrics" +) + +// Use exponentially decaying reservoir for sampling histograms with the same defaults as the Java library: +// 1028 elements, which offers a 99.9% confidence level with a 5% margin of error assuming a normal distribution, +// and an alpha factor of 0.015, which heavily biases the reservoir to the past 5 minutes of measurements. +// See https://github.com/dropwizard/metrics/blob/v3.1.0/metrics-core/src/main/java/com/codahale/metrics/ExponentiallyDecayingReservoir.java#L38 +const ( + metricsReservoirSize = 1028 + metricsAlphaFactor = 0.015 +) + +func getOrRegisterHistogram(name string, r metrics.Registry) metrics.Histogram { + return r.GetOrRegister(name, func() metrics.Histogram { + return metrics.NewHistogram(metrics.NewExpDecaySample(metricsReservoirSize, metricsAlphaFactor)) + }).(metrics.Histogram) +} + +func getMetricNameForBroker(name string, broker *Broker) string { + // Use broker id like the Java client as it does not contain '.' or ':' characters that + // can be interpreted as special character by monitoring tool (e.g. Graphite) + return fmt.Sprintf(name+"-for-broker-%d", broker.ID()) +} + +func getOrRegisterBrokerMeter(name string, broker *Broker, r metrics.Registry) metrics.Meter { + return metrics.GetOrRegisterMeter(getMetricNameForBroker(name, broker), r) +} + +func getOrRegisterBrokerHistogram(name string, broker *Broker, r metrics.Registry) metrics.Histogram { + return getOrRegisterHistogram(getMetricNameForBroker(name, broker), r) +} + +func getMetricNameForTopic(name string, topic string) string { + // Convert dot to _ since reporters like Graphite typically use dot to represent hierarchy + // cf. KAFKA-1902 and KAFKA-2337 + return fmt.Sprintf(name+"-for-topic-%s", strings.Replace(topic, ".", "_", -1)) +} + +func getOrRegisterTopicMeter(name string, topic string, r metrics.Registry) metrics.Meter { + return metrics.GetOrRegisterMeter(getMetricNameForTopic(name, topic), r) +} + +func getOrRegisterTopicHistogram(name string, topic string, r metrics.Registry) metrics.Histogram { + return getOrRegisterHistogram(getMetricNameForTopic(name, topic), r) +} diff --git a/vendor/github.com/Shopify/sarama/mockbroker.go b/vendor/github.com/Shopify/sarama/mockbroker.go new file mode 100644 index 00000000000..55ef1e2920f --- /dev/null +++ b/vendor/github.com/Shopify/sarama/mockbroker.go @@ -0,0 +1,330 @@ +package sarama + +import ( + "bytes" + "encoding/binary" + "fmt" + "io" + "net" + "reflect" + "strconv" + "sync" + "time" + + "github.com/davecgh/go-spew/spew" +) + +const ( + expectationTimeout = 500 * time.Millisecond +) + +type requestHandlerFunc func(req *request) (res encoder) + +// RequestNotifierFunc is invoked when a mock broker processes a request successfully +// and will provides the number of bytes read and written. +type RequestNotifierFunc func(bytesRead, bytesWritten int) + +// MockBroker is a mock Kafka broker that is used in unit tests. It is exposed +// to facilitate testing of higher level or specialized consumers and producers +// built on top of Sarama. Note that it does not 'mimic' the Kafka API protocol, +// but rather provides a facility to do that. It takes care of the TCP +// transport, request unmarshaling, response marshaling, and makes it the test +// writer responsibility to program correct according to the Kafka API protocol +// MockBroker behaviour. +// +// MockBroker is implemented as a TCP server listening on a kernel-selected +// localhost port that can accept many connections. It reads Kafka requests +// from that connection and returns responses programmed by the SetHandlerByMap +// function. If a MockBroker receives a request that it has no programmed +// response for, then it returns nothing and the request times out. +// +// A set of MockRequest builders to define mappings used by MockBroker is +// provided by Sarama. But users can develop MockRequests of their own and use +// them along with or instead of the standard ones. +// +// When running tests with MockBroker it is strongly recommended to specify +// a timeout to `go test` so that if the broker hangs waiting for a response, +// the test panics. +// +// It is not necessary to prefix message length or correlation ID to your +// response bytes, the server does that automatically as a convenience. +type MockBroker struct { + brokerID int32 + port int32 + closing chan none + stopper chan none + expectations chan encoder + listener net.Listener + t TestReporter + latency time.Duration + handler requestHandlerFunc + notifier RequestNotifierFunc + history []RequestResponse + lock sync.Mutex +} + +// RequestResponse represents a Request/Response pair processed by MockBroker. +type RequestResponse struct { + Request protocolBody + Response encoder +} + +// SetLatency makes broker pause for the specified period every time before +// replying. +func (b *MockBroker) SetLatency(latency time.Duration) { + b.latency = latency +} + +// SetHandlerByMap defines mapping of Request types to MockResponses. When a +// request is received by the broker, it looks up the request type in the map +// and uses the found MockResponse instance to generate an appropriate reply. +// If the request type is not found in the map then nothing is sent. +func (b *MockBroker) SetHandlerByMap(handlerMap map[string]MockResponse) { + b.setHandler(func(req *request) (res encoder) { + reqTypeName := reflect.TypeOf(req.body).Elem().Name() + mockResponse := handlerMap[reqTypeName] + if mockResponse == nil { + return nil + } + return mockResponse.For(req.body) + }) +} + +// SetNotifier set a function that will get invoked whenever a request has been +// processed successfully and will provide the number of bytes read and written +func (b *MockBroker) SetNotifier(notifier RequestNotifierFunc) { + b.lock.Lock() + b.notifier = notifier + b.lock.Unlock() +} + +// BrokerID returns broker ID assigned to the broker. +func (b *MockBroker) BrokerID() int32 { + return b.brokerID +} + +// History returns a slice of RequestResponse pairs in the order they were +// processed by the broker. Note that in case of multiple connections to the +// broker the order expected by a test can be different from the order recorded +// in the history, unless some synchronization is implemented in the test. +func (b *MockBroker) History() []RequestResponse { + b.lock.Lock() + history := make([]RequestResponse, len(b.history)) + copy(history, b.history) + b.lock.Unlock() + return history +} + +// Port returns the TCP port number the broker is listening for requests on. +func (b *MockBroker) Port() int32 { + return b.port +} + +// Addr returns the broker connection string in the form "
:". +func (b *MockBroker) Addr() string { + return b.listener.Addr().String() +} + +// Close terminates the broker blocking until it stops internal goroutines and +// releases all resources. +func (b *MockBroker) Close() { + close(b.expectations) + if len(b.expectations) > 0 { + buf := bytes.NewBufferString(fmt.Sprintf("mockbroker/%d: not all expectations were satisfied! Still waiting on:\n", b.BrokerID())) + for e := range b.expectations { + _, _ = buf.WriteString(spew.Sdump(e)) + } + b.t.Error(buf.String()) + } + close(b.closing) + <-b.stopper +} + +// setHandler sets the specified function as the request handler. Whenever +// a mock broker reads a request from the wire it passes the request to the +// function and sends back whatever the handler function returns. +func (b *MockBroker) setHandler(handler requestHandlerFunc) { + b.lock.Lock() + b.handler = handler + b.lock.Unlock() +} + +func (b *MockBroker) serverLoop() { + defer close(b.stopper) + var err error + var conn net.Conn + + go func() { + <-b.closing + err := b.listener.Close() + if err != nil { + b.t.Error(err) + } + }() + + wg := &sync.WaitGroup{} + i := 0 + for conn, err = b.listener.Accept(); err == nil; conn, err = b.listener.Accept() { + wg.Add(1) + go b.handleRequests(conn, i, wg) + i++ + } + wg.Wait() + Logger.Printf("*** mockbroker/%d: listener closed, err=%v", b.BrokerID(), err) +} + +func (b *MockBroker) handleRequests(conn net.Conn, idx int, wg *sync.WaitGroup) { + defer wg.Done() + defer func() { + _ = conn.Close() + }() + Logger.Printf("*** mockbroker/%d/%d: connection opened", b.BrokerID(), idx) + var err error + + abort := make(chan none) + defer close(abort) + go func() { + select { + case <-b.closing: + _ = conn.Close() + case <-abort: + } + }() + + resHeader := make([]byte, 8) + for { + req, bytesRead, err := decodeRequest(conn) + if err != nil { + Logger.Printf("*** mockbroker/%d/%d: invalid request: err=%+v, %+v", b.brokerID, idx, err, spew.Sdump(req)) + b.serverError(err) + break + } + + if b.latency > 0 { + time.Sleep(b.latency) + } + + b.lock.Lock() + res := b.handler(req) + b.history = append(b.history, RequestResponse{req.body, res}) + b.lock.Unlock() + + if res == nil { + Logger.Printf("*** mockbroker/%d/%d: ignored %v", b.brokerID, idx, spew.Sdump(req)) + continue + } + Logger.Printf("*** mockbroker/%d/%d: served %v -> %v", b.brokerID, idx, req, res) + + encodedRes, err := encode(res, nil) + if err != nil { + b.serverError(err) + break + } + if len(encodedRes) == 0 { + b.lock.Lock() + if b.notifier != nil { + b.notifier(bytesRead, 0) + } + b.lock.Unlock() + continue + } + + binary.BigEndian.PutUint32(resHeader, uint32(len(encodedRes)+4)) + binary.BigEndian.PutUint32(resHeader[4:], uint32(req.correlationID)) + if _, err = conn.Write(resHeader); err != nil { + b.serverError(err) + break + } + if _, err = conn.Write(encodedRes); err != nil { + b.serverError(err) + break + } + + b.lock.Lock() + if b.notifier != nil { + b.notifier(bytesRead, len(resHeader)+len(encodedRes)) + } + b.lock.Unlock() + } + Logger.Printf("*** mockbroker/%d/%d: connection closed, err=%v", b.BrokerID(), idx, err) +} + +func (b *MockBroker) defaultRequestHandler(req *request) (res encoder) { + select { + case res, ok := <-b.expectations: + if !ok { + return nil + } + return res + case <-time.After(expectationTimeout): + return nil + } +} + +func (b *MockBroker) serverError(err error) { + isConnectionClosedError := false + if _, ok := err.(*net.OpError); ok { + isConnectionClosedError = true + } else if err == io.EOF { + isConnectionClosedError = true + } else if err.Error() == "use of closed network connection" { + isConnectionClosedError = true + } + + if isConnectionClosedError { + return + } + + b.t.Errorf(err.Error()) +} + +// NewMockBroker launches a fake Kafka broker. It takes a TestReporter as provided by the +// test framework and a channel of responses to use. If an error occurs it is +// simply logged to the TestReporter and the broker exits. +func NewMockBroker(t TestReporter, brokerID int32) *MockBroker { + return NewMockBrokerAddr(t, brokerID, "localhost:0") +} + +// NewMockBrokerAddr behaves like newMockBroker but listens on the address you give +// it rather than just some ephemeral port. +func NewMockBrokerAddr(t TestReporter, brokerID int32, addr string) *MockBroker { + listener, err := net.Listen("tcp", addr) + if err != nil { + t.Fatal(err) + } + return NewMockBrokerListener(t, brokerID, listener) +} + +// NewMockBrokerListener behaves like newMockBrokerAddr but accepts connections on the listener specified. +func NewMockBrokerListener(t TestReporter, brokerID int32, listener net.Listener) *MockBroker { + var err error + + broker := &MockBroker{ + closing: make(chan none), + stopper: make(chan none), + t: t, + brokerID: brokerID, + expectations: make(chan encoder, 512), + listener: listener, + } + broker.handler = broker.defaultRequestHandler + + Logger.Printf("*** mockbroker/%d listening on %s\n", brokerID, broker.listener.Addr().String()) + _, portStr, err := net.SplitHostPort(broker.listener.Addr().String()) + if err != nil { + t.Fatal(err) + } + tmp, err := strconv.ParseInt(portStr, 10, 32) + if err != nil { + t.Fatal(err) + } + broker.port = int32(tmp) + + go broker.serverLoop() + + return broker +} + +func (b *MockBroker) Returns(e encoder) { + b.expectations <- e +} diff --git a/vendor/github.com/Shopify/sarama/mockresponses.go b/vendor/github.com/Shopify/sarama/mockresponses.go new file mode 100644 index 00000000000..1720441996f --- /dev/null +++ b/vendor/github.com/Shopify/sarama/mockresponses.go @@ -0,0 +1,727 @@ +package sarama + +import ( + "fmt" +) + +// TestReporter has methods matching go's testing.T to avoid importing +// `testing` in the main part of the library. +type TestReporter interface { + Error(...interface{}) + Errorf(string, ...interface{}) + Fatal(...interface{}) + Fatalf(string, ...interface{}) +} + +// MockResponse is a response builder interface it defines one method that +// allows generating a response based on a request body. MockResponses are used +// to program behavior of MockBroker in tests. +type MockResponse interface { + For(reqBody versionedDecoder) (res encoder) +} + +// MockWrapper is a mock response builder that returns a particular concrete +// response regardless of the actual request passed to the `For` method. +type MockWrapper struct { + res encoder +} + +func (mw *MockWrapper) For(reqBody versionedDecoder) (res encoder) { + return mw.res +} + +func NewMockWrapper(res encoder) *MockWrapper { + return &MockWrapper{res: res} +} + +// MockSequence is a mock response builder that is created from a sequence of +// concrete responses. Every time when a `MockBroker` calls its `For` method +// the next response from the sequence is returned. When the end of the +// sequence is reached the last element from the sequence is returned. +type MockSequence struct { + responses []MockResponse +} + +func NewMockSequence(responses ...interface{}) *MockSequence { + ms := &MockSequence{} + ms.responses = make([]MockResponse, len(responses)) + for i, res := range responses { + switch res := res.(type) { + case MockResponse: + ms.responses[i] = res + case encoder: + ms.responses[i] = NewMockWrapper(res) + default: + panic(fmt.Sprintf("Unexpected response type: %T", res)) + } + } + return ms +} + +func (mc *MockSequence) For(reqBody versionedDecoder) (res encoder) { + res = mc.responses[0].For(reqBody) + if len(mc.responses) > 1 { + mc.responses = mc.responses[1:] + } + return res +} + +// MockMetadataResponse is a `MetadataResponse` builder. +type MockMetadataResponse struct { + controllerID int32 + leaders map[string]map[int32]int32 + brokers map[string]int32 + t TestReporter +} + +func NewMockMetadataResponse(t TestReporter) *MockMetadataResponse { + return &MockMetadataResponse{ + leaders: make(map[string]map[int32]int32), + brokers: make(map[string]int32), + t: t, + } +} + +func (mmr *MockMetadataResponse) SetLeader(topic string, partition, brokerID int32) *MockMetadataResponse { + partitions := mmr.leaders[topic] + if partitions == nil { + partitions = make(map[int32]int32) + mmr.leaders[topic] = partitions + } + partitions[partition] = brokerID + return mmr +} + +func (mmr *MockMetadataResponse) SetBroker(addr string, brokerID int32) *MockMetadataResponse { + mmr.brokers[addr] = brokerID + return mmr +} + +func (mmr *MockMetadataResponse) SetController(brokerID int32) *MockMetadataResponse { + mmr.controllerID = brokerID + return mmr +} + +func (mmr *MockMetadataResponse) For(reqBody versionedDecoder) encoder { + metadataRequest := reqBody.(*MetadataRequest) + metadataResponse := &MetadataResponse{ + Version: metadataRequest.version(), + ControllerID: mmr.controllerID, + } + for addr, brokerID := range mmr.brokers { + metadataResponse.AddBroker(addr, brokerID) + } + if len(metadataRequest.Topics) == 0 { + for topic, partitions := range mmr.leaders { + for partition, brokerID := range partitions { + metadataResponse.AddTopicPartition(topic, partition, brokerID, nil, nil, ErrNoError) + } + } + return metadataResponse + } + for _, topic := range metadataRequest.Topics { + for partition, brokerID := range mmr.leaders[topic] { + metadataResponse.AddTopicPartition(topic, partition, brokerID, nil, nil, ErrNoError) + } + } + return metadataResponse +} + +// MockOffsetResponse is an `OffsetResponse` builder. +type MockOffsetResponse struct { + offsets map[string]map[int32]map[int64]int64 + t TestReporter + version int16 +} + +func NewMockOffsetResponse(t TestReporter) *MockOffsetResponse { + return &MockOffsetResponse{ + offsets: make(map[string]map[int32]map[int64]int64), + t: t, + } +} + +func (mor *MockOffsetResponse) SetVersion(version int16) *MockOffsetResponse { + mor.version = version + return mor +} + +func (mor *MockOffsetResponse) SetOffset(topic string, partition int32, time, offset int64) *MockOffsetResponse { + partitions := mor.offsets[topic] + if partitions == nil { + partitions = make(map[int32]map[int64]int64) + mor.offsets[topic] = partitions + } + times := partitions[partition] + if times == nil { + times = make(map[int64]int64) + partitions[partition] = times + } + times[time] = offset + return mor +} + +func (mor *MockOffsetResponse) For(reqBody versionedDecoder) encoder { + offsetRequest := reqBody.(*OffsetRequest) + offsetResponse := &OffsetResponse{Version: mor.version} + for topic, partitions := range offsetRequest.blocks { + for partition, block := range partitions { + offset := mor.getOffset(topic, partition, block.time) + offsetResponse.AddTopicPartition(topic, partition, offset) + } + } + return offsetResponse +} + +func (mor *MockOffsetResponse) getOffset(topic string, partition int32, time int64) int64 { + partitions := mor.offsets[topic] + if partitions == nil { + mor.t.Errorf("missing topic: %s", topic) + } + times := partitions[partition] + if times == nil { + mor.t.Errorf("missing partition: %d", partition) + } + offset, ok := times[time] + if !ok { + mor.t.Errorf("missing time: %d", time) + } + return offset +} + +// MockFetchResponse is a `FetchResponse` builder. +type MockFetchResponse struct { + messages map[string]map[int32]map[int64]Encoder + highWaterMarks map[string]map[int32]int64 + t TestReporter + batchSize int + version int16 +} + +func NewMockFetchResponse(t TestReporter, batchSize int) *MockFetchResponse { + return &MockFetchResponse{ + messages: make(map[string]map[int32]map[int64]Encoder), + highWaterMarks: make(map[string]map[int32]int64), + t: t, + batchSize: batchSize, + } +} + +func (mfr *MockFetchResponse) SetVersion(version int16) *MockFetchResponse { + mfr.version = version + return mfr +} + +func (mfr *MockFetchResponse) SetMessage(topic string, partition int32, offset int64, msg Encoder) *MockFetchResponse { + partitions := mfr.messages[topic] + if partitions == nil { + partitions = make(map[int32]map[int64]Encoder) + mfr.messages[topic] = partitions + } + messages := partitions[partition] + if messages == nil { + messages = make(map[int64]Encoder) + partitions[partition] = messages + } + messages[offset] = msg + return mfr +} + +func (mfr *MockFetchResponse) SetHighWaterMark(topic string, partition int32, offset int64) *MockFetchResponse { + partitions := mfr.highWaterMarks[topic] + if partitions == nil { + partitions = make(map[int32]int64) + mfr.highWaterMarks[topic] = partitions + } + partitions[partition] = offset + return mfr +} + +func (mfr *MockFetchResponse) For(reqBody versionedDecoder) encoder { + fetchRequest := reqBody.(*FetchRequest) + res := &FetchResponse{ + Version: mfr.version, + } + for topic, partitions := range fetchRequest.blocks { + for partition, block := range partitions { + initialOffset := block.fetchOffset + offset := initialOffset + maxOffset := initialOffset + int64(mfr.getMessageCount(topic, partition)) + for i := 0; i < mfr.batchSize && offset < maxOffset; { + msg := mfr.getMessage(topic, partition, offset) + if msg != nil { + res.AddMessage(topic, partition, nil, msg, offset) + i++ + } + offset++ + } + fb := res.GetBlock(topic, partition) + if fb == nil { + res.AddError(topic, partition, ErrNoError) + fb = res.GetBlock(topic, partition) + } + fb.HighWaterMarkOffset = mfr.getHighWaterMark(topic, partition) + } + } + return res +} + +func (mfr *MockFetchResponse) getMessage(topic string, partition int32, offset int64) Encoder { + partitions := mfr.messages[topic] + if partitions == nil { + return nil + } + messages := partitions[partition] + if messages == nil { + return nil + } + return messages[offset] +} + +func (mfr *MockFetchResponse) getMessageCount(topic string, partition int32) int { + partitions := mfr.messages[topic] + if partitions == nil { + return 0 + } + messages := partitions[partition] + if messages == nil { + return 0 + } + return len(messages) +} + +func (mfr *MockFetchResponse) getHighWaterMark(topic string, partition int32) int64 { + partitions := mfr.highWaterMarks[topic] + if partitions == nil { + return 0 + } + return partitions[partition] +} + +// MockConsumerMetadataResponse is a `ConsumerMetadataResponse` builder. +type MockConsumerMetadataResponse struct { + coordinators map[string]interface{} + t TestReporter +} + +func NewMockConsumerMetadataResponse(t TestReporter) *MockConsumerMetadataResponse { + return &MockConsumerMetadataResponse{ + coordinators: make(map[string]interface{}), + t: t, + } +} + +func (mr *MockConsumerMetadataResponse) SetCoordinator(group string, broker *MockBroker) *MockConsumerMetadataResponse { + mr.coordinators[group] = broker + return mr +} + +func (mr *MockConsumerMetadataResponse) SetError(group string, kerror KError) *MockConsumerMetadataResponse { + mr.coordinators[group] = kerror + return mr +} + +func (mr *MockConsumerMetadataResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*ConsumerMetadataRequest) + group := req.ConsumerGroup + res := &ConsumerMetadataResponse{} + v := mr.coordinators[group] + switch v := v.(type) { + case *MockBroker: + res.Coordinator = &Broker{id: v.BrokerID(), addr: v.Addr()} + case KError: + res.Err = v + } + return res +} + +// MockFindCoordinatorResponse is a `FindCoordinatorResponse` builder. +type MockFindCoordinatorResponse struct { + groupCoordinators map[string]interface{} + transCoordinators map[string]interface{} + t TestReporter +} + +func NewMockFindCoordinatorResponse(t TestReporter) *MockFindCoordinatorResponse { + return &MockFindCoordinatorResponse{ + groupCoordinators: make(map[string]interface{}), + transCoordinators: make(map[string]interface{}), + t: t, + } +} + +func (mr *MockFindCoordinatorResponse) SetCoordinator(coordinatorType CoordinatorType, group string, broker *MockBroker) *MockFindCoordinatorResponse { + switch coordinatorType { + case CoordinatorGroup: + mr.groupCoordinators[group] = broker + case CoordinatorTransaction: + mr.transCoordinators[group] = broker + } + return mr +} + +func (mr *MockFindCoordinatorResponse) SetError(coordinatorType CoordinatorType, group string, kerror KError) *MockFindCoordinatorResponse { + switch coordinatorType { + case CoordinatorGroup: + mr.groupCoordinators[group] = kerror + case CoordinatorTransaction: + mr.transCoordinators[group] = kerror + } + return mr +} + +func (mr *MockFindCoordinatorResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*FindCoordinatorRequest) + res := &FindCoordinatorResponse{} + var v interface{} + switch req.CoordinatorType { + case CoordinatorGroup: + v = mr.groupCoordinators[req.CoordinatorKey] + case CoordinatorTransaction: + v = mr.transCoordinators[req.CoordinatorKey] + } + switch v := v.(type) { + case *MockBroker: + res.Coordinator = &Broker{id: v.BrokerID(), addr: v.Addr()} + case KError: + res.Err = v + } + return res +} + +// MockOffsetCommitResponse is a `OffsetCommitResponse` builder. +type MockOffsetCommitResponse struct { + errors map[string]map[string]map[int32]KError + t TestReporter +} + +func NewMockOffsetCommitResponse(t TestReporter) *MockOffsetCommitResponse { + return &MockOffsetCommitResponse{t: t} +} + +func (mr *MockOffsetCommitResponse) SetError(group, topic string, partition int32, kerror KError) *MockOffsetCommitResponse { + if mr.errors == nil { + mr.errors = make(map[string]map[string]map[int32]KError) + } + topics := mr.errors[group] + if topics == nil { + topics = make(map[string]map[int32]KError) + mr.errors[group] = topics + } + partitions := topics[topic] + if partitions == nil { + partitions = make(map[int32]KError) + topics[topic] = partitions + } + partitions[partition] = kerror + return mr +} + +func (mr *MockOffsetCommitResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*OffsetCommitRequest) + group := req.ConsumerGroup + res := &OffsetCommitResponse{} + for topic, partitions := range req.blocks { + for partition := range partitions { + res.AddError(topic, partition, mr.getError(group, topic, partition)) + } + } + return res +} + +func (mr *MockOffsetCommitResponse) getError(group, topic string, partition int32) KError { + topics := mr.errors[group] + if topics == nil { + return ErrNoError + } + partitions := topics[topic] + if partitions == nil { + return ErrNoError + } + kerror, ok := partitions[partition] + if !ok { + return ErrNoError + } + return kerror +} + +// MockProduceResponse is a `ProduceResponse` builder. +type MockProduceResponse struct { + version int16 + errors map[string]map[int32]KError + t TestReporter +} + +func NewMockProduceResponse(t TestReporter) *MockProduceResponse { + return &MockProduceResponse{t: t} +} + +func (mr *MockProduceResponse) SetVersion(version int16) *MockProduceResponse { + mr.version = version + return mr +} + +func (mr *MockProduceResponse) SetError(topic string, partition int32, kerror KError) *MockProduceResponse { + if mr.errors == nil { + mr.errors = make(map[string]map[int32]KError) + } + partitions := mr.errors[topic] + if partitions == nil { + partitions = make(map[int32]KError) + mr.errors[topic] = partitions + } + partitions[partition] = kerror + return mr +} + +func (mr *MockProduceResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*ProduceRequest) + res := &ProduceResponse{ + Version: mr.version, + } + for topic, partitions := range req.records { + for partition := range partitions { + res.AddTopicPartition(topic, partition, mr.getError(topic, partition)) + } + } + return res +} + +func (mr *MockProduceResponse) getError(topic string, partition int32) KError { + partitions := mr.errors[topic] + if partitions == nil { + return ErrNoError + } + kerror, ok := partitions[partition] + if !ok { + return ErrNoError + } + return kerror +} + +// MockOffsetFetchResponse is a `OffsetFetchResponse` builder. +type MockOffsetFetchResponse struct { + offsets map[string]map[string]map[int32]*OffsetFetchResponseBlock + t TestReporter +} + +func NewMockOffsetFetchResponse(t TestReporter) *MockOffsetFetchResponse { + return &MockOffsetFetchResponse{t: t} +} + +func (mr *MockOffsetFetchResponse) SetOffset(group, topic string, partition int32, offset int64, metadata string, kerror KError) *MockOffsetFetchResponse { + if mr.offsets == nil { + mr.offsets = make(map[string]map[string]map[int32]*OffsetFetchResponseBlock) + } + topics := mr.offsets[group] + if topics == nil { + topics = make(map[string]map[int32]*OffsetFetchResponseBlock) + mr.offsets[group] = topics + } + partitions := topics[topic] + if partitions == nil { + partitions = make(map[int32]*OffsetFetchResponseBlock) + topics[topic] = partitions + } + partitions[partition] = &OffsetFetchResponseBlock{offset, metadata, kerror} + return mr +} + +func (mr *MockOffsetFetchResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*OffsetFetchRequest) + group := req.ConsumerGroup + res := &OffsetFetchResponse{} + for topic, partitions := range mr.offsets[group] { + for partition, block := range partitions { + res.AddBlock(topic, partition, block) + } + } + return res +} + +type MockCreateTopicsResponse struct { + t TestReporter +} + +func NewMockCreateTopicsResponse(t TestReporter) *MockCreateTopicsResponse { + return &MockCreateTopicsResponse{t: t} +} + +func (mr *MockCreateTopicsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*CreateTopicsRequest) + res := &CreateTopicsResponse{} + res.TopicErrors = make(map[string]*TopicError) + + for topic, _ := range req.TopicDetails { + res.TopicErrors[topic] = &TopicError{Err: ErrNoError} + } + return res +} + +type MockDeleteTopicsResponse struct { + t TestReporter +} + +func NewMockDeleteTopicsResponse(t TestReporter) *MockDeleteTopicsResponse { + return &MockDeleteTopicsResponse{t: t} +} + +func (mr *MockDeleteTopicsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*DeleteTopicsRequest) + res := &DeleteTopicsResponse{} + res.TopicErrorCodes = make(map[string]KError) + + for _, topic := range req.Topics { + res.TopicErrorCodes[topic] = ErrNoError + } + return res +} + +type MockCreatePartitionsResponse struct { + t TestReporter +} + +func NewMockCreatePartitionsResponse(t TestReporter) *MockCreatePartitionsResponse { + return &MockCreatePartitionsResponse{t: t} +} + +func (mr *MockCreatePartitionsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*CreatePartitionsRequest) + res := &CreatePartitionsResponse{} + res.TopicPartitionErrors = make(map[string]*TopicPartitionError) + + for topic, _ := range req.TopicPartitions { + res.TopicPartitionErrors[topic] = &TopicPartitionError{Err: ErrNoError} + } + return res +} + +type MockDeleteRecordsResponse struct { + t TestReporter +} + +func NewMockDeleteRecordsResponse(t TestReporter) *MockDeleteRecordsResponse { + return &MockDeleteRecordsResponse{t: t} +} + +func (mr *MockDeleteRecordsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*DeleteRecordsRequest) + res := &DeleteRecordsResponse{} + res.Topics = make(map[string]*DeleteRecordsResponseTopic) + + for topic, deleteRecordRequestTopic := range req.Topics { + partitions := make(map[int32]*DeleteRecordsResponsePartition) + for partition, _ := range deleteRecordRequestTopic.PartitionOffsets { + partitions[partition] = &DeleteRecordsResponsePartition{Err: ErrNoError} + } + res.Topics[topic] = &DeleteRecordsResponseTopic{Partitions: partitions} + } + return res +} + +type MockDescribeConfigsResponse struct { + t TestReporter +} + +func NewMockDescribeConfigsResponse(t TestReporter) *MockDescribeConfigsResponse { + return &MockDescribeConfigsResponse{t: t} +} + +func (mr *MockDescribeConfigsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*DescribeConfigsRequest) + res := &DescribeConfigsResponse{} + + var configEntries []*ConfigEntry + configEntries = append(configEntries, &ConfigEntry{Name: "my_topic", + Value: "my_topic", + ReadOnly: true, + Default: true, + Sensitive: false, + }) + + for _, r := range req.Resources { + res.Resources = append(res.Resources, &ResourceResponse{Name: r.Name, Configs: configEntries}) + } + return res +} + +type MockAlterConfigsResponse struct { + t TestReporter +} + +func NewMockAlterConfigsResponse(t TestReporter) *MockAlterConfigsResponse { + return &MockAlterConfigsResponse{t: t} +} + +func (mr *MockAlterConfigsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*AlterConfigsRequest) + res := &AlterConfigsResponse{} + + for _, r := range req.Resources { + res.Resources = append(res.Resources, &AlterConfigsResourceResponse{Name: r.Name, + Type: TopicResource, + ErrorMsg: "", + }) + } + return res +} + +type MockCreateAclsResponse struct { + t TestReporter +} + +func NewMockCreateAclsResponse(t TestReporter) *MockCreateAclsResponse { + return &MockCreateAclsResponse{t: t} +} + +func (mr *MockCreateAclsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*CreateAclsRequest) + res := &CreateAclsResponse{} + + for range req.AclCreations { + res.AclCreationResponses = append(res.AclCreationResponses, &AclCreationResponse{Err: ErrNoError}) + } + return res +} + +type MockListAclsResponse struct { + t TestReporter +} + +func NewMockListAclsResponse(t TestReporter) *MockListAclsResponse { + return &MockListAclsResponse{t: t} +} + +func (mr *MockListAclsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*DescribeAclsRequest) + res := &DescribeAclsResponse{} + + res.Err = ErrNoError + acl := &ResourceAcls{} + acl.Resource.ResourceName = *req.ResourceName + acl.Resource.ResourceType = req.ResourceType + acl.Acls = append(acl.Acls, &Acl{}) + res.ResourceAcls = append(res.ResourceAcls, acl) + + return res +} + +type MockDeleteAclsResponse struct { + t TestReporter +} + +func NewMockDeleteAclsResponse(t TestReporter) *MockDeleteAclsResponse { + return &MockDeleteAclsResponse{t: t} +} + +func (mr *MockDeleteAclsResponse) For(reqBody versionedDecoder) encoder { + req := reqBody.(*DeleteAclsRequest) + res := &DeleteAclsResponse{} + + for range req.Filters { + response := &FilterResponse{Err: ErrNoError} + response.MatchingAcls = append(response.MatchingAcls, &MatchingAcl{Err: ErrNoError}) + res.FilterResponses = append(res.FilterResponses, response) + } + return res +} diff --git a/vendor/github.com/Shopify/sarama/offset_commit_request.go b/vendor/github.com/Shopify/sarama/offset_commit_request.go new file mode 100644 index 00000000000..37e99fbf5b8 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_commit_request.go @@ -0,0 +1,204 @@ +package sarama + +import "errors" + +// ReceiveTime is a special value for the timestamp field of Offset Commit Requests which +// tells the broker to set the timestamp to the time at which the request was received. +// The timestamp is only used if message version 1 is used, which requires kafka 0.8.2. +const ReceiveTime int64 = -1 + +// GroupGenerationUndefined is a special value for the group generation field of +// Offset Commit Requests that should be used when a consumer group does not rely +// on Kafka for partition management. +const GroupGenerationUndefined = -1 + +type offsetCommitRequestBlock struct { + offset int64 + timestamp int64 + metadata string +} + +func (b *offsetCommitRequestBlock) encode(pe packetEncoder, version int16) error { + pe.putInt64(b.offset) + if version == 1 { + pe.putInt64(b.timestamp) + } else if b.timestamp != 0 { + Logger.Println("Non-zero timestamp specified for OffsetCommitRequest not v1, it will be ignored") + } + + return pe.putString(b.metadata) +} + +func (b *offsetCommitRequestBlock) decode(pd packetDecoder, version int16) (err error) { + if b.offset, err = pd.getInt64(); err != nil { + return err + } + if version == 1 { + if b.timestamp, err = pd.getInt64(); err != nil { + return err + } + } + b.metadata, err = pd.getString() + return err +} + +type OffsetCommitRequest struct { + ConsumerGroup string + ConsumerGroupGeneration int32 // v1 or later + ConsumerID string // v1 or later + RetentionTime int64 // v2 or later + + // Version can be: + // - 0 (kafka 0.8.1 and later) + // - 1 (kafka 0.8.2 and later) + // - 2 (kafka 0.9.0 and later) + Version int16 + blocks map[string]map[int32]*offsetCommitRequestBlock +} + +func (r *OffsetCommitRequest) encode(pe packetEncoder) error { + if r.Version < 0 || r.Version > 2 { + return PacketEncodingError{"invalid or unsupported OffsetCommitRequest version field"} + } + + if err := pe.putString(r.ConsumerGroup); err != nil { + return err + } + + if r.Version >= 1 { + pe.putInt32(r.ConsumerGroupGeneration) + if err := pe.putString(r.ConsumerID); err != nil { + return err + } + } else { + if r.ConsumerGroupGeneration != 0 { + Logger.Println("Non-zero ConsumerGroupGeneration specified for OffsetCommitRequest v0, it will be ignored") + } + if r.ConsumerID != "" { + Logger.Println("Non-empty ConsumerID specified for OffsetCommitRequest v0, it will be ignored") + } + } + + if r.Version >= 2 { + pe.putInt64(r.RetentionTime) + } else if r.RetentionTime != 0 { + Logger.Println("Non-zero RetentionTime specified for OffsetCommitRequest version <2, it will be ignored") + } + + if err := pe.putArrayLength(len(r.blocks)); err != nil { + return err + } + for topic, partitions := range r.blocks { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putArrayLength(len(partitions)); err != nil { + return err + } + for partition, block := range partitions { + pe.putInt32(partition) + if err := block.encode(pe, r.Version); err != nil { + return err + } + } + } + return nil +} + +func (r *OffsetCommitRequest) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + if r.ConsumerGroup, err = pd.getString(); err != nil { + return err + } + + if r.Version >= 1 { + if r.ConsumerGroupGeneration, err = pd.getInt32(); err != nil { + return err + } + if r.ConsumerID, err = pd.getString(); err != nil { + return err + } + } + + if r.Version >= 2 { + if r.RetentionTime, err = pd.getInt64(); err != nil { + return err + } + } + + topicCount, err := pd.getArrayLength() + if err != nil { + return err + } + if topicCount == 0 { + return nil + } + r.blocks = make(map[string]map[int32]*offsetCommitRequestBlock) + for i := 0; i < topicCount; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + partitionCount, err := pd.getArrayLength() + if err != nil { + return err + } + r.blocks[topic] = make(map[int32]*offsetCommitRequestBlock) + for j := 0; j < partitionCount; j++ { + partition, err := pd.getInt32() + if err != nil { + return err + } + block := &offsetCommitRequestBlock{} + if err := block.decode(pd, r.Version); err != nil { + return err + } + r.blocks[topic][partition] = block + } + } + return nil +} + +func (r *OffsetCommitRequest) key() int16 { + return 8 +} + +func (r *OffsetCommitRequest) version() int16 { + return r.Version +} + +func (r *OffsetCommitRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_8_2_0 + case 2: + return V0_9_0_0 + default: + return MinVersion + } +} + +func (r *OffsetCommitRequest) AddBlock(topic string, partitionID int32, offset int64, timestamp int64, metadata string) { + if r.blocks == nil { + r.blocks = make(map[string]map[int32]*offsetCommitRequestBlock) + } + + if r.blocks[topic] == nil { + r.blocks[topic] = make(map[int32]*offsetCommitRequestBlock) + } + + r.blocks[topic][partitionID] = &offsetCommitRequestBlock{offset, timestamp, metadata} +} + +func (r *OffsetCommitRequest) Offset(topic string, partitionID int32) (int64, string, error) { + partitions := r.blocks[topic] + if partitions == nil { + return 0, "", errors.New("No such offset") + } + block := partitions[partitionID] + if block == nil { + return 0, "", errors.New("No such offset") + } + return block.offset, block.metadata, nil +} diff --git a/vendor/github.com/Shopify/sarama/offset_commit_response.go b/vendor/github.com/Shopify/sarama/offset_commit_response.go new file mode 100644 index 00000000000..a4b18acdff2 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_commit_response.go @@ -0,0 +1,85 @@ +package sarama + +type OffsetCommitResponse struct { + Errors map[string]map[int32]KError +} + +func (r *OffsetCommitResponse) AddError(topic string, partition int32, kerror KError) { + if r.Errors == nil { + r.Errors = make(map[string]map[int32]KError) + } + partitions := r.Errors[topic] + if partitions == nil { + partitions = make(map[int32]KError) + r.Errors[topic] = partitions + } + partitions[partition] = kerror +} + +func (r *OffsetCommitResponse) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(r.Errors)); err != nil { + return err + } + for topic, partitions := range r.Errors { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putArrayLength(len(partitions)); err != nil { + return err + } + for partition, kerror := range partitions { + pe.putInt32(partition) + pe.putInt16(int16(kerror)) + } + } + return nil +} + +func (r *OffsetCommitResponse) decode(pd packetDecoder, version int16) (err error) { + numTopics, err := pd.getArrayLength() + if err != nil || numTopics == 0 { + return err + } + + r.Errors = make(map[string]map[int32]KError, numTopics) + for i := 0; i < numTopics; i++ { + name, err := pd.getString() + if err != nil { + return err + } + + numErrors, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Errors[name] = make(map[int32]KError, numErrors) + + for j := 0; j < numErrors; j++ { + id, err := pd.getInt32() + if err != nil { + return err + } + + tmp, err := pd.getInt16() + if err != nil { + return err + } + r.Errors[name][id] = KError(tmp) + } + } + + return nil +} + +func (r *OffsetCommitResponse) key() int16 { + return 8 +} + +func (r *OffsetCommitResponse) version() int16 { + return 0 +} + +func (r *OffsetCommitResponse) requiredVersion() KafkaVersion { + return MinVersion +} diff --git a/vendor/github.com/Shopify/sarama/offset_fetch_request.go b/vendor/github.com/Shopify/sarama/offset_fetch_request.go new file mode 100644 index 00000000000..5a05014b481 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_fetch_request.go @@ -0,0 +1,81 @@ +package sarama + +type OffsetFetchRequest struct { + ConsumerGroup string + Version int16 + partitions map[string][]int32 +} + +func (r *OffsetFetchRequest) encode(pe packetEncoder) (err error) { + if r.Version < 0 || r.Version > 1 { + return PacketEncodingError{"invalid or unsupported OffsetFetchRequest version field"} + } + + if err = pe.putString(r.ConsumerGroup); err != nil { + return err + } + if err = pe.putArrayLength(len(r.partitions)); err != nil { + return err + } + for topic, partitions := range r.partitions { + if err = pe.putString(topic); err != nil { + return err + } + if err = pe.putInt32Array(partitions); err != nil { + return err + } + } + return nil +} + +func (r *OffsetFetchRequest) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + if r.ConsumerGroup, err = pd.getString(); err != nil { + return err + } + partitionCount, err := pd.getArrayLength() + if err != nil { + return err + } + if partitionCount == 0 { + return nil + } + r.partitions = make(map[string][]int32) + for i := 0; i < partitionCount; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + partitions, err := pd.getInt32Array() + if err != nil { + return err + } + r.partitions[topic] = partitions + } + return nil +} + +func (r *OffsetFetchRequest) key() int16 { + return 9 +} + +func (r *OffsetFetchRequest) version() int16 { + return r.Version +} + +func (r *OffsetFetchRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_8_2_0 + default: + return MinVersion + } +} + +func (r *OffsetFetchRequest) AddPartition(topic string, partitionID int32) { + if r.partitions == nil { + r.partitions = make(map[string][]int32) + } + + r.partitions[topic] = append(r.partitions[topic], partitionID) +} diff --git a/vendor/github.com/Shopify/sarama/offset_fetch_response.go b/vendor/github.com/Shopify/sarama/offset_fetch_response.go new file mode 100644 index 00000000000..11e4b1f3fdf --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_fetch_response.go @@ -0,0 +1,143 @@ +package sarama + +type OffsetFetchResponseBlock struct { + Offset int64 + Metadata string + Err KError +} + +func (b *OffsetFetchResponseBlock) decode(pd packetDecoder) (err error) { + b.Offset, err = pd.getInt64() + if err != nil { + return err + } + + b.Metadata, err = pd.getString() + if err != nil { + return err + } + + tmp, err := pd.getInt16() + if err != nil { + return err + } + b.Err = KError(tmp) + + return nil +} + +func (b *OffsetFetchResponseBlock) encode(pe packetEncoder) (err error) { + pe.putInt64(b.Offset) + + err = pe.putString(b.Metadata) + if err != nil { + return err + } + + pe.putInt16(int16(b.Err)) + + return nil +} + +type OffsetFetchResponse struct { + Blocks map[string]map[int32]*OffsetFetchResponseBlock +} + +func (r *OffsetFetchResponse) encode(pe packetEncoder) error { + if err := pe.putArrayLength(len(r.Blocks)); err != nil { + return err + } + for topic, partitions := range r.Blocks { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putArrayLength(len(partitions)); err != nil { + return err + } + for partition, block := range partitions { + pe.putInt32(partition) + if err := block.encode(pe); err != nil { + return err + } + } + } + return nil +} + +func (r *OffsetFetchResponse) decode(pd packetDecoder, version int16) (err error) { + numTopics, err := pd.getArrayLength() + if err != nil || numTopics == 0 { + return err + } + + r.Blocks = make(map[string]map[int32]*OffsetFetchResponseBlock, numTopics) + for i := 0; i < numTopics; i++ { + name, err := pd.getString() + if err != nil { + return err + } + + numBlocks, err := pd.getArrayLength() + if err != nil { + return err + } + + if numBlocks == 0 { + r.Blocks[name] = nil + continue + } + r.Blocks[name] = make(map[int32]*OffsetFetchResponseBlock, numBlocks) + + for j := 0; j < numBlocks; j++ { + id, err := pd.getInt32() + if err != nil { + return err + } + + block := new(OffsetFetchResponseBlock) + err = block.decode(pd) + if err != nil { + return err + } + r.Blocks[name][id] = block + } + } + + return nil +} + +func (r *OffsetFetchResponse) key() int16 { + return 9 +} + +func (r *OffsetFetchResponse) version() int16 { + return 0 +} + +func (r *OffsetFetchResponse) requiredVersion() KafkaVersion { + return MinVersion +} + +func (r *OffsetFetchResponse) GetBlock(topic string, partition int32) *OffsetFetchResponseBlock { + if r.Blocks == nil { + return nil + } + + if r.Blocks[topic] == nil { + return nil + } + + return r.Blocks[topic][partition] +} + +func (r *OffsetFetchResponse) AddBlock(topic string, partition int32, block *OffsetFetchResponseBlock) { + if r.Blocks == nil { + r.Blocks = make(map[string]map[int32]*OffsetFetchResponseBlock) + } + partitions := r.Blocks[topic] + if partitions == nil { + partitions = make(map[int32]*OffsetFetchResponseBlock) + r.Blocks[topic] = partitions + } + partitions[partition] = block +} diff --git a/vendor/github.com/Shopify/sarama/offset_manager.go b/vendor/github.com/Shopify/sarama/offset_manager.go new file mode 100644 index 00000000000..8ea857f8351 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_manager.go @@ -0,0 +1,572 @@ +package sarama + +import ( + "sync" + "time" +) + +// Offset Manager + +// OffsetManager uses Kafka to store and fetch consumed partition offsets. +type OffsetManager interface { + // ManagePartition creates a PartitionOffsetManager on the given topic/partition. + // It will return an error if this OffsetManager is already managing the given + // topic/partition. + ManagePartition(topic string, partition int32) (PartitionOffsetManager, error) + + // Close stops the OffsetManager from managing offsets. It is required to call + // this function before an OffsetManager object passes out of scope, as it + // will otherwise leak memory. You must call this after all the + // PartitionOffsetManagers are closed. + Close() error +} + +type offsetManager struct { + client Client + conf *Config + group string + ticker *time.Ticker + + memberID string + generation int32 + + broker *Broker + brokerLock sync.RWMutex + + poms map[string]map[int32]*partitionOffsetManager + pomsLock sync.RWMutex + + closeOnce sync.Once + closing chan none + closed chan none +} + +// NewOffsetManagerFromClient creates a new OffsetManager from the given client. +// It is still necessary to call Close() on the underlying client when finished with the partition manager. +func NewOffsetManagerFromClient(group string, client Client) (OffsetManager, error) { + return newOffsetManagerFromClient(group, "", GroupGenerationUndefined, client) +} + +func newOffsetManagerFromClient(group, memberID string, generation int32, client Client) (*offsetManager, error) { + // Check that we are not dealing with a closed Client before processing any other arguments + if client.Closed() { + return nil, ErrClosedClient + } + + conf := client.Config() + om := &offsetManager{ + client: client, + conf: conf, + group: group, + ticker: time.NewTicker(conf.Consumer.Offsets.CommitInterval), + poms: make(map[string]map[int32]*partitionOffsetManager), + + memberID: memberID, + generation: generation, + + closing: make(chan none), + closed: make(chan none), + } + go withRecover(om.mainLoop) + + return om, nil +} + +func (om *offsetManager) ManagePartition(topic string, partition int32) (PartitionOffsetManager, error) { + pom, err := om.newPartitionOffsetManager(topic, partition) + if err != nil { + return nil, err + } + + om.pomsLock.Lock() + defer om.pomsLock.Unlock() + + topicManagers := om.poms[topic] + if topicManagers == nil { + topicManagers = make(map[int32]*partitionOffsetManager) + om.poms[topic] = topicManagers + } + + if topicManagers[partition] != nil { + return nil, ConfigurationError("That topic/partition is already being managed") + } + + topicManagers[partition] = pom + return pom, nil +} + +func (om *offsetManager) Close() error { + om.closeOnce.Do(func() { + // exit the mainLoop + close(om.closing) + <-om.closed + + // mark all POMs as closed + om.asyncClosePOMs() + + // flush one last time + for attempt := 0; attempt <= om.conf.Consumer.Offsets.Retry.Max; attempt++ { + om.flushToBroker() + if om.releasePOMs(false) == 0 { + break + } + } + + om.releasePOMs(true) + om.brokerLock.Lock() + om.broker = nil + om.brokerLock.Unlock() + }) + return nil +} + +func (om *offsetManager) fetchInitialOffset(topic string, partition int32, retries int) (int64, string, error) { + broker, err := om.coordinator() + if err != nil { + if retries <= 0 { + return 0, "", err + } + return om.fetchInitialOffset(topic, partition, retries-1) + } + + req := new(OffsetFetchRequest) + req.Version = 1 + req.ConsumerGroup = om.group + req.AddPartition(topic, partition) + + resp, err := broker.FetchOffset(req) + if err != nil { + if retries <= 0 { + return 0, "", err + } + om.releaseCoordinator(broker) + return om.fetchInitialOffset(topic, partition, retries-1) + } + + block := resp.GetBlock(topic, partition) + if block == nil { + return 0, "", ErrIncompleteResponse + } + + switch block.Err { + case ErrNoError: + return block.Offset, block.Metadata, nil + case ErrNotCoordinatorForConsumer: + if retries <= 0 { + return 0, "", block.Err + } + om.releaseCoordinator(broker) + return om.fetchInitialOffset(topic, partition, retries-1) + case ErrOffsetsLoadInProgress: + if retries <= 0 { + return 0, "", block.Err + } + select { + case <-om.closing: + return 0, "", block.Err + case <-time.After(om.conf.Metadata.Retry.Backoff): + } + return om.fetchInitialOffset(topic, partition, retries-1) + default: + return 0, "", block.Err + } +} + +func (om *offsetManager) coordinator() (*Broker, error) { + om.brokerLock.RLock() + broker := om.broker + om.brokerLock.RUnlock() + + if broker != nil { + return broker, nil + } + + om.brokerLock.Lock() + defer om.brokerLock.Unlock() + + if broker := om.broker; broker != nil { + return broker, nil + } + + if err := om.client.RefreshCoordinator(om.group); err != nil { + return nil, err + } + + broker, err := om.client.Coordinator(om.group) + if err != nil { + return nil, err + } + + om.broker = broker + return broker, nil +} + +func (om *offsetManager) releaseCoordinator(b *Broker) { + om.brokerLock.Lock() + if om.broker == b { + om.broker = nil + } + om.brokerLock.Unlock() +} + +func (om *offsetManager) mainLoop() { + defer om.ticker.Stop() + defer close(om.closed) + + for { + select { + case <-om.ticker.C: + om.flushToBroker() + om.releasePOMs(false) + case <-om.closing: + return + } + } +} + +func (om *offsetManager) flushToBroker() { + req := om.constructRequest() + if req == nil { + return + } + + broker, err := om.coordinator() + if err != nil { + om.handleError(err) + return + } + + resp, err := broker.CommitOffset(req) + if err != nil { + om.handleError(err) + om.releaseCoordinator(broker) + _ = broker.Close() + return + } + + om.handleResponse(broker, req, resp) +} + +func (om *offsetManager) constructRequest() *OffsetCommitRequest { + var r *OffsetCommitRequest + var perPartitionTimestamp int64 + if om.conf.Consumer.Offsets.Retention == 0 { + perPartitionTimestamp = ReceiveTime + r = &OffsetCommitRequest{ + Version: 1, + ConsumerGroup: om.group, + ConsumerID: om.memberID, + ConsumerGroupGeneration: om.generation, + } + } else { + r = &OffsetCommitRequest{ + Version: 2, + RetentionTime: int64(om.conf.Consumer.Offsets.Retention / time.Millisecond), + ConsumerGroup: om.group, + ConsumerID: om.memberID, + ConsumerGroupGeneration: om.generation, + } + + } + + om.pomsLock.RLock() + defer om.pomsLock.RUnlock() + + for _, topicManagers := range om.poms { + for _, pom := range topicManagers { + pom.lock.Lock() + if pom.dirty { + r.AddBlock(pom.topic, pom.partition, pom.offset, perPartitionTimestamp, pom.metadata) + } + pom.lock.Unlock() + } + } + + if len(r.blocks) > 0 { + return r + } + + return nil +} + +func (om *offsetManager) handleResponse(broker *Broker, req *OffsetCommitRequest, resp *OffsetCommitResponse) { + om.pomsLock.RLock() + defer om.pomsLock.RUnlock() + + for _, topicManagers := range om.poms { + for _, pom := range topicManagers { + if req.blocks[pom.topic] == nil || req.blocks[pom.topic][pom.partition] == nil { + continue + } + + var err KError + var ok bool + + if resp.Errors[pom.topic] == nil { + pom.handleError(ErrIncompleteResponse) + continue + } + if err, ok = resp.Errors[pom.topic][pom.partition]; !ok { + pom.handleError(ErrIncompleteResponse) + continue + } + + switch err { + case ErrNoError: + block := req.blocks[pom.topic][pom.partition] + pom.updateCommitted(block.offset, block.metadata) + case ErrNotLeaderForPartition, ErrLeaderNotAvailable, + ErrConsumerCoordinatorNotAvailable, ErrNotCoordinatorForConsumer: + // not a critical error, we just need to redispatch + om.releaseCoordinator(broker) + case ErrOffsetMetadataTooLarge, ErrInvalidCommitOffsetSize: + // nothing we can do about this, just tell the user and carry on + pom.handleError(err) + case ErrOffsetsLoadInProgress: + // nothing wrong but we didn't commit, we'll get it next time round + break + case ErrUnknownTopicOrPartition: + // let the user know *and* try redispatching - if topic-auto-create is + // enabled, redispatching should trigger a metadata req and create the + // topic; if not then re-dispatching won't help, but we've let the user + // know and it shouldn't hurt either (see https://github.com/Shopify/sarama/issues/706) + fallthrough + default: + // dunno, tell the user and try redispatching + pom.handleError(err) + om.releaseCoordinator(broker) + } + } + } +} + +func (om *offsetManager) handleError(err error) { + om.pomsLock.RLock() + defer om.pomsLock.RUnlock() + + for _, topicManagers := range om.poms { + for _, pom := range topicManagers { + pom.handleError(err) + } + } +} + +func (om *offsetManager) asyncClosePOMs() { + om.pomsLock.RLock() + defer om.pomsLock.RUnlock() + + for _, topicManagers := range om.poms { + for _, pom := range topicManagers { + pom.AsyncClose() + } + } +} + +// Releases/removes closed POMs once they are clean (or when forced) +func (om *offsetManager) releasePOMs(force bool) (remaining int) { + om.pomsLock.Lock() + defer om.pomsLock.Unlock() + + for topic, topicManagers := range om.poms { + for partition, pom := range topicManagers { + pom.lock.Lock() + releaseDue := pom.done && (force || !pom.dirty) + pom.lock.Unlock() + + if releaseDue { + pom.release() + + delete(om.poms[topic], partition) + if len(om.poms[topic]) == 0 { + delete(om.poms, topic) + } + } + } + remaining += len(om.poms[topic]) + } + return +} + +func (om *offsetManager) findPOM(topic string, partition int32) *partitionOffsetManager { + om.pomsLock.RLock() + defer om.pomsLock.RUnlock() + + if partitions, ok := om.poms[topic]; ok { + if pom, ok := partitions[partition]; ok { + return pom + } + } + return nil +} + +// Partition Offset Manager + +// PartitionOffsetManager uses Kafka to store and fetch consumed partition offsets. You MUST call Close() +// on a partition offset manager to avoid leaks, it will not be garbage-collected automatically when it passes +// out of scope. +type PartitionOffsetManager interface { + // NextOffset returns the next offset that should be consumed for the managed + // partition, accompanied by metadata which can be used to reconstruct the state + // of the partition consumer when it resumes. NextOffset() will return + // `config.Consumer.Offsets.Initial` and an empty metadata string if no offset + // was committed for this partition yet. + NextOffset() (int64, string) + + // MarkOffset marks the provided offset, alongside a metadata string + // that represents the state of the partition consumer at that point in time. The + // metadata string can be used by another consumer to restore that state, so it + // can resume consumption. + // + // To follow upstream conventions, you are expected to mark the offset of the + // next message to read, not the last message read. Thus, when calling `MarkOffset` + // you should typically add one to the offset of the last consumed message. + // + // Note: calling MarkOffset does not necessarily commit the offset to the backend + // store immediately for efficiency reasons, and it may never be committed if + // your application crashes. This means that you may end up processing the same + // message twice, and your processing should ideally be idempotent. + MarkOffset(offset int64, metadata string) + + // ResetOffset resets to the provided offset, alongside a metadata string that + // represents the state of the partition consumer at that point in time. Reset + // acts as a counterpart to MarkOffset, the difference being that it allows to + // reset an offset to an earlier or smaller value, where MarkOffset only + // allows incrementing the offset. cf MarkOffset for more details. + ResetOffset(offset int64, metadata string) + + // Errors returns a read channel of errors that occur during offset management, if + // enabled. By default, errors are logged and not returned over this channel. If + // you want to implement any custom error handling, set your config's + // Consumer.Return.Errors setting to true, and read from this channel. + Errors() <-chan *ConsumerError + + // AsyncClose initiates a shutdown of the PartitionOffsetManager. This method will + // return immediately, after which you should wait until the 'errors' channel has + // been drained and closed. It is required to call this function, or Close before + // a consumer object passes out of scope, as it will otherwise leak memory. You + // must call this before calling Close on the underlying client. + AsyncClose() + + // Close stops the PartitionOffsetManager from managing offsets. It is required to + // call this function (or AsyncClose) before a PartitionOffsetManager object + // passes out of scope, as it will otherwise leak memory. You must call this + // before calling Close on the underlying client. + Close() error +} + +type partitionOffsetManager struct { + parent *offsetManager + topic string + partition int32 + + lock sync.Mutex + offset int64 + metadata string + dirty bool + done bool + + releaseOnce sync.Once + errors chan *ConsumerError +} + +func (om *offsetManager) newPartitionOffsetManager(topic string, partition int32) (*partitionOffsetManager, error) { + offset, metadata, err := om.fetchInitialOffset(topic, partition, om.conf.Metadata.Retry.Max) + if err != nil { + return nil, err + } + + return &partitionOffsetManager{ + parent: om, + topic: topic, + partition: partition, + errors: make(chan *ConsumerError, om.conf.ChannelBufferSize), + offset: offset, + metadata: metadata, + }, nil +} + +func (pom *partitionOffsetManager) Errors() <-chan *ConsumerError { + return pom.errors +} + +func (pom *partitionOffsetManager) MarkOffset(offset int64, metadata string) { + pom.lock.Lock() + defer pom.lock.Unlock() + + if offset > pom.offset { + pom.offset = offset + pom.metadata = metadata + pom.dirty = true + } +} + +func (pom *partitionOffsetManager) ResetOffset(offset int64, metadata string) { + pom.lock.Lock() + defer pom.lock.Unlock() + + if offset <= pom.offset { + pom.offset = offset + pom.metadata = metadata + pom.dirty = true + } +} + +func (pom *partitionOffsetManager) updateCommitted(offset int64, metadata string) { + pom.lock.Lock() + defer pom.lock.Unlock() + + if pom.offset == offset && pom.metadata == metadata { + pom.dirty = false + } +} + +func (pom *partitionOffsetManager) NextOffset() (int64, string) { + pom.lock.Lock() + defer pom.lock.Unlock() + + if pom.offset >= 0 { + return pom.offset, pom.metadata + } + + return pom.parent.conf.Consumer.Offsets.Initial, "" +} + +func (pom *partitionOffsetManager) AsyncClose() { + pom.lock.Lock() + pom.done = true + pom.lock.Unlock() +} + +func (pom *partitionOffsetManager) Close() error { + pom.AsyncClose() + + var errors ConsumerErrors + for err := range pom.errors { + errors = append(errors, err) + } + + if len(errors) > 0 { + return errors + } + return nil +} + +func (pom *partitionOffsetManager) handleError(err error) { + cErr := &ConsumerError{ + Topic: pom.topic, + Partition: pom.partition, + Err: err, + } + + if pom.parent.conf.Consumer.Return.Errors { + pom.errors <- cErr + } else { + Logger.Println(cErr) + } +} + +func (pom *partitionOffsetManager) release() { + pom.releaseOnce.Do(func() { + go close(pom.errors) + }) +} diff --git a/vendor/github.com/Shopify/sarama/offset_request.go b/vendor/github.com/Shopify/sarama/offset_request.go new file mode 100644 index 00000000000..4c5df75df05 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_request.go @@ -0,0 +1,132 @@ +package sarama + +type offsetRequestBlock struct { + time int64 + maxOffsets int32 // Only used in version 0 +} + +func (b *offsetRequestBlock) encode(pe packetEncoder, version int16) error { + pe.putInt64(int64(b.time)) + if version == 0 { + pe.putInt32(b.maxOffsets) + } + + return nil +} + +func (b *offsetRequestBlock) decode(pd packetDecoder, version int16) (err error) { + if b.time, err = pd.getInt64(); err != nil { + return err + } + if version == 0 { + if b.maxOffsets, err = pd.getInt32(); err != nil { + return err + } + } + return nil +} + +type OffsetRequest struct { + Version int16 + blocks map[string]map[int32]*offsetRequestBlock +} + +func (r *OffsetRequest) encode(pe packetEncoder) error { + pe.putInt32(-1) // replica ID is always -1 for clients + err := pe.putArrayLength(len(r.blocks)) + if err != nil { + return err + } + for topic, partitions := range r.blocks { + err = pe.putString(topic) + if err != nil { + return err + } + err = pe.putArrayLength(len(partitions)) + if err != nil { + return err + } + for partition, block := range partitions { + pe.putInt32(partition) + if err = block.encode(pe, r.Version); err != nil { + return err + } + } + } + return nil +} + +func (r *OffsetRequest) decode(pd packetDecoder, version int16) error { + r.Version = version + + // Ignore replica ID + if _, err := pd.getInt32(); err != nil { + return err + } + blockCount, err := pd.getArrayLength() + if err != nil { + return err + } + if blockCount == 0 { + return nil + } + r.blocks = make(map[string]map[int32]*offsetRequestBlock) + for i := 0; i < blockCount; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + partitionCount, err := pd.getArrayLength() + if err != nil { + return err + } + r.blocks[topic] = make(map[int32]*offsetRequestBlock) + for j := 0; j < partitionCount; j++ { + partition, err := pd.getInt32() + if err != nil { + return err + } + block := &offsetRequestBlock{} + if err := block.decode(pd, version); err != nil { + return err + } + r.blocks[topic][partition] = block + } + } + return nil +} + +func (r *OffsetRequest) key() int16 { + return 2 +} + +func (r *OffsetRequest) version() int16 { + return r.Version +} + +func (r *OffsetRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_10_1_0 + default: + return MinVersion + } +} + +func (r *OffsetRequest) AddBlock(topic string, partitionID int32, time int64, maxOffsets int32) { + if r.blocks == nil { + r.blocks = make(map[string]map[int32]*offsetRequestBlock) + } + + if r.blocks[topic] == nil { + r.blocks[topic] = make(map[int32]*offsetRequestBlock) + } + + tmp := new(offsetRequestBlock) + tmp.time = time + if r.Version == 0 { + tmp.maxOffsets = maxOffsets + } + + r.blocks[topic][partitionID] = tmp +} diff --git a/vendor/github.com/Shopify/sarama/offset_response.go b/vendor/github.com/Shopify/sarama/offset_response.go new file mode 100644 index 00000000000..8b2193f9a0b --- /dev/null +++ b/vendor/github.com/Shopify/sarama/offset_response.go @@ -0,0 +1,174 @@ +package sarama + +type OffsetResponseBlock struct { + Err KError + Offsets []int64 // Version 0 + Offset int64 // Version 1 + Timestamp int64 // Version 1 +} + +func (b *OffsetResponseBlock) decode(pd packetDecoder, version int16) (err error) { + tmp, err := pd.getInt16() + if err != nil { + return err + } + b.Err = KError(tmp) + + if version == 0 { + b.Offsets, err = pd.getInt64Array() + + return err + } + + b.Timestamp, err = pd.getInt64() + if err != nil { + return err + } + + b.Offset, err = pd.getInt64() + if err != nil { + return err + } + + // For backwards compatibility put the offset in the offsets array too + b.Offsets = []int64{b.Offset} + + return nil +} + +func (b *OffsetResponseBlock) encode(pe packetEncoder, version int16) (err error) { + pe.putInt16(int16(b.Err)) + + if version == 0 { + return pe.putInt64Array(b.Offsets) + } + + pe.putInt64(b.Timestamp) + pe.putInt64(b.Offset) + + return nil +} + +type OffsetResponse struct { + Version int16 + Blocks map[string]map[int32]*OffsetResponseBlock +} + +func (r *OffsetResponse) decode(pd packetDecoder, version int16) (err error) { + numTopics, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Blocks = make(map[string]map[int32]*OffsetResponseBlock, numTopics) + for i := 0; i < numTopics; i++ { + name, err := pd.getString() + if err != nil { + return err + } + + numBlocks, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Blocks[name] = make(map[int32]*OffsetResponseBlock, numBlocks) + + for j := 0; j < numBlocks; j++ { + id, err := pd.getInt32() + if err != nil { + return err + } + + block := new(OffsetResponseBlock) + err = block.decode(pd, version) + if err != nil { + return err + } + r.Blocks[name][id] = block + } + } + + return nil +} + +func (r *OffsetResponse) GetBlock(topic string, partition int32) *OffsetResponseBlock { + if r.Blocks == nil { + return nil + } + + if r.Blocks[topic] == nil { + return nil + } + + return r.Blocks[topic][partition] +} + +/* +// [0 0 0 1 ntopics +0 8 109 121 95 116 111 112 105 99 topic +0 0 0 1 npartitions +0 0 0 0 id +0 0 + +0 0 0 1 0 0 0 0 +0 1 1 1 0 0 0 1 +0 8 109 121 95 116 111 112 +105 99 0 0 0 1 0 0 +0 0 0 0 0 0 0 1 +0 0 0 0 0 1 1 1] + +*/ +func (r *OffsetResponse) encode(pe packetEncoder) (err error) { + if err = pe.putArrayLength(len(r.Blocks)); err != nil { + return err + } + + for topic, partitions := range r.Blocks { + if err = pe.putString(topic); err != nil { + return err + } + if err = pe.putArrayLength(len(partitions)); err != nil { + return err + } + for partition, block := range partitions { + pe.putInt32(partition) + if err = block.encode(pe, r.version()); err != nil { + return err + } + } + } + + return nil +} + +func (r *OffsetResponse) key() int16 { + return 2 +} + +func (r *OffsetResponse) version() int16 { + return r.Version +} + +func (r *OffsetResponse) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_10_1_0 + default: + return MinVersion + } +} + +// testing API + +func (r *OffsetResponse) AddTopicPartition(topic string, partition int32, offset int64) { + if r.Blocks == nil { + r.Blocks = make(map[string]map[int32]*OffsetResponseBlock) + } + byTopic, ok := r.Blocks[topic] + if !ok { + byTopic = make(map[int32]*OffsetResponseBlock) + r.Blocks[topic] = byTopic + } + byTopic[partition] = &OffsetResponseBlock{Offsets: []int64{offset}, Offset: offset} +} diff --git a/vendor/github.com/Shopify/sarama/packet_decoder.go b/vendor/github.com/Shopify/sarama/packet_decoder.go new file mode 100644 index 00000000000..74805ccbf53 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/packet_decoder.go @@ -0,0 +1,60 @@ +package sarama + +// PacketDecoder is the interface providing helpers for reading with Kafka's encoding rules. +// Types implementing Decoder only need to worry about calling methods like GetString, +// not about how a string is represented in Kafka. +type packetDecoder interface { + // Primitives + getInt8() (int8, error) + getInt16() (int16, error) + getInt32() (int32, error) + getInt64() (int64, error) + getVarint() (int64, error) + getArrayLength() (int, error) + getBool() (bool, error) + + // Collections + getBytes() ([]byte, error) + getVarintBytes() ([]byte, error) + getRawBytes(length int) ([]byte, error) + getString() (string, error) + getNullableString() (*string, error) + getInt32Array() ([]int32, error) + getInt64Array() ([]int64, error) + getStringArray() ([]string, error) + + // Subsets + remaining() int + getSubset(length int) (packetDecoder, error) + peek(offset, length int) (packetDecoder, error) // similar to getSubset, but it doesn't advance the offset + + // Stacks, see PushDecoder + push(in pushDecoder) error + pop() error +} + +// PushDecoder is the interface for decoding fields like CRCs and lengths where the validity +// of the field depends on what is after it in the packet. Start them with PacketDecoder.Push() where +// the actual value is located in the packet, then PacketDecoder.Pop() them when all the bytes they +// depend upon have been decoded. +type pushDecoder interface { + // Saves the offset into the input buffer as the location to actually read the calculated value when able. + saveOffset(in int) + + // Returns the length of data to reserve for the input of this encoder (eg 4 bytes for a CRC32). + reserveLength() int + + // Indicates that all required data is now available to calculate and check the field. + // SaveOffset is guaranteed to have been called first. The implementation should read ReserveLength() bytes + // of data from the saved offset, and verify it based on the data between the saved offset and curOffset. + check(curOffset int, buf []byte) error +} + +// dynamicPushDecoder extends the interface of pushDecoder for uses cases where the length of the +// fields itself is unknown until its value was decoded (for instance varint encoded length +// fields). +// During push, dynamicPushDecoder.decode() method will be called instead of reserveLength() +type dynamicPushDecoder interface { + pushDecoder + decoder +} diff --git a/vendor/github.com/Shopify/sarama/packet_encoder.go b/vendor/github.com/Shopify/sarama/packet_encoder.go new file mode 100644 index 00000000000..67b8daed829 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/packet_encoder.go @@ -0,0 +1,65 @@ +package sarama + +import "github.com/rcrowley/go-metrics" + +// PacketEncoder is the interface providing helpers for writing with Kafka's encoding rules. +// Types implementing Encoder only need to worry about calling methods like PutString, +// not about how a string is represented in Kafka. +type packetEncoder interface { + // Primitives + putInt8(in int8) + putInt16(in int16) + putInt32(in int32) + putInt64(in int64) + putVarint(in int64) + putArrayLength(in int) error + putBool(in bool) + + // Collections + putBytes(in []byte) error + putVarintBytes(in []byte) error + putRawBytes(in []byte) error + putString(in string) error + putNullableString(in *string) error + putStringArray(in []string) error + putInt32Array(in []int32) error + putInt64Array(in []int64) error + + // Provide the current offset to record the batch size metric + offset() int + + // Stacks, see PushEncoder + push(in pushEncoder) + pop() error + + // To record metrics when provided + metricRegistry() metrics.Registry +} + +// PushEncoder is the interface for encoding fields like CRCs and lengths where the value +// of the field depends on what is encoded after it in the packet. Start them with PacketEncoder.Push() where +// the actual value is located in the packet, then PacketEncoder.Pop() them when all the bytes they +// depend upon have been written. +type pushEncoder interface { + // Saves the offset into the input buffer as the location to actually write the calculated value when able. + saveOffset(in int) + + // Returns the length of data to reserve for the output of this encoder (eg 4 bytes for a CRC32). + reserveLength() int + + // Indicates that all required data is now available to calculate and write the field. + // SaveOffset is guaranteed to have been called first. The implementation should write ReserveLength() bytes + // of data to the saved offset, based on the data between the saved offset and curOffset. + run(curOffset int, buf []byte) error +} + +// dynamicPushEncoder extends the interface of pushEncoder for uses cases where the length of the +// fields itself is unknown until its value was computed (for instance varint encoded length +// fields). +type dynamicPushEncoder interface { + pushEncoder + + // Called during pop() to adjust the length of the field. + // It should return the difference in bytes between the last computed length and current length. + adjustLength(currOffset int) int +} diff --git a/vendor/github.com/Shopify/sarama/partitioner.go b/vendor/github.com/Shopify/sarama/partitioner.go new file mode 100644 index 00000000000..6a708e729ee --- /dev/null +++ b/vendor/github.com/Shopify/sarama/partitioner.go @@ -0,0 +1,217 @@ +package sarama + +import ( + "hash" + "hash/fnv" + "math/rand" + "time" +) + +// Partitioner is anything that, given a Kafka message and a number of partitions indexed [0...numPartitions-1], +// decides to which partition to send the message. RandomPartitioner, RoundRobinPartitioner and HashPartitioner are provided +// as simple default implementations. +type Partitioner interface { + // Partition takes a message and partition count and chooses a partition + Partition(message *ProducerMessage, numPartitions int32) (int32, error) + + // RequiresConsistency indicates to the user of the partitioner whether the + // mapping of key->partition is consistent or not. Specifically, if a + // partitioner requires consistency then it must be allowed to choose from all + // partitions (even ones known to be unavailable), and its choice must be + // respected by the caller. The obvious example is the HashPartitioner. + RequiresConsistency() bool +} + +// DynamicConsistencyPartitioner can optionally be implemented by Partitioners +// in order to allow more flexibility than is originally allowed by the +// RequiresConsistency method in the Partitioner interface. This allows +// partitioners to require consistency sometimes, but not all times. It's useful +// for, e.g., the HashPartitioner, which does not require consistency if the +// message key is nil. +type DynamicConsistencyPartitioner interface { + Partitioner + + // MessageRequiresConsistency is similar to Partitioner.RequiresConsistency, + // but takes in the message being partitioned so that the partitioner can + // make a per-message determination. + MessageRequiresConsistency(message *ProducerMessage) bool +} + +// PartitionerConstructor is the type for a function capable of constructing new Partitioners. +type PartitionerConstructor func(topic string) Partitioner + +type manualPartitioner struct{} + +// HashPartitionOption lets you modify default values of the partitioner +type HashPartitionerOption func(*hashPartitioner) + +// WithAbsFirst means that the partitioner handles absolute values +// in the same way as the reference Java implementation +func WithAbsFirst() HashPartitionerOption { + return func(hp *hashPartitioner) { + hp.referenceAbs = true + } +} + +// WithCustomHashFunction lets you specify what hash function to use for the partitioning +func WithCustomHashFunction(hasher func() hash.Hash32) HashPartitionerOption { + return func(hp *hashPartitioner) { + hp.hasher = hasher() + } +} + +// WithCustomFallbackPartitioner lets you specify what HashPartitioner should be used in case a Distribution Key is empty +func WithCustomFallbackPartitioner(randomHP *hashPartitioner) HashPartitionerOption { + return func(hp *hashPartitioner) { + hp.random = hp + } +} + +// NewManualPartitioner returns a Partitioner which uses the partition manually set in the provided +// ProducerMessage's Partition field as the partition to produce to. +func NewManualPartitioner(topic string) Partitioner { + return new(manualPartitioner) +} + +func (p *manualPartitioner) Partition(message *ProducerMessage, numPartitions int32) (int32, error) { + return message.Partition, nil +} + +func (p *manualPartitioner) RequiresConsistency() bool { + return true +} + +type randomPartitioner struct { + generator *rand.Rand +} + +// NewRandomPartitioner returns a Partitioner which chooses a random partition each time. +func NewRandomPartitioner(topic string) Partitioner { + p := new(randomPartitioner) + p.generator = rand.New(rand.NewSource(time.Now().UTC().UnixNano())) + return p +} + +func (p *randomPartitioner) Partition(message *ProducerMessage, numPartitions int32) (int32, error) { + return int32(p.generator.Intn(int(numPartitions))), nil +} + +func (p *randomPartitioner) RequiresConsistency() bool { + return false +} + +type roundRobinPartitioner struct { + partition int32 +} + +// NewRoundRobinPartitioner returns a Partitioner which walks through the available partitions one at a time. +func NewRoundRobinPartitioner(topic string) Partitioner { + return &roundRobinPartitioner{} +} + +func (p *roundRobinPartitioner) Partition(message *ProducerMessage, numPartitions int32) (int32, error) { + if p.partition >= numPartitions { + p.partition = 0 + } + ret := p.partition + p.partition++ + return ret, nil +} + +func (p *roundRobinPartitioner) RequiresConsistency() bool { + return false +} + +type hashPartitioner struct { + random Partitioner + hasher hash.Hash32 + referenceAbs bool +} + +// NewCustomHashPartitioner is a wrapper around NewHashPartitioner, allowing the use of custom hasher. +// The argument is a function providing the instance, implementing the hash.Hash32 interface. This is to ensure that +// each partition dispatcher gets its own hasher, to avoid concurrency issues by sharing an instance. +func NewCustomHashPartitioner(hasher func() hash.Hash32) PartitionerConstructor { + return func(topic string) Partitioner { + p := new(hashPartitioner) + p.random = NewRandomPartitioner(topic) + p.hasher = hasher() + p.referenceAbs = false + return p + } +} + +// NewCustomPartitioner creates a default Partitioner but lets you specify the behavior of each component via options +func NewCustomPartitioner(options ...HashPartitionerOption) PartitionerConstructor { + return func(topic string) Partitioner { + p := new(hashPartitioner) + p.random = NewRandomPartitioner(topic) + p.hasher = fnv.New32a() + p.referenceAbs = false + for _, option := range options { + option(p) + } + return p + } +} + +// NewHashPartitioner returns a Partitioner which behaves as follows. If the message's key is nil then a +// random partition is chosen. Otherwise the FNV-1a hash of the encoded bytes of the message key is used, +// modulus the number of partitions. This ensures that messages with the same key always end up on the +// same partition. +func NewHashPartitioner(topic string) Partitioner { + p := new(hashPartitioner) + p.random = NewRandomPartitioner(topic) + p.hasher = fnv.New32a() + p.referenceAbs = false + return p +} + +// NewReferenceHashPartitioner is like NewHashPartitioner except that it handles absolute values +// in the same way as the reference Java implementation. NewHashPartitioner was supposed to do +// that but it had a mistake and now there are people depending on both behaviours. This will +// all go away on the next major version bump. +func NewReferenceHashPartitioner(topic string) Partitioner { + p := new(hashPartitioner) + p.random = NewRandomPartitioner(topic) + p.hasher = fnv.New32a() + p.referenceAbs = true + return p +} + +func (p *hashPartitioner) Partition(message *ProducerMessage, numPartitions int32) (int32, error) { + if message.Key == nil { + return p.random.Partition(message, numPartitions) + } + bytes, err := message.Key.Encode() + if err != nil { + return -1, err + } + p.hasher.Reset() + _, err = p.hasher.Write(bytes) + if err != nil { + return -1, err + } + var partition int32 + // Turns out we were doing our absolute value in a subtly different way from the upstream + // implementation, but now we need to maintain backwards compat for people who started using + // the old version; if referenceAbs is set we are compatible with the reference java client + // but not past Sarama versions + if p.referenceAbs { + partition = (int32(p.hasher.Sum32()) & 0x7fffffff) % numPartitions + } else { + partition = int32(p.hasher.Sum32()) % numPartitions + if partition < 0 { + partition = -partition + } + } + return partition, nil +} + +func (p *hashPartitioner) RequiresConsistency() bool { + return true +} + +func (p *hashPartitioner) MessageRequiresConsistency(message *ProducerMessage) bool { + return message.Key != nil +} diff --git a/vendor/github.com/Shopify/sarama/prep_encoder.go b/vendor/github.com/Shopify/sarama/prep_encoder.go new file mode 100644 index 00000000000..b633cd15111 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/prep_encoder.go @@ -0,0 +1,153 @@ +package sarama + +import ( + "encoding/binary" + "fmt" + "math" + + "github.com/rcrowley/go-metrics" +) + +type prepEncoder struct { + stack []pushEncoder + length int +} + +// primitives + +func (pe *prepEncoder) putInt8(in int8) { + pe.length++ +} + +func (pe *prepEncoder) putInt16(in int16) { + pe.length += 2 +} + +func (pe *prepEncoder) putInt32(in int32) { + pe.length += 4 +} + +func (pe *prepEncoder) putInt64(in int64) { + pe.length += 8 +} + +func (pe *prepEncoder) putVarint(in int64) { + var buf [binary.MaxVarintLen64]byte + pe.length += binary.PutVarint(buf[:], in) +} + +func (pe *prepEncoder) putArrayLength(in int) error { + if in > math.MaxInt32 { + return PacketEncodingError{fmt.Sprintf("array too long (%d)", in)} + } + pe.length += 4 + return nil +} + +func (pe *prepEncoder) putBool(in bool) { + pe.length++ +} + +// arrays + +func (pe *prepEncoder) putBytes(in []byte) error { + pe.length += 4 + if in == nil { + return nil + } + return pe.putRawBytes(in) +} + +func (pe *prepEncoder) putVarintBytes(in []byte) error { + if in == nil { + pe.putVarint(-1) + return nil + } + pe.putVarint(int64(len(in))) + return pe.putRawBytes(in) +} + +func (pe *prepEncoder) putRawBytes(in []byte) error { + if len(in) > math.MaxInt32 { + return PacketEncodingError{fmt.Sprintf("byteslice too long (%d)", len(in))} + } + pe.length += len(in) + return nil +} + +func (pe *prepEncoder) putNullableString(in *string) error { + if in == nil { + pe.length += 2 + return nil + } + return pe.putString(*in) +} + +func (pe *prepEncoder) putString(in string) error { + pe.length += 2 + if len(in) > math.MaxInt16 { + return PacketEncodingError{fmt.Sprintf("string too long (%d)", len(in))} + } + pe.length += len(in) + return nil +} + +func (pe *prepEncoder) putStringArray(in []string) error { + err := pe.putArrayLength(len(in)) + if err != nil { + return err + } + + for _, str := range in { + if err := pe.putString(str); err != nil { + return err + } + } + + return nil +} + +func (pe *prepEncoder) putInt32Array(in []int32) error { + err := pe.putArrayLength(len(in)) + if err != nil { + return err + } + pe.length += 4 * len(in) + return nil +} + +func (pe *prepEncoder) putInt64Array(in []int64) error { + err := pe.putArrayLength(len(in)) + if err != nil { + return err + } + pe.length += 8 * len(in) + return nil +} + +func (pe *prepEncoder) offset() int { + return pe.length +} + +// stackable + +func (pe *prepEncoder) push(in pushEncoder) { + in.saveOffset(pe.length) + pe.length += in.reserveLength() + pe.stack = append(pe.stack, in) +} + +func (pe *prepEncoder) pop() error { + in := pe.stack[len(pe.stack)-1] + pe.stack = pe.stack[:len(pe.stack)-1] + if dpe, ok := in.(dynamicPushEncoder); ok { + pe.length += dpe.adjustLength(pe.length) + } + + return nil +} + +// we do not record metrics during the prep encoder pass +func (pe *prepEncoder) metricRegistry() metrics.Registry { + return nil +} diff --git a/vendor/github.com/Shopify/sarama/produce_request.go b/vendor/github.com/Shopify/sarama/produce_request.go new file mode 100644 index 00000000000..0c755d02b64 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/produce_request.go @@ -0,0 +1,252 @@ +package sarama + +import "github.com/rcrowley/go-metrics" + +// RequiredAcks is used in Produce Requests to tell the broker how many replica acknowledgements +// it must see before responding. Any of the constants defined here are valid. On broker versions +// prior to 0.8.2.0 any other positive int16 is also valid (the broker will wait for that many +// acknowledgements) but in 0.8.2.0 and later this will raise an exception (it has been replaced +// by setting the `min.isr` value in the brokers configuration). +type RequiredAcks int16 + +const ( + // NoResponse doesn't send any response, the TCP ACK is all you get. + NoResponse RequiredAcks = 0 + // WaitForLocal waits for only the local commit to succeed before responding. + WaitForLocal RequiredAcks = 1 + // WaitForAll waits for all in-sync replicas to commit before responding. + // The minimum number of in-sync replicas is configured on the broker via + // the `min.insync.replicas` configuration key. + WaitForAll RequiredAcks = -1 +) + +type ProduceRequest struct { + TransactionalID *string + RequiredAcks RequiredAcks + Timeout int32 + Version int16 // v1 requires Kafka 0.9, v2 requires Kafka 0.10, v3 requires Kafka 0.11 + records map[string]map[int32]Records +} + +func updateMsgSetMetrics(msgSet *MessageSet, compressionRatioMetric metrics.Histogram, + topicCompressionRatioMetric metrics.Histogram) int64 { + var topicRecordCount int64 + for _, messageBlock := range msgSet.Messages { + // Is this a fake "message" wrapping real messages? + if messageBlock.Msg.Set != nil { + topicRecordCount += int64(len(messageBlock.Msg.Set.Messages)) + } else { + // A single uncompressed message + topicRecordCount++ + } + // Better be safe than sorry when computing the compression ratio + if messageBlock.Msg.compressedSize != 0 { + compressionRatio := float64(len(messageBlock.Msg.Value)) / + float64(messageBlock.Msg.compressedSize) + // Histogram do not support decimal values, let's multiple it by 100 for better precision + intCompressionRatio := int64(100 * compressionRatio) + compressionRatioMetric.Update(intCompressionRatio) + topicCompressionRatioMetric.Update(intCompressionRatio) + } + } + return topicRecordCount +} + +func updateBatchMetrics(recordBatch *RecordBatch, compressionRatioMetric metrics.Histogram, + topicCompressionRatioMetric metrics.Histogram) int64 { + if recordBatch.compressedRecords != nil { + compressionRatio := int64(float64(recordBatch.recordsLen) / float64(len(recordBatch.compressedRecords)) * 100) + compressionRatioMetric.Update(compressionRatio) + topicCompressionRatioMetric.Update(compressionRatio) + } + + return int64(len(recordBatch.Records)) +} + +func (r *ProduceRequest) encode(pe packetEncoder) error { + if r.Version >= 3 { + if err := pe.putNullableString(r.TransactionalID); err != nil { + return err + } + } + pe.putInt16(int16(r.RequiredAcks)) + pe.putInt32(r.Timeout) + metricRegistry := pe.metricRegistry() + var batchSizeMetric metrics.Histogram + var compressionRatioMetric metrics.Histogram + if metricRegistry != nil { + batchSizeMetric = getOrRegisterHistogram("batch-size", metricRegistry) + compressionRatioMetric = getOrRegisterHistogram("compression-ratio", metricRegistry) + } + totalRecordCount := int64(0) + + err := pe.putArrayLength(len(r.records)) + if err != nil { + return err + } + + for topic, partitions := range r.records { + err = pe.putString(topic) + if err != nil { + return err + } + err = pe.putArrayLength(len(partitions)) + if err != nil { + return err + } + topicRecordCount := int64(0) + var topicCompressionRatioMetric metrics.Histogram + if metricRegistry != nil { + topicCompressionRatioMetric = getOrRegisterTopicHistogram("compression-ratio", topic, metricRegistry) + } + for id, records := range partitions { + startOffset := pe.offset() + pe.putInt32(id) + pe.push(&lengthField{}) + err = records.encode(pe) + if err != nil { + return err + } + err = pe.pop() + if err != nil { + return err + } + if metricRegistry != nil { + if r.Version >= 3 { + topicRecordCount += updateBatchMetrics(records.RecordBatch, compressionRatioMetric, topicCompressionRatioMetric) + } else { + topicRecordCount += updateMsgSetMetrics(records.MsgSet, compressionRatioMetric, topicCompressionRatioMetric) + } + batchSize := int64(pe.offset() - startOffset) + batchSizeMetric.Update(batchSize) + getOrRegisterTopicHistogram("batch-size", topic, metricRegistry).Update(batchSize) + } + } + if topicRecordCount > 0 { + getOrRegisterTopicMeter("record-send-rate", topic, metricRegistry).Mark(topicRecordCount) + getOrRegisterTopicHistogram("records-per-request", topic, metricRegistry).Update(topicRecordCount) + totalRecordCount += topicRecordCount + } + } + if totalRecordCount > 0 { + metrics.GetOrRegisterMeter("record-send-rate", metricRegistry).Mark(totalRecordCount) + getOrRegisterHistogram("records-per-request", metricRegistry).Update(totalRecordCount) + } + + return nil +} + +func (r *ProduceRequest) decode(pd packetDecoder, version int16) error { + r.Version = version + + if version >= 3 { + id, err := pd.getNullableString() + if err != nil { + return err + } + r.TransactionalID = id + } + requiredAcks, err := pd.getInt16() + if err != nil { + return err + } + r.RequiredAcks = RequiredAcks(requiredAcks) + if r.Timeout, err = pd.getInt32(); err != nil { + return err + } + topicCount, err := pd.getArrayLength() + if err != nil { + return err + } + if topicCount == 0 { + return nil + } + + r.records = make(map[string]map[int32]Records) + for i := 0; i < topicCount; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + partitionCount, err := pd.getArrayLength() + if err != nil { + return err + } + r.records[topic] = make(map[int32]Records) + + for j := 0; j < partitionCount; j++ { + partition, err := pd.getInt32() + if err != nil { + return err + } + size, err := pd.getInt32() + if err != nil { + return err + } + recordsDecoder, err := pd.getSubset(int(size)) + if err != nil { + return err + } + var records Records + if err := records.decode(recordsDecoder); err != nil { + return err + } + r.records[topic][partition] = records + } + } + + return nil +} + +func (r *ProduceRequest) key() int16 { + return 0 +} + +func (r *ProduceRequest) version() int16 { + return r.Version +} + +func (r *ProduceRequest) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_9_0_0 + case 2: + return V0_10_0_0 + case 3: + return V0_11_0_0 + default: + return MinVersion + } +} + +func (r *ProduceRequest) ensureRecords(topic string, partition int32) { + if r.records == nil { + r.records = make(map[string]map[int32]Records) + } + + if r.records[topic] == nil { + r.records[topic] = make(map[int32]Records) + } +} + +func (r *ProduceRequest) AddMessage(topic string, partition int32, msg *Message) { + r.ensureRecords(topic, partition) + set := r.records[topic][partition].MsgSet + + if set == nil { + set = new(MessageSet) + r.records[topic][partition] = newLegacyRecords(set) + } + + set.addMessage(msg) +} + +func (r *ProduceRequest) AddSet(topic string, partition int32, set *MessageSet) { + r.ensureRecords(topic, partition) + r.records[topic][partition] = newLegacyRecords(set) +} + +func (r *ProduceRequest) AddBatch(topic string, partition int32, batch *RecordBatch) { + r.ensureRecords(topic, partition) + r.records[topic][partition] = newDefaultRecords(batch) +} diff --git a/vendor/github.com/Shopify/sarama/produce_response.go b/vendor/github.com/Shopify/sarama/produce_response.go new file mode 100644 index 00000000000..667e34c661b --- /dev/null +++ b/vendor/github.com/Shopify/sarama/produce_response.go @@ -0,0 +1,183 @@ +package sarama + +import ( + "fmt" + "time" +) + +type ProduceResponseBlock struct { + Err KError + Offset int64 + // only provided if Version >= 2 and the broker is configured with `LogAppendTime` + Timestamp time.Time +} + +func (b *ProduceResponseBlock) decode(pd packetDecoder, version int16) (err error) { + tmp, err := pd.getInt16() + if err != nil { + return err + } + b.Err = KError(tmp) + + b.Offset, err = pd.getInt64() + if err != nil { + return err + } + + if version >= 2 { + if millis, err := pd.getInt64(); err != nil { + return err + } else if millis != -1 { + b.Timestamp = time.Unix(millis/1000, (millis%1000)*int64(time.Millisecond)) + } + } + + return nil +} + +func (b *ProduceResponseBlock) encode(pe packetEncoder, version int16) (err error) { + pe.putInt16(int16(b.Err)) + pe.putInt64(b.Offset) + + if version >= 2 { + timestamp := int64(-1) + if !b.Timestamp.Before(time.Unix(0, 0)) { + timestamp = b.Timestamp.UnixNano() / int64(time.Millisecond) + } else if !b.Timestamp.IsZero() { + return PacketEncodingError{fmt.Sprintf("invalid timestamp (%v)", b.Timestamp)} + } + pe.putInt64(timestamp) + } + + return nil +} + +type ProduceResponse struct { + Blocks map[string]map[int32]*ProduceResponseBlock + Version int16 + ThrottleTime time.Duration // only provided if Version >= 1 +} + +func (r *ProduceResponse) decode(pd packetDecoder, version int16) (err error) { + r.Version = version + + numTopics, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Blocks = make(map[string]map[int32]*ProduceResponseBlock, numTopics) + for i := 0; i < numTopics; i++ { + name, err := pd.getString() + if err != nil { + return err + } + + numBlocks, err := pd.getArrayLength() + if err != nil { + return err + } + + r.Blocks[name] = make(map[int32]*ProduceResponseBlock, numBlocks) + + for j := 0; j < numBlocks; j++ { + id, err := pd.getInt32() + if err != nil { + return err + } + + block := new(ProduceResponseBlock) + err = block.decode(pd, version) + if err != nil { + return err + } + r.Blocks[name][id] = block + } + } + + if r.Version >= 1 { + millis, err := pd.getInt32() + if err != nil { + return err + } + + r.ThrottleTime = time.Duration(millis) * time.Millisecond + } + + return nil +} + +func (r *ProduceResponse) encode(pe packetEncoder) error { + err := pe.putArrayLength(len(r.Blocks)) + if err != nil { + return err + } + for topic, partitions := range r.Blocks { + err = pe.putString(topic) + if err != nil { + return err + } + err = pe.putArrayLength(len(partitions)) + if err != nil { + return err + } + for id, prb := range partitions { + pe.putInt32(id) + err = prb.encode(pe, r.Version) + if err != nil { + return err + } + } + } + if r.Version >= 1 { + pe.putInt32(int32(r.ThrottleTime / time.Millisecond)) + } + return nil +} + +func (r *ProduceResponse) key() int16 { + return 0 +} + +func (r *ProduceResponse) version() int16 { + return r.Version +} + +func (r *ProduceResponse) requiredVersion() KafkaVersion { + switch r.Version { + case 1: + return V0_9_0_0 + case 2: + return V0_10_0_0 + case 3: + return V0_11_0_0 + default: + return MinVersion + } +} + +func (r *ProduceResponse) GetBlock(topic string, partition int32) *ProduceResponseBlock { + if r.Blocks == nil { + return nil + } + + if r.Blocks[topic] == nil { + return nil + } + + return r.Blocks[topic][partition] +} + +// Testing API + +func (r *ProduceResponse) AddTopicPartition(topic string, partition int32, err KError) { + if r.Blocks == nil { + r.Blocks = make(map[string]map[int32]*ProduceResponseBlock) + } + byTopic, ok := r.Blocks[topic] + if !ok { + byTopic = make(map[int32]*ProduceResponseBlock) + r.Blocks[topic] = byTopic + } + byTopic[partition] = &ProduceResponseBlock{Err: err} +} diff --git a/vendor/github.com/Shopify/sarama/produce_set.go b/vendor/github.com/Shopify/sarama/produce_set.go new file mode 100644 index 00000000000..13be2b3c92b --- /dev/null +++ b/vendor/github.com/Shopify/sarama/produce_set.go @@ -0,0 +1,252 @@ +package sarama + +import ( + "encoding/binary" + "time" +) + +type partitionSet struct { + msgs []*ProducerMessage + recordsToSend Records + bufferBytes int +} + +type produceSet struct { + parent *asyncProducer + msgs map[string]map[int32]*partitionSet + + bufferBytes int + bufferCount int +} + +func newProduceSet(parent *asyncProducer) *produceSet { + return &produceSet{ + msgs: make(map[string]map[int32]*partitionSet), + parent: parent, + } +} + +func (ps *produceSet) add(msg *ProducerMessage) error { + var err error + var key, val []byte + + if msg.Key != nil { + if key, err = msg.Key.Encode(); err != nil { + return err + } + } + + if msg.Value != nil { + if val, err = msg.Value.Encode(); err != nil { + return err + } + } + + timestamp := msg.Timestamp + if msg.Timestamp.IsZero() { + timestamp = time.Now() + } + + partitions := ps.msgs[msg.Topic] + if partitions == nil { + partitions = make(map[int32]*partitionSet) + ps.msgs[msg.Topic] = partitions + } + + var size int + + set := partitions[msg.Partition] + if set == nil { + if ps.parent.conf.Version.IsAtLeast(V0_11_0_0) { + batch := &RecordBatch{ + FirstTimestamp: timestamp, + Version: 2, + ProducerID: -1, /* No producer id */ + Codec: ps.parent.conf.Producer.Compression, + CompressionLevel: ps.parent.conf.Producer.CompressionLevel, + } + set = &partitionSet{recordsToSend: newDefaultRecords(batch)} + size = recordBatchOverhead + } else { + set = &partitionSet{recordsToSend: newLegacyRecords(new(MessageSet))} + } + partitions[msg.Partition] = set + } + + set.msgs = append(set.msgs, msg) + if ps.parent.conf.Version.IsAtLeast(V0_11_0_0) { + // We are being conservative here to avoid having to prep encode the record + size += maximumRecordOverhead + rec := &Record{ + Key: key, + Value: val, + TimestampDelta: timestamp.Sub(set.recordsToSend.RecordBatch.FirstTimestamp), + } + size += len(key) + len(val) + if len(msg.Headers) > 0 { + rec.Headers = make([]*RecordHeader, len(msg.Headers)) + for i := range msg.Headers { + rec.Headers[i] = &msg.Headers[i] + size += len(rec.Headers[i].Key) + len(rec.Headers[i].Value) + 2*binary.MaxVarintLen32 + } + } + set.recordsToSend.RecordBatch.addRecord(rec) + } else { + msgToSend := &Message{Codec: CompressionNone, Key: key, Value: val} + if ps.parent.conf.Version.IsAtLeast(V0_10_0_0) { + msgToSend.Timestamp = timestamp + msgToSend.Version = 1 + } + set.recordsToSend.MsgSet.addMessage(msgToSend) + size = producerMessageOverhead + len(key) + len(val) + } + + set.bufferBytes += size + ps.bufferBytes += size + ps.bufferCount++ + + return nil +} + +func (ps *produceSet) buildRequest() *ProduceRequest { + req := &ProduceRequest{ + RequiredAcks: ps.parent.conf.Producer.RequiredAcks, + Timeout: int32(ps.parent.conf.Producer.Timeout / time.Millisecond), + } + if ps.parent.conf.Version.IsAtLeast(V0_10_0_0) { + req.Version = 2 + } + if ps.parent.conf.Version.IsAtLeast(V0_11_0_0) { + req.Version = 3 + } + + for topic, partitionSet := range ps.msgs { + for partition, set := range partitionSet { + if req.Version >= 3 { + // If the API version we're hitting is 3 or greater, we need to calculate + // offsets for each record in the batch relative to FirstOffset. + // Additionally, we must set LastOffsetDelta to the value of the last offset + // in the batch. Since the OffsetDelta of the first record is 0, we know that the + // final record of any batch will have an offset of (# of records in batch) - 1. + // (See https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol#AGuideToTheKafkaProtocol-Messagesets + // under the RecordBatch section for details.) + rb := set.recordsToSend.RecordBatch + if len(rb.Records) > 0 { + rb.LastOffsetDelta = int32(len(rb.Records) - 1) + for i, record := range rb.Records { + record.OffsetDelta = int64(i) + } + } + + req.AddBatch(topic, partition, rb) + continue + } + if ps.parent.conf.Producer.Compression == CompressionNone { + req.AddSet(topic, partition, set.recordsToSend.MsgSet) + } else { + // When compression is enabled, the entire set for each partition is compressed + // and sent as the payload of a single fake "message" with the appropriate codec + // set and no key. When the server sees a message with a compression codec, it + // decompresses the payload and treats the result as its message set. + + if ps.parent.conf.Version.IsAtLeast(V0_10_0_0) { + // If our version is 0.10 or later, assign relative offsets + // to the inner messages. This lets the broker avoid + // recompressing the message set. + // (See https://cwiki.apache.org/confluence/display/KAFKA/KIP-31+-+Move+to+relative+offsets+in+compressed+message+sets + // for details on relative offsets.) + for i, msg := range set.recordsToSend.MsgSet.Messages { + msg.Offset = int64(i) + } + } + payload, err := encode(set.recordsToSend.MsgSet, ps.parent.conf.MetricRegistry) + if err != nil { + Logger.Println(err) // if this happens, it's basically our fault. + panic(err) + } + compMsg := &Message{ + Codec: ps.parent.conf.Producer.Compression, + CompressionLevel: ps.parent.conf.Producer.CompressionLevel, + Key: nil, + Value: payload, + Set: set.recordsToSend.MsgSet, // Provide the underlying message set for accurate metrics + } + if ps.parent.conf.Version.IsAtLeast(V0_10_0_0) { + compMsg.Version = 1 + compMsg.Timestamp = set.recordsToSend.MsgSet.Messages[0].Msg.Timestamp + } + req.AddMessage(topic, partition, compMsg) + } + } + } + + return req +} + +func (ps *produceSet) eachPartition(cb func(topic string, partition int32, msgs []*ProducerMessage)) { + for topic, partitionSet := range ps.msgs { + for partition, set := range partitionSet { + cb(topic, partition, set.msgs) + } + } +} + +func (ps *produceSet) dropPartition(topic string, partition int32) []*ProducerMessage { + if ps.msgs[topic] == nil { + return nil + } + set := ps.msgs[topic][partition] + if set == nil { + return nil + } + ps.bufferBytes -= set.bufferBytes + ps.bufferCount -= len(set.msgs) + delete(ps.msgs[topic], partition) + return set.msgs +} + +func (ps *produceSet) wouldOverflow(msg *ProducerMessage) bool { + version := 1 + if ps.parent.conf.Version.IsAtLeast(V0_11_0_0) { + version = 2 + } + + switch { + // Would we overflow our maximum possible size-on-the-wire? 10KiB is arbitrary overhead for safety. + case ps.bufferBytes+msg.byteSize(version) >= int(MaxRequestSize-(10*1024)): + return true + // Would we overflow the size-limit of a compressed message-batch for this partition? + case ps.parent.conf.Producer.Compression != CompressionNone && + ps.msgs[msg.Topic] != nil && ps.msgs[msg.Topic][msg.Partition] != nil && + ps.msgs[msg.Topic][msg.Partition].bufferBytes+msg.byteSize(version) >= ps.parent.conf.Producer.MaxMessageBytes: + return true + // Would we overflow simply in number of messages? + case ps.parent.conf.Producer.Flush.MaxMessages > 0 && ps.bufferCount >= ps.parent.conf.Producer.Flush.MaxMessages: + return true + default: + return false + } +} + +func (ps *produceSet) readyToFlush() bool { + switch { + // If we don't have any messages, nothing else matters + case ps.empty(): + return false + // If all three config values are 0, we always flush as-fast-as-possible + case ps.parent.conf.Producer.Flush.Frequency == 0 && ps.parent.conf.Producer.Flush.Bytes == 0 && ps.parent.conf.Producer.Flush.Messages == 0: + return true + // If we've passed the message trigger-point + case ps.parent.conf.Producer.Flush.Messages > 0 && ps.bufferCount >= ps.parent.conf.Producer.Flush.Messages: + return true + // If we've passed the byte trigger-point + case ps.parent.conf.Producer.Flush.Bytes > 0 && ps.bufferBytes >= ps.parent.conf.Producer.Flush.Bytes: + return true + default: + return false + } +} + +func (ps *produceSet) empty() bool { + return ps.bufferCount == 0 +} diff --git a/vendor/github.com/Shopify/sarama/real_decoder.go b/vendor/github.com/Shopify/sarama/real_decoder.go new file mode 100644 index 00000000000..23045e7d33a --- /dev/null +++ b/vendor/github.com/Shopify/sarama/real_decoder.go @@ -0,0 +1,324 @@ +package sarama + +import ( + "encoding/binary" + "math" +) + +var errInvalidArrayLength = PacketDecodingError{"invalid array length"} +var errInvalidByteSliceLength = PacketDecodingError{"invalid byteslice length"} +var errInvalidByteSliceLengthType = PacketDecodingError{"invalid byteslice length type"} +var errInvalidStringLength = PacketDecodingError{"invalid string length"} +var errInvalidSubsetSize = PacketDecodingError{"invalid subset size"} +var errVarintOverflow = PacketDecodingError{"varint overflow"} +var errInvalidBool = PacketDecodingError{"invalid bool"} + +type realDecoder struct { + raw []byte + off int + stack []pushDecoder +} + +// primitives + +func (rd *realDecoder) getInt8() (int8, error) { + if rd.remaining() < 1 { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } + tmp := int8(rd.raw[rd.off]) + rd.off++ + return tmp, nil +} + +func (rd *realDecoder) getInt16() (int16, error) { + if rd.remaining() < 2 { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } + tmp := int16(binary.BigEndian.Uint16(rd.raw[rd.off:])) + rd.off += 2 + return tmp, nil +} + +func (rd *realDecoder) getInt32() (int32, error) { + if rd.remaining() < 4 { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } + tmp := int32(binary.BigEndian.Uint32(rd.raw[rd.off:])) + rd.off += 4 + return tmp, nil +} + +func (rd *realDecoder) getInt64() (int64, error) { + if rd.remaining() < 8 { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } + tmp := int64(binary.BigEndian.Uint64(rd.raw[rd.off:])) + rd.off += 8 + return tmp, nil +} + +func (rd *realDecoder) getVarint() (int64, error) { + tmp, n := binary.Varint(rd.raw[rd.off:]) + if n == 0 { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } + if n < 0 { + rd.off -= n + return -1, errVarintOverflow + } + rd.off += n + return tmp, nil +} + +func (rd *realDecoder) getArrayLength() (int, error) { + if rd.remaining() < 4 { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } + tmp := int(int32(binary.BigEndian.Uint32(rd.raw[rd.off:]))) + rd.off += 4 + if tmp > rd.remaining() { + rd.off = len(rd.raw) + return -1, ErrInsufficientData + } else if tmp > 2*math.MaxUint16 { + return -1, errInvalidArrayLength + } + return tmp, nil +} + +func (rd *realDecoder) getBool() (bool, error) { + b, err := rd.getInt8() + if err != nil || b == 0 { + return false, err + } + if b != 1 { + return false, errInvalidBool + } + return true, nil +} + +// collections + +func (rd *realDecoder) getBytes() ([]byte, error) { + tmp, err := rd.getInt32() + if err != nil { + return nil, err + } + if tmp == -1 { + return nil, nil + } + + return rd.getRawBytes(int(tmp)) +} + +func (rd *realDecoder) getVarintBytes() ([]byte, error) { + tmp, err := rd.getVarint() + if err != nil { + return nil, err + } + if tmp == -1 { + return nil, nil + } + + return rd.getRawBytes(int(tmp)) +} + +func (rd *realDecoder) getStringLength() (int, error) { + length, err := rd.getInt16() + if err != nil { + return 0, err + } + + n := int(length) + + switch { + case n < -1: + return 0, errInvalidStringLength + case n > rd.remaining(): + rd.off = len(rd.raw) + return 0, ErrInsufficientData + } + + return n, nil +} + +func (rd *realDecoder) getString() (string, error) { + n, err := rd.getStringLength() + if err != nil || n == -1 { + return "", err + } + + tmpStr := string(rd.raw[rd.off : rd.off+n]) + rd.off += n + return tmpStr, nil +} + +func (rd *realDecoder) getNullableString() (*string, error) { + n, err := rd.getStringLength() + if err != nil || n == -1 { + return nil, err + } + + tmpStr := string(rd.raw[rd.off : rd.off+n]) + rd.off += n + return &tmpStr, err +} + +func (rd *realDecoder) getInt32Array() ([]int32, error) { + if rd.remaining() < 4 { + rd.off = len(rd.raw) + return nil, ErrInsufficientData + } + n := int(binary.BigEndian.Uint32(rd.raw[rd.off:])) + rd.off += 4 + + if rd.remaining() < 4*n { + rd.off = len(rd.raw) + return nil, ErrInsufficientData + } + + if n == 0 { + return nil, nil + } + + if n < 0 { + return nil, errInvalidArrayLength + } + + ret := make([]int32, n) + for i := range ret { + ret[i] = int32(binary.BigEndian.Uint32(rd.raw[rd.off:])) + rd.off += 4 + } + return ret, nil +} + +func (rd *realDecoder) getInt64Array() ([]int64, error) { + if rd.remaining() < 4 { + rd.off = len(rd.raw) + return nil, ErrInsufficientData + } + n := int(binary.BigEndian.Uint32(rd.raw[rd.off:])) + rd.off += 4 + + if rd.remaining() < 8*n { + rd.off = len(rd.raw) + return nil, ErrInsufficientData + } + + if n == 0 { + return nil, nil + } + + if n < 0 { + return nil, errInvalidArrayLength + } + + ret := make([]int64, n) + for i := range ret { + ret[i] = int64(binary.BigEndian.Uint64(rd.raw[rd.off:])) + rd.off += 8 + } + return ret, nil +} + +func (rd *realDecoder) getStringArray() ([]string, error) { + if rd.remaining() < 4 { + rd.off = len(rd.raw) + return nil, ErrInsufficientData + } + n := int(binary.BigEndian.Uint32(rd.raw[rd.off:])) + rd.off += 4 + + if n == 0 { + return nil, nil + } + + if n < 0 { + return nil, errInvalidArrayLength + } + + ret := make([]string, n) + for i := range ret { + str, err := rd.getString() + if err != nil { + return nil, err + } + + ret[i] = str + } + return ret, nil +} + +// subsets + +func (rd *realDecoder) remaining() int { + return len(rd.raw) - rd.off +} + +func (rd *realDecoder) getSubset(length int) (packetDecoder, error) { + buf, err := rd.getRawBytes(length) + if err != nil { + return nil, err + } + return &realDecoder{raw: buf}, nil +} + +func (rd *realDecoder) getRawBytes(length int) ([]byte, error) { + if length < 0 { + return nil, errInvalidByteSliceLength + } else if length > rd.remaining() { + rd.off = len(rd.raw) + return nil, ErrInsufficientData + } + + start := rd.off + rd.off += length + return rd.raw[start:rd.off], nil +} + +func (rd *realDecoder) peek(offset, length int) (packetDecoder, error) { + if rd.remaining() < offset+length { + return nil, ErrInsufficientData + } + off := rd.off + offset + return &realDecoder{raw: rd.raw[off : off+length]}, nil +} + +// stacks + +func (rd *realDecoder) push(in pushDecoder) error { + in.saveOffset(rd.off) + + var reserve int + if dpd, ok := in.(dynamicPushDecoder); ok { + if err := dpd.decode(rd); err != nil { + return err + } + } else { + reserve = in.reserveLength() + if rd.remaining() < reserve { + rd.off = len(rd.raw) + return ErrInsufficientData + } + } + + rd.stack = append(rd.stack, in) + + rd.off += reserve + + return nil +} + +func (rd *realDecoder) pop() error { + // this is go's ugly pop pattern (the inverse of append) + in := rd.stack[len(rd.stack)-1] + rd.stack = rd.stack[:len(rd.stack)-1] + + return in.check(rd.off, rd.raw) +} diff --git a/vendor/github.com/Shopify/sarama/real_encoder.go b/vendor/github.com/Shopify/sarama/real_encoder.go new file mode 100644 index 00000000000..3c75387f779 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/real_encoder.go @@ -0,0 +1,156 @@ +package sarama + +import ( + "encoding/binary" + + "github.com/rcrowley/go-metrics" +) + +type realEncoder struct { + raw []byte + off int + stack []pushEncoder + registry metrics.Registry +} + +// primitives + +func (re *realEncoder) putInt8(in int8) { + re.raw[re.off] = byte(in) + re.off++ +} + +func (re *realEncoder) putInt16(in int16) { + binary.BigEndian.PutUint16(re.raw[re.off:], uint16(in)) + re.off += 2 +} + +func (re *realEncoder) putInt32(in int32) { + binary.BigEndian.PutUint32(re.raw[re.off:], uint32(in)) + re.off += 4 +} + +func (re *realEncoder) putInt64(in int64) { + binary.BigEndian.PutUint64(re.raw[re.off:], uint64(in)) + re.off += 8 +} + +func (re *realEncoder) putVarint(in int64) { + re.off += binary.PutVarint(re.raw[re.off:], in) +} + +func (re *realEncoder) putArrayLength(in int) error { + re.putInt32(int32(in)) + return nil +} + +func (re *realEncoder) putBool(in bool) { + if in { + re.putInt8(1) + return + } + re.putInt8(0) +} + +// collection + +func (re *realEncoder) putRawBytes(in []byte) error { + copy(re.raw[re.off:], in) + re.off += len(in) + return nil +} + +func (re *realEncoder) putBytes(in []byte) error { + if in == nil { + re.putInt32(-1) + return nil + } + re.putInt32(int32(len(in))) + return re.putRawBytes(in) +} + +func (re *realEncoder) putVarintBytes(in []byte) error { + if in == nil { + re.putVarint(-1) + return nil + } + re.putVarint(int64(len(in))) + return re.putRawBytes(in) +} + +func (re *realEncoder) putString(in string) error { + re.putInt16(int16(len(in))) + copy(re.raw[re.off:], in) + re.off += len(in) + return nil +} + +func (re *realEncoder) putNullableString(in *string) error { + if in == nil { + re.putInt16(-1) + return nil + } + return re.putString(*in) +} + +func (re *realEncoder) putStringArray(in []string) error { + err := re.putArrayLength(len(in)) + if err != nil { + return err + } + + for _, val := range in { + if err := re.putString(val); err != nil { + return err + } + } + + return nil +} + +func (re *realEncoder) putInt32Array(in []int32) error { + err := re.putArrayLength(len(in)) + if err != nil { + return err + } + for _, val := range in { + re.putInt32(val) + } + return nil +} + +func (re *realEncoder) putInt64Array(in []int64) error { + err := re.putArrayLength(len(in)) + if err != nil { + return err + } + for _, val := range in { + re.putInt64(val) + } + return nil +} + +func (re *realEncoder) offset() int { + return re.off +} + +// stacks + +func (re *realEncoder) push(in pushEncoder) { + in.saveOffset(re.off) + re.off += in.reserveLength() + re.stack = append(re.stack, in) +} + +func (re *realEncoder) pop() error { + // this is go's ugly pop pattern (the inverse of append) + in := re.stack[len(re.stack)-1] + re.stack = re.stack[:len(re.stack)-1] + + return in.run(re.off, re.raw) +} + +// we do record metrics during the real encoder pass +func (re *realEncoder) metricRegistry() metrics.Registry { + return re.registry +} diff --git a/vendor/github.com/Shopify/sarama/record.go b/vendor/github.com/Shopify/sarama/record.go new file mode 100644 index 00000000000..cded308cf0f --- /dev/null +++ b/vendor/github.com/Shopify/sarama/record.go @@ -0,0 +1,113 @@ +package sarama + +import ( + "encoding/binary" + "time" +) + +const ( + controlMask = 0x20 + maximumRecordOverhead = 5*binary.MaxVarintLen32 + binary.MaxVarintLen64 + 1 +) + +type RecordHeader struct { + Key []byte + Value []byte +} + +func (h *RecordHeader) encode(pe packetEncoder) error { + if err := pe.putVarintBytes(h.Key); err != nil { + return err + } + return pe.putVarintBytes(h.Value) +} + +func (h *RecordHeader) decode(pd packetDecoder) (err error) { + if h.Key, err = pd.getVarintBytes(); err != nil { + return err + } + + if h.Value, err = pd.getVarintBytes(); err != nil { + return err + } + return nil +} + +type Record struct { + Attributes int8 + TimestampDelta time.Duration + OffsetDelta int64 + Key []byte + Value []byte + Headers []*RecordHeader + + length varintLengthField +} + +func (r *Record) encode(pe packetEncoder) error { + pe.push(&r.length) + pe.putInt8(r.Attributes) + pe.putVarint(int64(r.TimestampDelta / time.Millisecond)) + pe.putVarint(r.OffsetDelta) + if err := pe.putVarintBytes(r.Key); err != nil { + return err + } + if err := pe.putVarintBytes(r.Value); err != nil { + return err + } + pe.putVarint(int64(len(r.Headers))) + + for _, h := range r.Headers { + if err := h.encode(pe); err != nil { + return err + } + } + + return pe.pop() +} + +func (r *Record) decode(pd packetDecoder) (err error) { + if err = pd.push(&r.length); err != nil { + return err + } + + if r.Attributes, err = pd.getInt8(); err != nil { + return err + } + + timestamp, err := pd.getVarint() + if err != nil { + return err + } + r.TimestampDelta = time.Duration(timestamp) * time.Millisecond + + if r.OffsetDelta, err = pd.getVarint(); err != nil { + return err + } + + if r.Key, err = pd.getVarintBytes(); err != nil { + return err + } + + if r.Value, err = pd.getVarintBytes(); err != nil { + return err + } + + numHeaders, err := pd.getVarint() + if err != nil { + return err + } + + if numHeaders >= 0 { + r.Headers = make([]*RecordHeader, numHeaders) + } + for i := int64(0); i < numHeaders; i++ { + hdr := new(RecordHeader) + if err := hdr.decode(pd); err != nil { + return err + } + r.Headers[i] = hdr + } + + return pd.pop() +} diff --git a/vendor/github.com/Shopify/sarama/record_batch.go b/vendor/github.com/Shopify/sarama/record_batch.go new file mode 100644 index 00000000000..845318aa341 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/record_batch.go @@ -0,0 +1,268 @@ +package sarama + +import ( + "bytes" + "compress/gzip" + "fmt" + "io/ioutil" + "time" + + "github.com/eapache/go-xerial-snappy" + "github.com/pierrec/lz4" +) + +const recordBatchOverhead = 49 + +type recordsArray []*Record + +func (e recordsArray) encode(pe packetEncoder) error { + for _, r := range e { + if err := r.encode(pe); err != nil { + return err + } + } + return nil +} + +func (e recordsArray) decode(pd packetDecoder) error { + for i := range e { + rec := &Record{} + if err := rec.decode(pd); err != nil { + return err + } + e[i] = rec + } + return nil +} + +type RecordBatch struct { + FirstOffset int64 + PartitionLeaderEpoch int32 + Version int8 + Codec CompressionCodec + CompressionLevel int + Control bool + LastOffsetDelta int32 + FirstTimestamp time.Time + MaxTimestamp time.Time + ProducerID int64 + ProducerEpoch int16 + FirstSequence int32 + Records []*Record + PartialTrailingRecord bool + + compressedRecords []byte + recordsLen int // uncompressed records size +} + +func (b *RecordBatch) encode(pe packetEncoder) error { + if b.Version != 2 { + return PacketEncodingError{fmt.Sprintf("unsupported compression codec (%d)", b.Codec)} + } + pe.putInt64(b.FirstOffset) + pe.push(&lengthField{}) + pe.putInt32(b.PartitionLeaderEpoch) + pe.putInt8(b.Version) + pe.push(newCRC32Field(crcCastagnoli)) + pe.putInt16(b.computeAttributes()) + pe.putInt32(b.LastOffsetDelta) + + if err := (Timestamp{&b.FirstTimestamp}).encode(pe); err != nil { + return err + } + + if err := (Timestamp{&b.MaxTimestamp}).encode(pe); err != nil { + return err + } + + pe.putInt64(b.ProducerID) + pe.putInt16(b.ProducerEpoch) + pe.putInt32(b.FirstSequence) + + if err := pe.putArrayLength(len(b.Records)); err != nil { + return err + } + + if b.compressedRecords == nil { + if err := b.encodeRecords(pe); err != nil { + return err + } + } + if err := pe.putRawBytes(b.compressedRecords); err != nil { + return err + } + + if err := pe.pop(); err != nil { + return err + } + return pe.pop() +} + +func (b *RecordBatch) decode(pd packetDecoder) (err error) { + if b.FirstOffset, err = pd.getInt64(); err != nil { + return err + } + + batchLen, err := pd.getInt32() + if err != nil { + return err + } + + if b.PartitionLeaderEpoch, err = pd.getInt32(); err != nil { + return err + } + + if b.Version, err = pd.getInt8(); err != nil { + return err + } + + if err = pd.push(&crc32Field{polynomial: crcCastagnoli}); err != nil { + return err + } + + attributes, err := pd.getInt16() + if err != nil { + return err + } + b.Codec = CompressionCodec(int8(attributes) & compressionCodecMask) + b.Control = attributes&controlMask == controlMask + + if b.LastOffsetDelta, err = pd.getInt32(); err != nil { + return err + } + + if err = (Timestamp{&b.FirstTimestamp}).decode(pd); err != nil { + return err + } + + if err = (Timestamp{&b.MaxTimestamp}).decode(pd); err != nil { + return err + } + + if b.ProducerID, err = pd.getInt64(); err != nil { + return err + } + + if b.ProducerEpoch, err = pd.getInt16(); err != nil { + return err + } + + if b.FirstSequence, err = pd.getInt32(); err != nil { + return err + } + + numRecs, err := pd.getArrayLength() + if err != nil { + return err + } + if numRecs >= 0 { + b.Records = make([]*Record, numRecs) + } + + bufSize := int(batchLen) - recordBatchOverhead + recBuffer, err := pd.getRawBytes(bufSize) + if err != nil { + if err == ErrInsufficientData { + b.PartialTrailingRecord = true + b.Records = nil + return nil + } + return err + } + + if err = pd.pop(); err != nil { + return err + } + + switch b.Codec { + case CompressionNone: + case CompressionGZIP: + reader, err := gzip.NewReader(bytes.NewReader(recBuffer)) + if err != nil { + return err + } + if recBuffer, err = ioutil.ReadAll(reader); err != nil { + return err + } + case CompressionSnappy: + if recBuffer, err = snappy.Decode(recBuffer); err != nil { + return err + } + case CompressionLZ4: + reader := lz4.NewReader(bytes.NewReader(recBuffer)) + if recBuffer, err = ioutil.ReadAll(reader); err != nil { + return err + } + default: + return PacketDecodingError{fmt.Sprintf("invalid compression specified (%d)", b.Codec)} + } + + b.recordsLen = len(recBuffer) + err = decode(recBuffer, recordsArray(b.Records)) + if err == ErrInsufficientData { + b.PartialTrailingRecord = true + b.Records = nil + return nil + } + return err +} + +func (b *RecordBatch) encodeRecords(pe packetEncoder) error { + var raw []byte + var err error + if raw, err = encode(recordsArray(b.Records), pe.metricRegistry()); err != nil { + return err + } + b.recordsLen = len(raw) + + switch b.Codec { + case CompressionNone: + b.compressedRecords = raw + case CompressionGZIP: + var buf bytes.Buffer + var writer *gzip.Writer + if b.CompressionLevel != CompressionLevelDefault { + writer, err = gzip.NewWriterLevel(&buf, b.CompressionLevel) + if err != nil { + return err + } + } else { + writer = gzip.NewWriter(&buf) + } + if _, err := writer.Write(raw); err != nil { + return err + } + if err := writer.Close(); err != nil { + return err + } + b.compressedRecords = buf.Bytes() + case CompressionSnappy: + b.compressedRecords = snappy.Encode(raw) + case CompressionLZ4: + var buf bytes.Buffer + writer := lz4.NewWriter(&buf) + if _, err := writer.Write(raw); err != nil { + return err + } + if err := writer.Close(); err != nil { + return err + } + b.compressedRecords = buf.Bytes() + default: + return PacketEncodingError{fmt.Sprintf("unsupported compression codec (%d)", b.Codec)} + } + + return nil +} + +func (b *RecordBatch) computeAttributes() int16 { + attr := int16(b.Codec) & int16(compressionCodecMask) + if b.Control { + attr |= controlMask + } + return attr +} + +func (b *RecordBatch) addRecord(r *Record) { + b.Records = append(b.Records, r) +} diff --git a/vendor/github.com/Shopify/sarama/records.go b/vendor/github.com/Shopify/sarama/records.go new file mode 100644 index 00000000000..192f5927b21 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/records.go @@ -0,0 +1,194 @@ +package sarama + +import "fmt" + +const ( + unknownRecords = iota + legacyRecords + defaultRecords + + magicOffset = 16 + magicLength = 1 +) + +// Records implements a union type containing either a RecordBatch or a legacy MessageSet. +type Records struct { + recordsType int + MsgSet *MessageSet + RecordBatch *RecordBatch +} + +func newLegacyRecords(msgSet *MessageSet) Records { + return Records{recordsType: legacyRecords, MsgSet: msgSet} +} + +func newDefaultRecords(batch *RecordBatch) Records { + return Records{recordsType: defaultRecords, RecordBatch: batch} +} + +// setTypeFromFields sets type of Records depending on which of MsgSet or RecordBatch is not nil. +// The first return value indicates whether both fields are nil (and the type is not set). +// If both fields are not nil, it returns an error. +func (r *Records) setTypeFromFields() (bool, error) { + if r.MsgSet == nil && r.RecordBatch == nil { + return true, nil + } + if r.MsgSet != nil && r.RecordBatch != nil { + return false, fmt.Errorf("both MsgSet and RecordBatch are set, but record type is unknown") + } + r.recordsType = defaultRecords + if r.MsgSet != nil { + r.recordsType = legacyRecords + } + return false, nil +} + +func (r *Records) encode(pe packetEncoder) error { + if r.recordsType == unknownRecords { + if empty, err := r.setTypeFromFields(); err != nil || empty { + return err + } + } + + switch r.recordsType { + case legacyRecords: + if r.MsgSet == nil { + return nil + } + return r.MsgSet.encode(pe) + case defaultRecords: + if r.RecordBatch == nil { + return nil + } + return r.RecordBatch.encode(pe) + } + + return fmt.Errorf("unknown records type: %v", r.recordsType) +} + +func (r *Records) setTypeFromMagic(pd packetDecoder) error { + magic, err := magicValue(pd) + if err != nil { + return err + } + + r.recordsType = defaultRecords + if magic < 2 { + r.recordsType = legacyRecords + } + + return nil +} + +func (r *Records) decode(pd packetDecoder) error { + if r.recordsType == unknownRecords { + if err := r.setTypeFromMagic(pd); err != nil { + return err + } + } + + switch r.recordsType { + case legacyRecords: + r.MsgSet = &MessageSet{} + return r.MsgSet.decode(pd) + case defaultRecords: + r.RecordBatch = &RecordBatch{} + return r.RecordBatch.decode(pd) + } + return fmt.Errorf("unknown records type: %v", r.recordsType) +} + +func (r *Records) numRecords() (int, error) { + if r.recordsType == unknownRecords { + if empty, err := r.setTypeFromFields(); err != nil || empty { + return 0, err + } + } + + switch r.recordsType { + case legacyRecords: + if r.MsgSet == nil { + return 0, nil + } + return len(r.MsgSet.Messages), nil + case defaultRecords: + if r.RecordBatch == nil { + return 0, nil + } + return len(r.RecordBatch.Records), nil + } + return 0, fmt.Errorf("unknown records type: %v", r.recordsType) +} + +func (r *Records) isPartial() (bool, error) { + if r.recordsType == unknownRecords { + if empty, err := r.setTypeFromFields(); err != nil || empty { + return false, err + } + } + + switch r.recordsType { + case unknownRecords: + return false, nil + case legacyRecords: + if r.MsgSet == nil { + return false, nil + } + return r.MsgSet.PartialTrailingMessage, nil + case defaultRecords: + if r.RecordBatch == nil { + return false, nil + } + return r.RecordBatch.PartialTrailingRecord, nil + } + return false, fmt.Errorf("unknown records type: %v", r.recordsType) +} + +func (r *Records) isControl() (bool, error) { + if r.recordsType == unknownRecords { + if empty, err := r.setTypeFromFields(); err != nil || empty { + return false, err + } + } + + switch r.recordsType { + case legacyRecords: + return false, nil + case defaultRecords: + if r.RecordBatch == nil { + return false, nil + } + return r.RecordBatch.Control, nil + } + return false, fmt.Errorf("unknown records type: %v", r.recordsType) +} + +func (r *Records) isOverflow() (bool, error) { + if r.recordsType == unknownRecords { + if empty, err := r.setTypeFromFields(); err != nil || empty { + return false, err + } + } + + switch r.recordsType { + case unknownRecords: + return false, nil + case legacyRecords: + if r.MsgSet == nil { + return false, nil + } + return r.MsgSet.OverflowMessage, nil + case defaultRecords: + return false, nil + } + return false, fmt.Errorf("unknown records type: %v", r.recordsType) +} + +func magicValue(pd packetDecoder) (int8, error) { + dec, err := pd.peek(magicOffset, magicLength) + if err != nil { + return 0, err + } + + return dec.getInt8() +} diff --git a/vendor/github.com/Shopify/sarama/request.go b/vendor/github.com/Shopify/sarama/request.go new file mode 100644 index 00000000000..4d211a14f17 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/request.go @@ -0,0 +1,149 @@ +package sarama + +import ( + "encoding/binary" + "fmt" + "io" +) + +type protocolBody interface { + encoder + versionedDecoder + key() int16 + version() int16 + requiredVersion() KafkaVersion +} + +type request struct { + correlationID int32 + clientID string + body protocolBody +} + +func (r *request) encode(pe packetEncoder) (err error) { + pe.push(&lengthField{}) + pe.putInt16(r.body.key()) + pe.putInt16(r.body.version()) + pe.putInt32(r.correlationID) + err = pe.putString(r.clientID) + if err != nil { + return err + } + err = r.body.encode(pe) + if err != nil { + return err + } + return pe.pop() +} + +func (r *request) decode(pd packetDecoder) (err error) { + var key int16 + if key, err = pd.getInt16(); err != nil { + return err + } + var version int16 + if version, err = pd.getInt16(); err != nil { + return err + } + if r.correlationID, err = pd.getInt32(); err != nil { + return err + } + r.clientID, err = pd.getString() + + r.body = allocateBody(key, version) + if r.body == nil { + return PacketDecodingError{fmt.Sprintf("unknown request key (%d)", key)} + } + return r.body.decode(pd, version) +} + +func decodeRequest(r io.Reader) (req *request, bytesRead int, err error) { + lengthBytes := make([]byte, 4) + if _, err := io.ReadFull(r, lengthBytes); err != nil { + return nil, bytesRead, err + } + bytesRead += len(lengthBytes) + + length := int32(binary.BigEndian.Uint32(lengthBytes)) + if length <= 4 || length > MaxRequestSize { + return nil, bytesRead, PacketDecodingError{fmt.Sprintf("message of length %d too large or too small", length)} + } + + encodedReq := make([]byte, length) + if _, err := io.ReadFull(r, encodedReq); err != nil { + return nil, bytesRead, err + } + bytesRead += len(encodedReq) + + req = &request{} + if err := decode(encodedReq, req); err != nil { + return nil, bytesRead, err + } + return req, bytesRead, nil +} + +func allocateBody(key, version int16) protocolBody { + switch key { + case 0: + return &ProduceRequest{} + case 1: + return &FetchRequest{} + case 2: + return &OffsetRequest{Version: version} + case 3: + return &MetadataRequest{} + case 8: + return &OffsetCommitRequest{Version: version} + case 9: + return &OffsetFetchRequest{} + case 10: + return &FindCoordinatorRequest{} + case 11: + return &JoinGroupRequest{} + case 12: + return &HeartbeatRequest{} + case 13: + return &LeaveGroupRequest{} + case 14: + return &SyncGroupRequest{} + case 15: + return &DescribeGroupsRequest{} + case 16: + return &ListGroupsRequest{} + case 17: + return &SaslHandshakeRequest{} + case 18: + return &ApiVersionsRequest{} + case 19: + return &CreateTopicsRequest{} + case 20: + return &DeleteTopicsRequest{} + case 21: + return &DeleteRecordsRequest{} + case 22: + return &InitProducerIDRequest{} + case 24: + return &AddPartitionsToTxnRequest{} + case 25: + return &AddOffsetsToTxnRequest{} + case 26: + return &EndTxnRequest{} + case 28: + return &TxnOffsetCommitRequest{} + case 29: + return &DescribeAclsRequest{} + case 30: + return &CreateAclsRequest{} + case 31: + return &DeleteAclsRequest{} + case 32: + return &DescribeConfigsRequest{} + case 33: + return &AlterConfigsRequest{} + case 37: + return &CreatePartitionsRequest{} + case 42: + return &DeleteGroupsRequest{} + } + return nil +} diff --git a/vendor/github.com/Shopify/sarama/response_header.go b/vendor/github.com/Shopify/sarama/response_header.go new file mode 100644 index 00000000000..f3f4d27d6c4 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/response_header.go @@ -0,0 +1,21 @@ +package sarama + +import "fmt" + +type responseHeader struct { + length int32 + correlationID int32 +} + +func (r *responseHeader) decode(pd packetDecoder) (err error) { + r.length, err = pd.getInt32() + if err != nil { + return err + } + if r.length <= 4 || r.length > MaxResponseSize { + return PacketDecodingError{fmt.Sprintf("message of length %d too large or too small", r.length)} + } + + r.correlationID, err = pd.getInt32() + return err +} diff --git a/vendor/github.com/Shopify/sarama/sarama.go b/vendor/github.com/Shopify/sarama/sarama.go new file mode 100644 index 00000000000..7d5dc60d3e2 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sarama.go @@ -0,0 +1,99 @@ +/* +Package sarama is a pure Go client library for dealing with Apache Kafka (versions 0.8 and later). It includes a high-level +API for easily producing and consuming messages, and a low-level API for controlling bytes on the wire when the high-level +API is insufficient. Usage examples for the high-level APIs are provided inline with their full documentation. + +To produce messages, use either the AsyncProducer or the SyncProducer. The AsyncProducer accepts messages on a channel +and produces them asynchronously in the background as efficiently as possible; it is preferred in most cases. +The SyncProducer provides a method which will block until Kafka acknowledges the message as produced. This can be +useful but comes with two caveats: it will generally be less efficient, and the actual durability guarantees +depend on the configured value of `Producer.RequiredAcks`. There are configurations where a message acknowledged by the +SyncProducer can still sometimes be lost. + +To consume messages, use the Consumer. Note that Sarama's Consumer implementation does not currently support automatic +consumer-group rebalancing and offset tracking. For Zookeeper-based tracking (Kafka 0.8.2 and earlier), the +https://github.com/wvanbergen/kafka library builds on Sarama to add this support. For Kafka-based tracking (Kafka 0.9 +and later), the https://github.com/bsm/sarama-cluster library builds on Sarama to add this support. + +For lower-level needs, the Broker and Request/Response objects permit precise control over each connection +and message sent on the wire; the Client provides higher-level metadata management that is shared between +the producers and the consumer. The Request/Response objects and properties are mostly undocumented, as they line up +exactly with the protocol fields documented by Kafka at +https://cwiki.apache.org/confluence/display/KAFKA/A+Guide+To+The+Kafka+Protocol + +Metrics are exposed through https://github.com/rcrowley/go-metrics library in a local registry. + +Broker related metrics: + + +----------------------------------------------+------------+---------------------------------------------------------------+ + | Name | Type | Description | + +----------------------------------------------+------------+---------------------------------------------------------------+ + | incoming-byte-rate | meter | Bytes/second read off all brokers | + | incoming-byte-rate-for-broker- | meter | Bytes/second read off a given broker | + | outgoing-byte-rate | meter | Bytes/second written off all brokers | + | outgoing-byte-rate-for-broker- | meter | Bytes/second written off a given broker | + | request-rate | meter | Requests/second sent to all brokers | + | request-rate-for-broker- | meter | Requests/second sent to a given broker | + | request-size | histogram | Distribution of the request size in bytes for all brokers | + | request-size-for-broker- | histogram | Distribution of the request size in bytes for a given broker | + | request-latency-in-ms | histogram | Distribution of the request latency in ms for all brokers | + | request-latency-in-ms-for-broker- | histogram | Distribution of the request latency in ms for a given broker | + | response-rate | meter | Responses/second received from all brokers | + | response-rate-for-broker- | meter | Responses/second received from a given broker | + | response-size | histogram | Distribution of the response size in bytes for all brokers | + | response-size-for-broker- | histogram | Distribution of the response size in bytes for a given broker | + +----------------------------------------------+------------+---------------------------------------------------------------+ + +Note that we do not gather specific metrics for seed brokers but they are part of the "all brokers" metrics. + +Producer related metrics: + + +-------------------------------------------+------------+--------------------------------------------------------------------------------------+ + | Name | Type | Description | + +-------------------------------------------+------------+--------------------------------------------------------------------------------------+ + | batch-size | histogram | Distribution of the number of bytes sent per partition per request for all topics | + | batch-size-for-topic- | histogram | Distribution of the number of bytes sent per partition per request for a given topic | + | record-send-rate | meter | Records/second sent to all topics | + | record-send-rate-for-topic- | meter | Records/second sent to a given topic | + | records-per-request | histogram | Distribution of the number of records sent per request for all topics | + | records-per-request-for-topic- | histogram | Distribution of the number of records sent per request for a given topic | + | compression-ratio | histogram | Distribution of the compression ratio times 100 of record batches for all topics | + | compression-ratio-for-topic- | histogram | Distribution of the compression ratio times 100 of record batches for a given topic | + +-------------------------------------------+------------+--------------------------------------------------------------------------------------+ + +*/ +package sarama + +import ( + "io/ioutil" + "log" +) + +// Logger is the instance of a StdLogger interface that Sarama writes connection +// management events to. By default it is set to discard all log messages via ioutil.Discard, +// but you can set it to redirect wherever you want. +var Logger StdLogger = log.New(ioutil.Discard, "[Sarama] ", log.LstdFlags) + +// StdLogger is used to log error messages. +type StdLogger interface { + Print(v ...interface{}) + Printf(format string, v ...interface{}) + Println(v ...interface{}) +} + +// PanicHandler is called for recovering from panics spawned internally to the library (and thus +// not recoverable by the caller's goroutine). Defaults to nil, which means panics are not recovered. +var PanicHandler func(interface{}) + +// MaxRequestSize is the maximum size (in bytes) of any request that Sarama will attempt to send. Trying +// to send a request larger than this will result in an PacketEncodingError. The default of 100 MiB is aligned +// with Kafka's default `socket.request.max.bytes`, which is the largest request the broker will attempt +// to process. +var MaxRequestSize int32 = 100 * 1024 * 1024 + +// MaxResponseSize is the maximum size (in bytes) of any response that Sarama will attempt to parse. If +// a broker returns a response message larger than this value, Sarama will return a PacketDecodingError to +// protect the client from running out of memory. Please note that brokers do not have any natural limit on +// the size of responses they send. In particular, they can send arbitrarily large fetch responses to consumers +// (see https://issues.apache.org/jira/browse/KAFKA-2063). +var MaxResponseSize int32 = 100 * 1024 * 1024 diff --git a/vendor/github.com/Shopify/sarama/sasl_handshake_request.go b/vendor/github.com/Shopify/sarama/sasl_handshake_request.go new file mode 100644 index 00000000000..fbbc8947b2e --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sasl_handshake_request.go @@ -0,0 +1,33 @@ +package sarama + +type SaslHandshakeRequest struct { + Mechanism string +} + +func (r *SaslHandshakeRequest) encode(pe packetEncoder) error { + if err := pe.putString(r.Mechanism); err != nil { + return err + } + + return nil +} + +func (r *SaslHandshakeRequest) decode(pd packetDecoder, version int16) (err error) { + if r.Mechanism, err = pd.getString(); err != nil { + return err + } + + return nil +} + +func (r *SaslHandshakeRequest) key() int16 { + return 17 +} + +func (r *SaslHandshakeRequest) version() int16 { + return 0 +} + +func (r *SaslHandshakeRequest) requiredVersion() KafkaVersion { + return V0_10_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/sasl_handshake_response.go b/vendor/github.com/Shopify/sarama/sasl_handshake_response.go new file mode 100644 index 00000000000..ef290d4bc6d --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sasl_handshake_response.go @@ -0,0 +1,38 @@ +package sarama + +type SaslHandshakeResponse struct { + Err KError + EnabledMechanisms []string +} + +func (r *SaslHandshakeResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(r.Err)) + return pe.putStringArray(r.EnabledMechanisms) +} + +func (r *SaslHandshakeResponse) decode(pd packetDecoder, version int16) error { + kerr, err := pd.getInt16() + if err != nil { + return err + } + + r.Err = KError(kerr) + + if r.EnabledMechanisms, err = pd.getStringArray(); err != nil { + return err + } + + return nil +} + +func (r *SaslHandshakeResponse) key() int16 { + return 17 +} + +func (r *SaslHandshakeResponse) version() int16 { + return 0 +} + +func (r *SaslHandshakeResponse) requiredVersion() KafkaVersion { + return V0_10_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/sync_group_request.go b/vendor/github.com/Shopify/sarama/sync_group_request.go new file mode 100644 index 00000000000..fe207080e03 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sync_group_request.go @@ -0,0 +1,100 @@ +package sarama + +type SyncGroupRequest struct { + GroupId string + GenerationId int32 + MemberId string + GroupAssignments map[string][]byte +} + +func (r *SyncGroupRequest) encode(pe packetEncoder) error { + if err := pe.putString(r.GroupId); err != nil { + return err + } + + pe.putInt32(r.GenerationId) + + if err := pe.putString(r.MemberId); err != nil { + return err + } + + if err := pe.putArrayLength(len(r.GroupAssignments)); err != nil { + return err + } + for memberId, memberAssignment := range r.GroupAssignments { + if err := pe.putString(memberId); err != nil { + return err + } + if err := pe.putBytes(memberAssignment); err != nil { + return err + } + } + + return nil +} + +func (r *SyncGroupRequest) decode(pd packetDecoder, version int16) (err error) { + if r.GroupId, err = pd.getString(); err != nil { + return + } + if r.GenerationId, err = pd.getInt32(); err != nil { + return + } + if r.MemberId, err = pd.getString(); err != nil { + return + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + if n == 0 { + return nil + } + + r.GroupAssignments = make(map[string][]byte) + for i := 0; i < n; i++ { + memberId, err := pd.getString() + if err != nil { + return err + } + memberAssignment, err := pd.getBytes() + if err != nil { + return err + } + + r.GroupAssignments[memberId] = memberAssignment + } + + return nil +} + +func (r *SyncGroupRequest) key() int16 { + return 14 +} + +func (r *SyncGroupRequest) version() int16 { + return 0 +} + +func (r *SyncGroupRequest) requiredVersion() KafkaVersion { + return V0_9_0_0 +} + +func (r *SyncGroupRequest) AddGroupAssignment(memberId string, memberAssignment []byte) { + if r.GroupAssignments == nil { + r.GroupAssignments = make(map[string][]byte) + } + + r.GroupAssignments[memberId] = memberAssignment +} + +func (r *SyncGroupRequest) AddGroupAssignmentMember(memberId string, memberAssignment *ConsumerGroupMemberAssignment) error { + bin, err := encode(memberAssignment, nil) + if err != nil { + return err + } + + r.AddGroupAssignment(memberId, bin) + return nil +} diff --git a/vendor/github.com/Shopify/sarama/sync_group_response.go b/vendor/github.com/Shopify/sarama/sync_group_response.go new file mode 100644 index 00000000000..194b382b4ab --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sync_group_response.go @@ -0,0 +1,41 @@ +package sarama + +type SyncGroupResponse struct { + Err KError + MemberAssignment []byte +} + +func (r *SyncGroupResponse) GetMemberAssignment() (*ConsumerGroupMemberAssignment, error) { + assignment := new(ConsumerGroupMemberAssignment) + err := decode(r.MemberAssignment, assignment) + return assignment, err +} + +func (r *SyncGroupResponse) encode(pe packetEncoder) error { + pe.putInt16(int16(r.Err)) + return pe.putBytes(r.MemberAssignment) +} + +func (r *SyncGroupResponse) decode(pd packetDecoder, version int16) (err error) { + kerr, err := pd.getInt16() + if err != nil { + return err + } + + r.Err = KError(kerr) + + r.MemberAssignment, err = pd.getBytes() + return +} + +func (r *SyncGroupResponse) key() int16 { + return 14 +} + +func (r *SyncGroupResponse) version() int16 { + return 0 +} + +func (r *SyncGroupResponse) requiredVersion() KafkaVersion { + return V0_9_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/sync_producer.go b/vendor/github.com/Shopify/sarama/sync_producer.go new file mode 100644 index 00000000000..021c5a01032 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/sync_producer.go @@ -0,0 +1,149 @@ +package sarama + +import "sync" + +// SyncProducer publishes Kafka messages, blocking until they have been acknowledged. It routes messages to the correct +// broker, refreshing metadata as appropriate, and parses responses for errors. You must call Close() on a producer +// to avoid leaks, it may not be garbage-collected automatically when it passes out of scope. +// +// The SyncProducer comes with two caveats: it will generally be less efficient than the AsyncProducer, and the actual +// durability guarantee provided when a message is acknowledged depend on the configured value of `Producer.RequiredAcks`. +// There are configurations where a message acknowledged by the SyncProducer can still sometimes be lost. +// +// For implementation reasons, the SyncProducer requires `Producer.Return.Errors` and `Producer.Return.Successes` to +// be set to true in its configuration. +type SyncProducer interface { + + // SendMessage produces a given message, and returns only when it either has + // succeeded or failed to produce. It will return the partition and the offset + // of the produced message, or an error if the message failed to produce. + SendMessage(msg *ProducerMessage) (partition int32, offset int64, err error) + + // SendMessages produces a given set of messages, and returns only when all + // messages in the set have either succeeded or failed. Note that messages + // can succeed and fail individually; if some succeed and some fail, + // SendMessages will return an error. + SendMessages(msgs []*ProducerMessage) error + + // Close shuts down the producer and waits for any buffered messages to be + // flushed. You must call this function before a producer object passes out of + // scope, as it may otherwise leak memory. You must call this before calling + // Close on the underlying client. + Close() error +} + +type syncProducer struct { + producer *asyncProducer + wg sync.WaitGroup +} + +// NewSyncProducer creates a new SyncProducer using the given broker addresses and configuration. +func NewSyncProducer(addrs []string, config *Config) (SyncProducer, error) { + if config == nil { + config = NewConfig() + config.Producer.Return.Successes = true + } + + if err := verifyProducerConfig(config); err != nil { + return nil, err + } + + p, err := NewAsyncProducer(addrs, config) + if err != nil { + return nil, err + } + return newSyncProducerFromAsyncProducer(p.(*asyncProducer)), nil +} + +// NewSyncProducerFromClient creates a new SyncProducer using the given client. It is still +// necessary to call Close() on the underlying client when shutting down this producer. +func NewSyncProducerFromClient(client Client) (SyncProducer, error) { + if err := verifyProducerConfig(client.Config()); err != nil { + return nil, err + } + + p, err := NewAsyncProducerFromClient(client) + if err != nil { + return nil, err + } + return newSyncProducerFromAsyncProducer(p.(*asyncProducer)), nil +} + +func newSyncProducerFromAsyncProducer(p *asyncProducer) *syncProducer { + sp := &syncProducer{producer: p} + + sp.wg.Add(2) + go withRecover(sp.handleSuccesses) + go withRecover(sp.handleErrors) + + return sp +} + +func verifyProducerConfig(config *Config) error { + if !config.Producer.Return.Errors { + return ConfigurationError("Producer.Return.Errors must be true to be used in a SyncProducer") + } + if !config.Producer.Return.Successes { + return ConfigurationError("Producer.Return.Successes must be true to be used in a SyncProducer") + } + return nil +} + +func (sp *syncProducer) SendMessage(msg *ProducerMessage) (partition int32, offset int64, err error) { + expectation := make(chan *ProducerError, 1) + msg.expectation = expectation + sp.producer.Input() <- msg + + if err := <-expectation; err != nil { + return -1, -1, err.Err + } + + return msg.Partition, msg.Offset, nil +} + +func (sp *syncProducer) SendMessages(msgs []*ProducerMessage) error { + expectations := make(chan chan *ProducerError, len(msgs)) + go func() { + for _, msg := range msgs { + expectation := make(chan *ProducerError, 1) + msg.expectation = expectation + sp.producer.Input() <- msg + expectations <- expectation + } + close(expectations) + }() + + var errors ProducerErrors + for expectation := range expectations { + if err := <-expectation; err != nil { + errors = append(errors, err) + } + } + + if len(errors) > 0 { + return errors + } + return nil +} + +func (sp *syncProducer) handleSuccesses() { + defer sp.wg.Done() + for msg := range sp.producer.Successes() { + expectation := msg.expectation + expectation <- nil + } +} + +func (sp *syncProducer) handleErrors() { + defer sp.wg.Done() + for err := range sp.producer.Errors() { + expectation := err.Msg.expectation + expectation <- err + } +} + +func (sp *syncProducer) Close() error { + sp.producer.AsyncClose() + sp.wg.Wait() + return nil +} diff --git a/vendor/github.com/Shopify/sarama/timestamp.go b/vendor/github.com/Shopify/sarama/timestamp.go new file mode 100644 index 00000000000..372278d0bfa --- /dev/null +++ b/vendor/github.com/Shopify/sarama/timestamp.go @@ -0,0 +1,40 @@ +package sarama + +import ( + "fmt" + "time" +) + +type Timestamp struct { + *time.Time +} + +func (t Timestamp) encode(pe packetEncoder) error { + timestamp := int64(-1) + + if !t.Before(time.Unix(0, 0)) { + timestamp = t.UnixNano() / int64(time.Millisecond) + } else if !t.IsZero() { + return PacketEncodingError{fmt.Sprintf("invalid timestamp (%v)", t)} + } + + pe.putInt64(timestamp) + return nil +} + +func (t Timestamp) decode(pd packetDecoder) error { + millis, err := pd.getInt64() + if err != nil { + return err + } + + // negative timestamps are invalid, in these cases we should return + // a zero time + timestamp := time.Time{} + if millis >= 0 { + timestamp = time.Unix(millis/1000, (millis%1000)*int64(time.Millisecond)) + } + + *t.Time = timestamp + return nil +} diff --git a/vendor/github.com/Shopify/sarama/txn_offset_commit_request.go b/vendor/github.com/Shopify/sarama/txn_offset_commit_request.go new file mode 100644 index 00000000000..71e95b814cb --- /dev/null +++ b/vendor/github.com/Shopify/sarama/txn_offset_commit_request.go @@ -0,0 +1,126 @@ +package sarama + +type TxnOffsetCommitRequest struct { + TransactionalID string + GroupID string + ProducerID int64 + ProducerEpoch int16 + Topics map[string][]*PartitionOffsetMetadata +} + +func (t *TxnOffsetCommitRequest) encode(pe packetEncoder) error { + if err := pe.putString(t.TransactionalID); err != nil { + return err + } + if err := pe.putString(t.GroupID); err != nil { + return err + } + pe.putInt64(t.ProducerID) + pe.putInt16(t.ProducerEpoch) + + if err := pe.putArrayLength(len(t.Topics)); err != nil { + return err + } + for topic, partitions := range t.Topics { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putArrayLength(len(partitions)); err != nil { + return err + } + for _, partition := range partitions { + if err := partition.encode(pe); err != nil { + return err + } + } + } + + return nil +} + +func (t *TxnOffsetCommitRequest) decode(pd packetDecoder, version int16) (err error) { + if t.TransactionalID, err = pd.getString(); err != nil { + return err + } + if t.GroupID, err = pd.getString(); err != nil { + return err + } + if t.ProducerID, err = pd.getInt64(); err != nil { + return err + } + if t.ProducerEpoch, err = pd.getInt16(); err != nil { + return err + } + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + t.Topics = make(map[string][]*PartitionOffsetMetadata) + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + + m, err := pd.getArrayLength() + if err != nil { + return err + } + + t.Topics[topic] = make([]*PartitionOffsetMetadata, m) + + for j := 0; j < m; j++ { + partitionOffsetMetadata := new(PartitionOffsetMetadata) + if err := partitionOffsetMetadata.decode(pd, version); err != nil { + return err + } + t.Topics[topic][j] = partitionOffsetMetadata + } + } + + return nil +} + +func (a *TxnOffsetCommitRequest) key() int16 { + return 28 +} + +func (a *TxnOffsetCommitRequest) version() int16 { + return 0 +} + +func (a *TxnOffsetCommitRequest) requiredVersion() KafkaVersion { + return V0_11_0_0 +} + +type PartitionOffsetMetadata struct { + Partition int32 + Offset int64 + Metadata *string +} + +func (p *PartitionOffsetMetadata) encode(pe packetEncoder) error { + pe.putInt32(p.Partition) + pe.putInt64(p.Offset) + if err := pe.putNullableString(p.Metadata); err != nil { + return err + } + + return nil +} + +func (p *PartitionOffsetMetadata) decode(pd packetDecoder, version int16) (err error) { + if p.Partition, err = pd.getInt32(); err != nil { + return err + } + if p.Offset, err = pd.getInt64(); err != nil { + return err + } + if p.Metadata, err = pd.getNullableString(); err != nil { + return err + } + + return nil +} diff --git a/vendor/github.com/Shopify/sarama/txn_offset_commit_response.go b/vendor/github.com/Shopify/sarama/txn_offset_commit_response.go new file mode 100644 index 00000000000..6c980f4066f --- /dev/null +++ b/vendor/github.com/Shopify/sarama/txn_offset_commit_response.go @@ -0,0 +1,83 @@ +package sarama + +import ( + "time" +) + +type TxnOffsetCommitResponse struct { + ThrottleTime time.Duration + Topics map[string][]*PartitionError +} + +func (t *TxnOffsetCommitResponse) encode(pe packetEncoder) error { + pe.putInt32(int32(t.ThrottleTime / time.Millisecond)) + if err := pe.putArrayLength(len(t.Topics)); err != nil { + return err + } + + for topic, e := range t.Topics { + if err := pe.putString(topic); err != nil { + return err + } + if err := pe.putArrayLength(len(e)); err != nil { + return err + } + for _, partitionError := range e { + if err := partitionError.encode(pe); err != nil { + return err + } + } + } + + return nil +} + +func (t *TxnOffsetCommitResponse) decode(pd packetDecoder, version int16) (err error) { + throttleTime, err := pd.getInt32() + if err != nil { + return err + } + t.ThrottleTime = time.Duration(throttleTime) * time.Millisecond + + n, err := pd.getArrayLength() + if err != nil { + return err + } + + t.Topics = make(map[string][]*PartitionError) + + for i := 0; i < n; i++ { + topic, err := pd.getString() + if err != nil { + return err + } + + m, err := pd.getArrayLength() + if err != nil { + return err + } + + t.Topics[topic] = make([]*PartitionError, m) + + for j := 0; j < m; j++ { + t.Topics[topic][j] = new(PartitionError) + if err := t.Topics[topic][j].decode(pd, version); err != nil { + return err + } + } + } + + return nil +} + +func (a *TxnOffsetCommitResponse) key() int16 { + return 28 +} + +func (a *TxnOffsetCommitResponse) version() int16 { + return 0 +} + +func (a *TxnOffsetCommitResponse) requiredVersion() KafkaVersion { + return V0_11_0_0 +} diff --git a/vendor/github.com/Shopify/sarama/utils.go b/vendor/github.com/Shopify/sarama/utils.go new file mode 100644 index 00000000000..1bb00d761a4 --- /dev/null +++ b/vendor/github.com/Shopify/sarama/utils.go @@ -0,0 +1,214 @@ +package sarama + +import ( + "bufio" + "fmt" + "net" + "regexp" +) + +type none struct{} + +// make []int32 sortable so we can sort partition numbers +type int32Slice []int32 + +func (slice int32Slice) Len() int { + return len(slice) +} + +func (slice int32Slice) Less(i, j int) bool { + return slice[i] < slice[j] +} + +func (slice int32Slice) Swap(i, j int) { + slice[i], slice[j] = slice[j], slice[i] +} + +func dupInt32Slice(input []int32) []int32 { + ret := make([]int32, 0, len(input)) + for _, val := range input { + ret = append(ret, val) + } + return ret +} + +func withRecover(fn func()) { + defer func() { + handler := PanicHandler + if handler != nil { + if err := recover(); err != nil { + handler(err) + } + } + }() + + fn() +} + +func safeAsyncClose(b *Broker) { + tmp := b // local var prevents clobbering in goroutine + go withRecover(func() { + if connected, _ := tmp.Connected(); connected { + if err := tmp.Close(); err != nil { + Logger.Println("Error closing broker", tmp.ID(), ":", err) + } + } + }) +} + +// Encoder is a simple interface for any type that can be encoded as an array of bytes +// in order to be sent as the key or value of a Kafka message. Length() is provided as an +// optimization, and must return the same as len() on the result of Encode(). +type Encoder interface { + Encode() ([]byte, error) + Length() int +} + +// make strings and byte slices encodable for convenience so they can be used as keys +// and/or values in kafka messages + +// StringEncoder implements the Encoder interface for Go strings so that they can be used +// as the Key or Value in a ProducerMessage. +type StringEncoder string + +func (s StringEncoder) Encode() ([]byte, error) { + return []byte(s), nil +} + +func (s StringEncoder) Length() int { + return len(s) +} + +// ByteEncoder implements the Encoder interface for Go byte slices so that they can be used +// as the Key or Value in a ProducerMessage. +type ByteEncoder []byte + +func (b ByteEncoder) Encode() ([]byte, error) { + return b, nil +} + +func (b ByteEncoder) Length() int { + return len(b) +} + +// bufConn wraps a net.Conn with a buffer for reads to reduce the number of +// reads that trigger syscalls. +type bufConn struct { + net.Conn + buf *bufio.Reader +} + +func newBufConn(conn net.Conn) *bufConn { + return &bufConn{ + Conn: conn, + buf: bufio.NewReader(conn), + } +} + +func (bc *bufConn) Read(b []byte) (n int, err error) { + return bc.buf.Read(b) +} + +// KafkaVersion instances represent versions of the upstream Kafka broker. +type KafkaVersion struct { + // it's a struct rather than just typing the array directly to make it opaque and stop people + // generating their own arbitrary versions + version [4]uint +} + +func newKafkaVersion(major, minor, veryMinor, patch uint) KafkaVersion { + return KafkaVersion{ + version: [4]uint{major, minor, veryMinor, patch}, + } +} + +// IsAtLeast return true if and only if the version it is called on is +// greater than or equal to the version passed in: +// V1.IsAtLeast(V2) // false +// V2.IsAtLeast(V1) // true +func (v KafkaVersion) IsAtLeast(other KafkaVersion) bool { + for i := range v.version { + if v.version[i] > other.version[i] { + return true + } else if v.version[i] < other.version[i] { + return false + } + } + return true +} + +// Effective constants defining the supported kafka versions. +var ( + V0_8_2_0 = newKafkaVersion(0, 8, 2, 0) + V0_8_2_1 = newKafkaVersion(0, 8, 2, 1) + V0_8_2_2 = newKafkaVersion(0, 8, 2, 2) + V0_9_0_0 = newKafkaVersion(0, 9, 0, 0) + V0_9_0_1 = newKafkaVersion(0, 9, 0, 1) + V0_10_0_0 = newKafkaVersion(0, 10, 0, 0) + V0_10_0_1 = newKafkaVersion(0, 10, 0, 1) + V0_10_1_0 = newKafkaVersion(0, 10, 1, 0) + V0_10_1_1 = newKafkaVersion(0, 10, 1, 1) + V0_10_2_0 = newKafkaVersion(0, 10, 2, 0) + V0_10_2_1 = newKafkaVersion(0, 10, 2, 1) + V0_11_0_0 = newKafkaVersion(0, 11, 0, 0) + V0_11_0_1 = newKafkaVersion(0, 11, 0, 1) + V0_11_0_2 = newKafkaVersion(0, 11, 0, 2) + V1_0_0_0 = newKafkaVersion(1, 0, 0, 0) + V1_1_0_0 = newKafkaVersion(1, 1, 0, 0) + V2_0_0_0 = newKafkaVersion(2, 0, 0, 0) + + SupportedVersions = []KafkaVersion{ + V0_8_2_0, + V0_8_2_1, + V0_8_2_2, + V0_9_0_0, + V0_9_0_1, + V0_10_0_0, + V0_10_0_1, + V0_10_1_0, + V0_10_1_1, + V0_10_2_0, + V0_10_2_1, + V0_11_0_0, + V0_11_0_1, + V0_11_0_2, + V1_0_0_0, + V1_1_0_0, + V2_0_0_0, + } + MinVersion = V0_8_2_0 + MaxVersion = V2_0_0_0 +) + +func ParseKafkaVersion(s string) (KafkaVersion, error) { + if len(s) < 5 { + return MinVersion, fmt.Errorf("invalid version `%s`", s) + } + var major, minor, veryMinor, patch uint + var err error + if s[0] == '0' { + err = scanKafkaVersion(s, `^0\.\d+\.\d+\.\d+$`, "0.%d.%d.%d", [3]*uint{&minor, &veryMinor, &patch}) + } else { + err = scanKafkaVersion(s, `^\d+\.\d+\.\d+$`, "%d.%d.%d", [3]*uint{&major, &minor, &veryMinor}) + } + if err != nil { + return MinVersion, err + } + return newKafkaVersion(major, minor, veryMinor, patch), nil +} + +func scanKafkaVersion(s string, pattern string, format string, v [3]*uint) error { + if !regexp.MustCompile(pattern).MatchString(s) { + return fmt.Errorf("invalid version `%s`", s) + } + _, err := fmt.Sscanf(s, format, v[0], v[1], v[2]) + return err +} + +func (v KafkaVersion) String() string { + if v.version[0] == 0 { + return fmt.Sprintf("0.%d.%d.%d", v.version[1], v.version[2], v.version[3]) + } else { + return fmt.Sprintf("%d.%d.%d", v.version[0], v.version[1], v.version[2]) + } +} diff --git a/vendor/github.com/eapache/go-resiliency/LICENSE b/vendor/github.com/eapache/go-resiliency/LICENSE new file mode 100644 index 00000000000..698a3f51397 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/LICENSE @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2014 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/vendor/github.com/eapache/go-resiliency/breaker/breaker.go b/vendor/github.com/eapache/go-resiliency/breaker/breaker.go new file mode 100644 index 00000000000..f88ca7248b0 --- /dev/null +++ b/vendor/github.com/eapache/go-resiliency/breaker/breaker.go @@ -0,0 +1,161 @@ +// Package breaker implements the circuit-breaker resiliency pattern for Go. +package breaker + +import ( + "errors" + "sync" + "sync/atomic" + "time" +) + +// ErrBreakerOpen is the error returned from Run() when the function is not executed +// because the breaker is currently open. +var ErrBreakerOpen = errors.New("circuit breaker is open") + +const ( + closed uint32 = iota + open + halfOpen +) + +// Breaker implements the circuit-breaker resiliency pattern +type Breaker struct { + errorThreshold, successThreshold int + timeout time.Duration + + lock sync.Mutex + state uint32 + errors, successes int + lastError time.Time +} + +// New constructs a new circuit-breaker that starts closed. +// From closed, the breaker opens if "errorThreshold" errors are seen +// without an error-free period of at least "timeout". From open, the +// breaker half-closes after "timeout". From half-open, the breaker closes +// after "successThreshold" consecutive successes, or opens on a single error. +func New(errorThreshold, successThreshold int, timeout time.Duration) *Breaker { + return &Breaker{ + errorThreshold: errorThreshold, + successThreshold: successThreshold, + timeout: timeout, + } +} + +// Run will either return ErrBreakerOpen immediately if the circuit-breaker is +// already open, or it will run the given function and pass along its return +// value. It is safe to call Run concurrently on the same Breaker. +func (b *Breaker) Run(work func() error) error { + state := atomic.LoadUint32(&b.state) + + if state == open { + return ErrBreakerOpen + } + + return b.doWork(state, work) +} + +// Go will either return ErrBreakerOpen immediately if the circuit-breaker is +// already open, or it will run the given function in a separate goroutine. +// If the function is run, Go will return nil immediately, and will *not* return +// the return value of the function. It is safe to call Go concurrently on the +// same Breaker. +func (b *Breaker) Go(work func() error) error { + state := atomic.LoadUint32(&b.state) + + if state == open { + return ErrBreakerOpen + } + + // errcheck complains about ignoring the error return value, but + // that's on purpose; if you want an error from a goroutine you have to + // get it over a channel or something + go b.doWork(state, work) + + return nil +} + +func (b *Breaker) doWork(state uint32, work func() error) error { + var panicValue interface{} + + result := func() error { + defer func() { + panicValue = recover() + }() + return work() + }() + + if result == nil && panicValue == nil && state == closed { + // short-circuit the normal, success path without contending + // on the lock + return nil + } + + // oh well, I guess we have to contend on the lock + b.processResult(result, panicValue) + + if panicValue != nil { + // as close as Go lets us come to a "rethrow" although unfortunately + // we lose the original panicing location + panic(panicValue) + } + + return result +} + +func (b *Breaker) processResult(result error, panicValue interface{}) { + b.lock.Lock() + defer b.lock.Unlock() + + if result == nil && panicValue == nil { + if b.state == halfOpen { + b.successes++ + if b.successes == b.successThreshold { + b.closeBreaker() + } + } + } else { + if b.errors > 0 { + expiry := b.lastError.Add(b.timeout) + if time.Now().After(expiry) { + b.errors = 0 + } + } + + switch b.state { + case closed: + b.errors++ + if b.errors == b.errorThreshold { + b.openBreaker() + } else { + b.lastError = time.Now() + } + case halfOpen: + b.openBreaker() + } + } +} + +func (b *Breaker) openBreaker() { + b.changeState(open) + go b.timer() +} + +func (b *Breaker) closeBreaker() { + b.changeState(closed) +} + +func (b *Breaker) timer() { + time.Sleep(b.timeout) + + b.lock.Lock() + defer b.lock.Unlock() + + b.changeState(halfOpen) +} + +func (b *Breaker) changeState(newState uint32) { + b.errors = 0 + b.successes = 0 + atomic.StoreUint32(&b.state, newState) +} diff --git a/vendor/github.com/eapache/go-xerial-snappy/LICENSE b/vendor/github.com/eapache/go-xerial-snappy/LICENSE new file mode 100644 index 00000000000..5bf3688d9e4 --- /dev/null +++ b/vendor/github.com/eapache/go-xerial-snappy/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2016 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/vendor/github.com/eapache/go-xerial-snappy/fuzz.go b/vendor/github.com/eapache/go-xerial-snappy/fuzz.go new file mode 100644 index 00000000000..6a46f4784e1 --- /dev/null +++ b/vendor/github.com/eapache/go-xerial-snappy/fuzz.go @@ -0,0 +1,16 @@ +// +build gofuzz + +package snappy + +func Fuzz(data []byte) int { + decode, err := Decode(data) + if decode == nil && err == nil { + panic("nil error with nil result") + } + + if err != nil { + return 0 + } + + return 1 +} diff --git a/vendor/github.com/eapache/go-xerial-snappy/snappy.go b/vendor/github.com/eapache/go-xerial-snappy/snappy.go new file mode 100644 index 00000000000..ea8f7afeb33 --- /dev/null +++ b/vendor/github.com/eapache/go-xerial-snappy/snappy.go @@ -0,0 +1,131 @@ +package snappy + +import ( + "bytes" + "encoding/binary" + "errors" + + master "github.com/golang/snappy" +) + +const ( + sizeOffset = 16 + sizeBytes = 4 +) + +var ( + xerialHeader = []byte{130, 83, 78, 65, 80, 80, 89, 0} + + // This is xerial version 1 and minimally compatible with version 1 + xerialVersionInfo = []byte{0, 0, 0, 1, 0, 0, 0, 1} + + // ErrMalformed is returned by the decoder when the xerial framing + // is malformed + ErrMalformed = errors.New("malformed xerial framing") +) + +func min(x, y int) int { + if x < y { + return x + } + return y +} + +// Encode encodes data as snappy with no framing header. +func Encode(src []byte) []byte { + return master.Encode(nil, src) +} + +// EncodeStream *appends* to the specified 'dst' the compressed +// 'src' in xerial framing format. If 'dst' does not have enough +// capacity, then a new slice will be allocated. If 'dst' has +// non-zero length, then if *must* have been built using this function. +func EncodeStream(dst, src []byte) []byte { + if len(dst) == 0 { + dst = append(dst, xerialHeader...) + dst = append(dst, xerialVersionInfo...) + } + + // Snappy encode in blocks of maximum 32KB + var ( + max = len(src) + blockSize = 32 * 1024 + pos = 0 + chunk []byte + ) + + for pos < max { + newPos := min(pos + blockSize, max) + chunk = master.Encode(chunk[:cap(chunk)], src[pos:newPos]) + + // First encode the compressed size (big-endian) + // Put* panics if the buffer is too small, so pad 4 bytes first + origLen := len(dst) + dst = append(dst, dst[0:4]...) + binary.BigEndian.PutUint32(dst[origLen:], uint32(len(chunk))) + + // And now the compressed data + dst = append(dst, chunk...) + pos = newPos + } + return dst +} + +// Decode decodes snappy data whether it is traditional unframed +// or includes the xerial framing format. +func Decode(src []byte) ([]byte, error) { + return DecodeInto(nil, src) +} + +// DecodeInto decodes snappy data whether it is traditional unframed +// or includes the xerial framing format into the specified `dst`. +// It is assumed that the entirety of `dst` including all capacity is available +// for use by this function. If `dst` is nil *or* insufficiently large to hold +// the decoded `src`, new space will be allocated. +func DecodeInto(dst, src []byte) ([]byte, error) { + var max = len(src) + if max < len(xerialHeader) { + return nil, ErrMalformed + } + + if !bytes.Equal(src[:8], xerialHeader) { + return master.Decode(dst[:cap(dst)], src) + } + + if max < sizeOffset+sizeBytes { + return nil, ErrMalformed + } + + if dst == nil { + dst = make([]byte, 0, len(src)) + } + + dst = dst[:0] + var ( + pos = sizeOffset + chunk []byte + err error + ) + + for pos+sizeBytes <= max { + size := int(binary.BigEndian.Uint32(src[pos : pos+sizeBytes])) + pos += sizeBytes + + nextPos := pos + size + // On architectures where int is 32-bytes wide size + pos could + // overflow so we need to check the low bound as well as the + // high + if nextPos < pos || nextPos > max { + return nil, ErrMalformed + } + + chunk, err = master.Decode(chunk[:cap(chunk)], src[pos:nextPos]) + + if err != nil { + return nil, err + } + pos = nextPos + dst = append(dst, chunk...) + } + return dst, nil +} diff --git a/vendor/github.com/eapache/queue/LICENSE b/vendor/github.com/eapache/queue/LICENSE new file mode 100644 index 00000000000..d5f36dbcaaf --- /dev/null +++ b/vendor/github.com/eapache/queue/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2014 Evan Huus + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file diff --git a/vendor/github.com/eapache/queue/queue.go b/vendor/github.com/eapache/queue/queue.go new file mode 100644 index 00000000000..71d1acdf27b --- /dev/null +++ b/vendor/github.com/eapache/queue/queue.go @@ -0,0 +1,102 @@ +/* +Package queue provides a fast, ring-buffer queue based on the version suggested by Dariusz Górecki. +Using this instead of other, simpler, queue implementations (slice+append or linked list) provides +substantial memory and time benefits, and fewer GC pauses. + +The queue implemented here is as fast as it is for an additional reason: it is *not* thread-safe. +*/ +package queue + +// minQueueLen is smallest capacity that queue may have. +// Must be power of 2 for bitwise modulus: x % n == x & (n - 1). +const minQueueLen = 16 + +// Queue represents a single instance of the queue data structure. +type Queue struct { + buf []interface{} + head, tail, count int +} + +// New constructs and returns a new Queue. +func New() *Queue { + return &Queue{ + buf: make([]interface{}, minQueueLen), + } +} + +// Length returns the number of elements currently stored in the queue. +func (q *Queue) Length() int { + return q.count +} + +// resizes the queue to fit exactly twice its current contents +// this can result in shrinking if the queue is less than half-full +func (q *Queue) resize() { + newBuf := make([]interface{}, q.count<<1) + + if q.tail > q.head { + copy(newBuf, q.buf[q.head:q.tail]) + } else { + n := copy(newBuf, q.buf[q.head:]) + copy(newBuf[n:], q.buf[:q.tail]) + } + + q.head = 0 + q.tail = q.count + q.buf = newBuf +} + +// Add puts an element on the end of the queue. +func (q *Queue) Add(elem interface{}) { + if q.count == len(q.buf) { + q.resize() + } + + q.buf[q.tail] = elem + // bitwise modulus + q.tail = (q.tail + 1) & (len(q.buf) - 1) + q.count++ +} + +// Peek returns the element at the head of the queue. This call panics +// if the queue is empty. +func (q *Queue) Peek() interface{} { + if q.count <= 0 { + panic("queue: Peek() called on empty queue") + } + return q.buf[q.head] +} + +// Get returns the element at index i in the queue. If the index is +// invalid, the call will panic. This method accepts both positive and +// negative index values. Index 0 refers to the first element, and +// index -1 refers to the last. +func (q *Queue) Get(i int) interface{} { + // If indexing backwards, convert to positive index. + if i < 0 { + i += q.count + } + if i < 0 || i >= q.count { + panic("queue: Get() called with index out of range") + } + // bitwise modulus + return q.buf[(q.head+i)&(len(q.buf)-1)] +} + +// Remove removes and returns the element from the front of the queue. If the +// queue is empty, the call will panic. +func (q *Queue) Remove() interface{} { + if q.count <= 0 { + panic("queue: Remove() called on empty queue") + } + ret := q.buf[q.head] + q.buf[q.head] = nil + // bitwise modulus + q.head = (q.head + 1) & (len(q.buf) - 1) + q.count-- + // Resize down if buffer 1/4 full. + if len(q.buf) > minQueueLen && (q.count<<2) == len(q.buf) { + q.resize() + } + return ret +} diff --git a/vendor/github.com/golang/snappy/AUTHORS b/vendor/github.com/golang/snappy/AUTHORS new file mode 100644 index 00000000000..bcfa19520af --- /dev/null +++ b/vendor/github.com/golang/snappy/AUTHORS @@ -0,0 +1,15 @@ +# This is the official list of Snappy-Go authors for copyright purposes. +# This file is distinct from the CONTRIBUTORS files. +# See the latter for an explanation. + +# Names should be added to this file as +# Name or Organization +# The email address is not required for organizations. + +# Please keep the list sorted. + +Damian Gryski +Google Inc. +Jan Mercl <0xjnml@gmail.com> +Rodolfo Carvalho +Sebastien Binet diff --git a/vendor/github.com/golang/snappy/CONTRIBUTORS b/vendor/github.com/golang/snappy/CONTRIBUTORS new file mode 100644 index 00000000000..931ae31606f --- /dev/null +++ b/vendor/github.com/golang/snappy/CONTRIBUTORS @@ -0,0 +1,37 @@ +# This is the official list of people who can contribute +# (and typically have contributed) code to the Snappy-Go repository. +# The AUTHORS file lists the copyright holders; this file +# lists people. For example, Google employees are listed here +# but not in AUTHORS, because Google holds the copyright. +# +# The submission process automatically checks to make sure +# that people submitting code are listed in this file (by email address). +# +# Names should be added to this file only after verifying that +# the individual or the individual's organization has agreed to +# the appropriate Contributor License Agreement, found here: +# +# http://code.google.com/legal/individual-cla-v1.0.html +# http://code.google.com/legal/corporate-cla-v1.0.html +# +# The agreement for individuals can be filled out on the web. +# +# When adding J Random Contributor's name to this file, +# either J's name or J's organization's name should be +# added to the AUTHORS file, depending on whether the +# individual or corporate CLA was used. + +# Names should be added to this file like so: +# Name + +# Please keep the list sorted. + +Damian Gryski +Jan Mercl <0xjnml@gmail.com> +Kai Backman +Marc-Antoine Ruel +Nigel Tao +Rob Pike +Rodolfo Carvalho +Russ Cox +Sebastien Binet diff --git a/vendor/github.com/golang/snappy/LICENSE b/vendor/github.com/golang/snappy/LICENSE new file mode 100644 index 00000000000..6050c10f4c8 --- /dev/null +++ b/vendor/github.com/golang/snappy/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2011 The Snappy-Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/golang/snappy/decode.go b/vendor/github.com/golang/snappy/decode.go new file mode 100644 index 00000000000..72efb0353dd --- /dev/null +++ b/vendor/github.com/golang/snappy/decode.go @@ -0,0 +1,237 @@ +// Copyright 2011 The Snappy-Go 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 snappy + +import ( + "encoding/binary" + "errors" + "io" +) + +var ( + // ErrCorrupt reports that the input is invalid. + ErrCorrupt = errors.New("snappy: corrupt input") + // ErrTooLarge reports that the uncompressed length is too large. + ErrTooLarge = errors.New("snappy: decoded block is too large") + // ErrUnsupported reports that the input isn't supported. + ErrUnsupported = errors.New("snappy: unsupported input") + + errUnsupportedLiteralLength = errors.New("snappy: unsupported literal length") +) + +// DecodedLen returns the length of the decoded block. +func DecodedLen(src []byte) (int, error) { + v, _, err := decodedLen(src) + return v, err +} + +// decodedLen returns the length of the decoded block and the number of bytes +// that the length header occupied. +func decodedLen(src []byte) (blockLen, headerLen int, err error) { + v, n := binary.Uvarint(src) + if n <= 0 || v > 0xffffffff { + return 0, 0, ErrCorrupt + } + + const wordSize = 32 << (^uint(0) >> 32 & 1) + if wordSize == 32 && v > 0x7fffffff { + return 0, 0, ErrTooLarge + } + return int(v), n, nil +} + +const ( + decodeErrCodeCorrupt = 1 + decodeErrCodeUnsupportedLiteralLength = 2 +) + +// Decode returns the decoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire decoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +func Decode(dst, src []byte) ([]byte, error) { + dLen, s, err := decodedLen(src) + if err != nil { + return nil, err + } + if dLen <= len(dst) { + dst = dst[:dLen] + } else { + dst = make([]byte, dLen) + } + switch decode(dst, src[s:]) { + case 0: + return dst, nil + case decodeErrCodeUnsupportedLiteralLength: + return nil, errUnsupportedLiteralLength + } + return nil, ErrCorrupt +} + +// NewReader returns a new Reader that decompresses from r, using the framing +// format described at +// https://github.com/google/snappy/blob/master/framing_format.txt +func NewReader(r io.Reader) *Reader { + return &Reader{ + r: r, + decoded: make([]byte, maxBlockSize), + buf: make([]byte, maxEncodedLenOfMaxBlockSize+checksumSize), + } +} + +// Reader is an io.Reader that can read Snappy-compressed bytes. +type Reader struct { + r io.Reader + err error + decoded []byte + buf []byte + // decoded[i:j] contains decoded bytes that have not yet been passed on. + i, j int + readHeader bool +} + +// Reset discards any buffered data, resets all state, and switches the Snappy +// reader to read from r. This permits reusing a Reader rather than allocating +// a new one. +func (r *Reader) Reset(reader io.Reader) { + r.r = reader + r.err = nil + r.i = 0 + r.j = 0 + r.readHeader = false +} + +func (r *Reader) readFull(p []byte, allowEOF bool) (ok bool) { + if _, r.err = io.ReadFull(r.r, p); r.err != nil { + if r.err == io.ErrUnexpectedEOF || (r.err == io.EOF && !allowEOF) { + r.err = ErrCorrupt + } + return false + } + return true +} + +// Read satisfies the io.Reader interface. +func (r *Reader) Read(p []byte) (int, error) { + if r.err != nil { + return 0, r.err + } + for { + if r.i < r.j { + n := copy(p, r.decoded[r.i:r.j]) + r.i += n + return n, nil + } + if !r.readFull(r.buf[:4], true) { + return 0, r.err + } + chunkType := r.buf[0] + if !r.readHeader { + if chunkType != chunkTypeStreamIdentifier { + r.err = ErrCorrupt + return 0, r.err + } + r.readHeader = true + } + chunkLen := int(r.buf[1]) | int(r.buf[2])<<8 | int(r.buf[3])<<16 + if chunkLen > len(r.buf) { + r.err = ErrUnsupported + return 0, r.err + } + + // The chunk types are specified at + // https://github.com/google/snappy/blob/master/framing_format.txt + switch chunkType { + case chunkTypeCompressedData: + // Section 4.2. Compressed data (chunk type 0x00). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return 0, r.err + } + buf := r.buf[:chunkLen] + if !r.readFull(buf, false) { + return 0, r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + buf = buf[checksumSize:] + + n, err := DecodedLen(buf) + if err != nil { + r.err = err + return 0, r.err + } + if n > len(r.decoded) { + r.err = ErrCorrupt + return 0, r.err + } + if _, err := Decode(r.decoded, buf); err != nil { + r.err = err + return 0, r.err + } + if crc(r.decoded[:n]) != checksum { + r.err = ErrCorrupt + return 0, r.err + } + r.i, r.j = 0, n + continue + + case chunkTypeUncompressedData: + // Section 4.3. Uncompressed data (chunk type 0x01). + if chunkLen < checksumSize { + r.err = ErrCorrupt + return 0, r.err + } + buf := r.buf[:checksumSize] + if !r.readFull(buf, false) { + return 0, r.err + } + checksum := uint32(buf[0]) | uint32(buf[1])<<8 | uint32(buf[2])<<16 | uint32(buf[3])<<24 + // Read directly into r.decoded instead of via r.buf. + n := chunkLen - checksumSize + if n > len(r.decoded) { + r.err = ErrCorrupt + return 0, r.err + } + if !r.readFull(r.decoded[:n], false) { + return 0, r.err + } + if crc(r.decoded[:n]) != checksum { + r.err = ErrCorrupt + return 0, r.err + } + r.i, r.j = 0, n + continue + + case chunkTypeStreamIdentifier: + // Section 4.1. Stream identifier (chunk type 0xff). + if chunkLen != len(magicBody) { + r.err = ErrCorrupt + return 0, r.err + } + if !r.readFull(r.buf[:len(magicBody)], false) { + return 0, r.err + } + for i := 0; i < len(magicBody); i++ { + if r.buf[i] != magicBody[i] { + r.err = ErrCorrupt + return 0, r.err + } + } + continue + } + + if chunkType <= 0x7f { + // Section 4.5. Reserved unskippable chunks (chunk types 0x02-0x7f). + r.err = ErrUnsupported + return 0, r.err + } + // Section 4.4 Padding (chunk type 0xfe). + // Section 4.6. Reserved skippable chunks (chunk types 0x80-0xfd). + if !r.readFull(r.buf[:chunkLen], false) { + return 0, r.err + } + } +} diff --git a/vendor/github.com/golang/snappy/decode_amd64.go b/vendor/github.com/golang/snappy/decode_amd64.go new file mode 100644 index 00000000000..fcd192b849e --- /dev/null +++ b/vendor/github.com/golang/snappy/decode_amd64.go @@ -0,0 +1,14 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine +// +build gc +// +build !noasm + +package snappy + +// decode has the same semantics as in decode_other.go. +// +//go:noescape +func decode(dst, src []byte) int diff --git a/vendor/github.com/golang/snappy/decode_amd64.s b/vendor/github.com/golang/snappy/decode_amd64.s new file mode 100644 index 00000000000..e6179f65e35 --- /dev/null +++ b/vendor/github.com/golang/snappy/decode_amd64.s @@ -0,0 +1,490 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine +// +build gc +// +build !noasm + +#include "textflag.h" + +// The asm code generally follows the pure Go code in decode_other.go, except +// where marked with a "!!!". + +// func decode(dst, src []byte) int +// +// All local variables fit into registers. The non-zero stack size is only to +// spill registers and push args when issuing a CALL. The register allocation: +// - AX scratch +// - BX scratch +// - CX length or x +// - DX offset +// - SI &src[s] +// - DI &dst[d] +// + R8 dst_base +// + R9 dst_len +// + R10 dst_base + dst_len +// + R11 src_base +// + R12 src_len +// + R13 src_base + src_len +// - R14 used by doCopy +// - R15 used by doCopy +// +// The registers R8-R13 (marked with a "+") are set at the start of the +// function, and after a CALL returns, and are not otherwise modified. +// +// The d variable is implicitly DI - R8, and len(dst)-d is R10 - DI. +// The s variable is implicitly SI - R11, and len(src)-s is R13 - SI. +TEXT ·decode(SB), NOSPLIT, $48-56 + // Initialize SI, DI and R8-R13. + MOVQ dst_base+0(FP), R8 + MOVQ dst_len+8(FP), R9 + MOVQ R8, DI + MOVQ R8, R10 + ADDQ R9, R10 + MOVQ src_base+24(FP), R11 + MOVQ src_len+32(FP), R12 + MOVQ R11, SI + MOVQ R11, R13 + ADDQ R12, R13 + +loop: + // for s < len(src) + CMPQ SI, R13 + JEQ end + + // CX = uint32(src[s]) + // + // switch src[s] & 0x03 + MOVBLZX (SI), CX + MOVL CX, BX + ANDL $3, BX + CMPL BX, $1 + JAE tagCopy + + // ---------------------------------------- + // The code below handles literal tags. + + // case tagLiteral: + // x := uint32(src[s] >> 2) + // switch + SHRL $2, CX + CMPL CX, $60 + JAE tagLit60Plus + + // case x < 60: + // s++ + INCQ SI + +doLit: + // This is the end of the inner "switch", when we have a literal tag. + // + // We assume that CX == x and x fits in a uint32, where x is the variable + // used in the pure Go decode_other.go code. + + // length = int(x) + 1 + // + // Unlike the pure Go code, we don't need to check if length <= 0 because + // CX can hold 64 bits, so the increment cannot overflow. + INCQ CX + + // Prepare to check if copying length bytes will run past the end of dst or + // src. + // + // AX = len(dst) - d + // BX = len(src) - s + MOVQ R10, AX + SUBQ DI, AX + MOVQ R13, BX + SUBQ SI, BX + + // !!! Try a faster technique for short (16 or fewer bytes) copies. + // + // if length > 16 || len(dst)-d < 16 || len(src)-s < 16 { + // goto callMemmove // Fall back on calling runtime·memmove. + // } + // + // The C++ snappy code calls this TryFastAppend. It also checks len(src)-s + // against 21 instead of 16, because it cannot assume that all of its input + // is contiguous in memory and so it needs to leave enough source bytes to + // read the next tag without refilling buffers, but Go's Decode assumes + // contiguousness (the src argument is a []byte). + CMPQ CX, $16 + JGT callMemmove + CMPQ AX, $16 + JLT callMemmove + CMPQ BX, $16 + JLT callMemmove + + // !!! Implement the copy from src to dst as a 16-byte load and store. + // (Decode's documentation says that dst and src must not overlap.) + // + // This always copies 16 bytes, instead of only length bytes, but that's + // OK. If the input is a valid Snappy encoding then subsequent iterations + // will fix up the overrun. Otherwise, Decode returns a nil []byte (and a + // non-nil error), so the overrun will be ignored. + // + // Note that on amd64, it is legal and cheap to issue unaligned 8-byte or + // 16-byte loads and stores. This technique probably wouldn't be as + // effective on architectures that are fussier about alignment. + MOVOU 0(SI), X0 + MOVOU X0, 0(DI) + + // d += length + // s += length + ADDQ CX, DI + ADDQ CX, SI + JMP loop + +callMemmove: + // if length > len(dst)-d || length > len(src)-s { etc } + CMPQ CX, AX + JGT errCorrupt + CMPQ CX, BX + JGT errCorrupt + + // copy(dst[d:], src[s:s+length]) + // + // This means calling runtime·memmove(&dst[d], &src[s], length), so we push + // DI, SI and CX as arguments. Coincidentally, we also need to spill those + // three registers to the stack, to save local variables across the CALL. + MOVQ DI, 0(SP) + MOVQ SI, 8(SP) + MOVQ CX, 16(SP) + MOVQ DI, 24(SP) + MOVQ SI, 32(SP) + MOVQ CX, 40(SP) + CALL runtime·memmove(SB) + + // Restore local variables: unspill registers from the stack and + // re-calculate R8-R13. + MOVQ 24(SP), DI + MOVQ 32(SP), SI + MOVQ 40(SP), CX + MOVQ dst_base+0(FP), R8 + MOVQ dst_len+8(FP), R9 + MOVQ R8, R10 + ADDQ R9, R10 + MOVQ src_base+24(FP), R11 + MOVQ src_len+32(FP), R12 + MOVQ R11, R13 + ADDQ R12, R13 + + // d += length + // s += length + ADDQ CX, DI + ADDQ CX, SI + JMP loop + +tagLit60Plus: + // !!! This fragment does the + // + // s += x - 58; if uint(s) > uint(len(src)) { etc } + // + // checks. In the asm version, we code it once instead of once per switch case. + ADDQ CX, SI + SUBQ $58, SI + MOVQ SI, BX + SUBQ R11, BX + CMPQ BX, R12 + JA errCorrupt + + // case x == 60: + CMPL CX, $61 + JEQ tagLit61 + JA tagLit62Plus + + // x = uint32(src[s-1]) + MOVBLZX -1(SI), CX + JMP doLit + +tagLit61: + // case x == 61: + // x = uint32(src[s-2]) | uint32(src[s-1])<<8 + MOVWLZX -2(SI), CX + JMP doLit + +tagLit62Plus: + CMPL CX, $62 + JA tagLit63 + + // case x == 62: + // x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 + MOVWLZX -3(SI), CX + MOVBLZX -1(SI), BX + SHLL $16, BX + ORL BX, CX + JMP doLit + +tagLit63: + // case x == 63: + // x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + MOVL -4(SI), CX + JMP doLit + +// The code above handles literal tags. +// ---------------------------------------- +// The code below handles copy tags. + +tagCopy4: + // case tagCopy4: + // s += 5 + ADDQ $5, SI + + // if uint(s) > uint(len(src)) { etc } + MOVQ SI, BX + SUBQ R11, BX + CMPQ BX, R12 + JA errCorrupt + + // length = 1 + int(src[s-5])>>2 + SHRQ $2, CX + INCQ CX + + // offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) + MOVLQZX -4(SI), DX + JMP doCopy + +tagCopy2: + // case tagCopy2: + // s += 3 + ADDQ $3, SI + + // if uint(s) > uint(len(src)) { etc } + MOVQ SI, BX + SUBQ R11, BX + CMPQ BX, R12 + JA errCorrupt + + // length = 1 + int(src[s-3])>>2 + SHRQ $2, CX + INCQ CX + + // offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) + MOVWQZX -2(SI), DX + JMP doCopy + +tagCopy: + // We have a copy tag. We assume that: + // - BX == src[s] & 0x03 + // - CX == src[s] + CMPQ BX, $2 + JEQ tagCopy2 + JA tagCopy4 + + // case tagCopy1: + // s += 2 + ADDQ $2, SI + + // if uint(s) > uint(len(src)) { etc } + MOVQ SI, BX + SUBQ R11, BX + CMPQ BX, R12 + JA errCorrupt + + // offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + MOVQ CX, DX + ANDQ $0xe0, DX + SHLQ $3, DX + MOVBQZX -1(SI), BX + ORQ BX, DX + + // length = 4 + int(src[s-2])>>2&0x7 + SHRQ $2, CX + ANDQ $7, CX + ADDQ $4, CX + +doCopy: + // This is the end of the outer "switch", when we have a copy tag. + // + // We assume that: + // - CX == length && CX > 0 + // - DX == offset + + // if offset <= 0 { etc } + CMPQ DX, $0 + JLE errCorrupt + + // if d < offset { etc } + MOVQ DI, BX + SUBQ R8, BX + CMPQ BX, DX + JLT errCorrupt + + // if length > len(dst)-d { etc } + MOVQ R10, BX + SUBQ DI, BX + CMPQ CX, BX + JGT errCorrupt + + // forwardCopy(dst[d:d+length], dst[d-offset:]); d += length + // + // Set: + // - R14 = len(dst)-d + // - R15 = &dst[d-offset] + MOVQ R10, R14 + SUBQ DI, R14 + MOVQ DI, R15 + SUBQ DX, R15 + + // !!! Try a faster technique for short (16 or fewer bytes) forward copies. + // + // First, try using two 8-byte load/stores, similar to the doLit technique + // above. Even if dst[d:d+length] and dst[d-offset:] can overlap, this is + // still OK if offset >= 8. Note that this has to be two 8-byte load/stores + // and not one 16-byte load/store, and the first store has to be before the + // second load, due to the overlap if offset is in the range [8, 16). + // + // if length > 16 || offset < 8 || len(dst)-d < 16 { + // goto slowForwardCopy + // } + // copy 16 bytes + // d += length + CMPQ CX, $16 + JGT slowForwardCopy + CMPQ DX, $8 + JLT slowForwardCopy + CMPQ R14, $16 + JLT slowForwardCopy + MOVQ 0(R15), AX + MOVQ AX, 0(DI) + MOVQ 8(R15), BX + MOVQ BX, 8(DI) + ADDQ CX, DI + JMP loop + +slowForwardCopy: + // !!! If the forward copy is longer than 16 bytes, or if offset < 8, we + // can still try 8-byte load stores, provided we can overrun up to 10 extra + // bytes. As above, the overrun will be fixed up by subsequent iterations + // of the outermost loop. + // + // The C++ snappy code calls this technique IncrementalCopyFastPath. Its + // commentary says: + // + // ---- + // + // The main part of this loop is a simple copy of eight bytes at a time + // until we've copied (at least) the requested amount of bytes. However, + // if d and d-offset are less than eight bytes apart (indicating a + // repeating pattern of length < 8), we first need to expand the pattern in + // order to get the correct results. For instance, if the buffer looks like + // this, with the eight-byte and patterns marked as + // intervals: + // + // abxxxxxxxxxxxx + // [------] d-offset + // [------] d + // + // a single eight-byte copy from to will repeat the pattern + // once, after which we can move two bytes without moving : + // + // ababxxxxxxxxxx + // [------] d-offset + // [------] d + // + // and repeat the exercise until the two no longer overlap. + // + // This allows us to do very well in the special case of one single byte + // repeated many times, without taking a big hit for more general cases. + // + // The worst case of extra writing past the end of the match occurs when + // offset == 1 and length == 1; the last copy will read from byte positions + // [0..7] and write to [4..11], whereas it was only supposed to write to + // position 1. Thus, ten excess bytes. + // + // ---- + // + // That "10 byte overrun" worst case is confirmed by Go's + // TestSlowForwardCopyOverrun, which also tests the fixUpSlowForwardCopy + // and finishSlowForwardCopy algorithm. + // + // if length > len(dst)-d-10 { + // goto verySlowForwardCopy + // } + SUBQ $10, R14 + CMPQ CX, R14 + JGT verySlowForwardCopy + +makeOffsetAtLeast8: + // !!! As above, expand the pattern so that offset >= 8 and we can use + // 8-byte load/stores. + // + // for offset < 8 { + // copy 8 bytes from dst[d-offset:] to dst[d:] + // length -= offset + // d += offset + // offset += offset + // // The two previous lines together means that d-offset, and therefore + // // R15, is unchanged. + // } + CMPQ DX, $8 + JGE fixUpSlowForwardCopy + MOVQ (R15), BX + MOVQ BX, (DI) + SUBQ DX, CX + ADDQ DX, DI + ADDQ DX, DX + JMP makeOffsetAtLeast8 + +fixUpSlowForwardCopy: + // !!! Add length (which might be negative now) to d (implied by DI being + // &dst[d]) so that d ends up at the right place when we jump back to the + // top of the loop. Before we do that, though, we save DI to AX so that, if + // length is positive, copying the remaining length bytes will write to the + // right place. + MOVQ DI, AX + ADDQ CX, DI + +finishSlowForwardCopy: + // !!! Repeat 8-byte load/stores until length <= 0. Ending with a negative + // length means that we overrun, but as above, that will be fixed up by + // subsequent iterations of the outermost loop. + CMPQ CX, $0 + JLE loop + MOVQ (R15), BX + MOVQ BX, (AX) + ADDQ $8, R15 + ADDQ $8, AX + SUBQ $8, CX + JMP finishSlowForwardCopy + +verySlowForwardCopy: + // verySlowForwardCopy is a simple implementation of forward copy. In C + // parlance, this is a do/while loop instead of a while loop, since we know + // that length > 0. In Go syntax: + // + // for { + // dst[d] = dst[d - offset] + // d++ + // length-- + // if length == 0 { + // break + // } + // } + MOVB (R15), BX + MOVB BX, (DI) + INCQ R15 + INCQ DI + DECQ CX + JNZ verySlowForwardCopy + JMP loop + +// The code above handles copy tags. +// ---------------------------------------- + +end: + // This is the end of the "for s < len(src)". + // + // if d != len(dst) { etc } + CMPQ DI, R10 + JNE errCorrupt + + // return 0 + MOVQ $0, ret+48(FP) + RET + +errCorrupt: + // return decodeErrCodeCorrupt + MOVQ $1, ret+48(FP) + RET diff --git a/vendor/github.com/golang/snappy/decode_other.go b/vendor/github.com/golang/snappy/decode_other.go new file mode 100644 index 00000000000..8c9f2049bc7 --- /dev/null +++ b/vendor/github.com/golang/snappy/decode_other.go @@ -0,0 +1,101 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !amd64 appengine !gc noasm + +package snappy + +// decode writes the decoding of src to dst. It assumes that the varint-encoded +// length of the decompressed bytes has already been read, and that len(dst) +// equals that length. +// +// It returns 0 on success or a decodeErrCodeXxx error code on failure. +func decode(dst, src []byte) int { + var d, s, offset, length int + for s < len(src) { + switch src[s] & 0x03 { + case tagLiteral: + x := uint32(src[s] >> 2) + switch { + case x < 60: + s++ + case x == 60: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-1]) + case x == 61: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-2]) | uint32(src[s-1])<<8 + case x == 62: + s += 4 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-3]) | uint32(src[s-2])<<8 | uint32(src[s-1])<<16 + case x == 63: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + x = uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24 + } + length = int(x) + 1 + if length <= 0 { + return decodeErrCodeUnsupportedLiteralLength + } + if length > len(dst)-d || length > len(src)-s { + return decodeErrCodeCorrupt + } + copy(dst[d:], src[s:s+length]) + d += length + s += length + continue + + case tagCopy1: + s += 2 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 4 + int(src[s-2])>>2&0x7 + offset = int(uint32(src[s-2])&0xe0<<3 | uint32(src[s-1])) + + case tagCopy2: + s += 3 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 1 + int(src[s-3])>>2 + offset = int(uint32(src[s-2]) | uint32(src[s-1])<<8) + + case tagCopy4: + s += 5 + if uint(s) > uint(len(src)) { // The uint conversions catch overflow from the previous line. + return decodeErrCodeCorrupt + } + length = 1 + int(src[s-5])>>2 + offset = int(uint32(src[s-4]) | uint32(src[s-3])<<8 | uint32(src[s-2])<<16 | uint32(src[s-1])<<24) + } + + if offset <= 0 || d < offset || length > len(dst)-d { + return decodeErrCodeCorrupt + } + // Copy from an earlier sub-slice of dst to a later sub-slice. Unlike + // the built-in copy function, this byte-by-byte copy always runs + // forwards, even if the slices overlap. Conceptually, this is: + // + // d += forwardCopy(dst[d:d+length], dst[d-offset:]) + for end := d + length; d != end; d++ { + dst[d] = dst[d-offset] + } + } + if d != len(dst) { + return decodeErrCodeCorrupt + } + return 0 +} diff --git a/vendor/github.com/golang/snappy/encode.go b/vendor/github.com/golang/snappy/encode.go new file mode 100644 index 00000000000..8d393e904bb --- /dev/null +++ b/vendor/github.com/golang/snappy/encode.go @@ -0,0 +1,285 @@ +// Copyright 2011 The Snappy-Go 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 snappy + +import ( + "encoding/binary" + "errors" + "io" +) + +// Encode returns the encoded form of src. The returned slice may be a sub- +// slice of dst if dst was large enough to hold the entire encoded block. +// Otherwise, a newly allocated slice will be returned. +// +// The dst and src must not overlap. It is valid to pass a nil dst. +func Encode(dst, src []byte) []byte { + if n := MaxEncodedLen(len(src)); n < 0 { + panic(ErrTooLarge) + } else if len(dst) < n { + dst = make([]byte, n) + } + + // The block starts with the varint-encoded length of the decompressed bytes. + d := binary.PutUvarint(dst, uint64(len(src))) + + for len(src) > 0 { + p := src + src = nil + if len(p) > maxBlockSize { + p, src = p[:maxBlockSize], p[maxBlockSize:] + } + if len(p) < minNonLiteralBlockSize { + d += emitLiteral(dst[d:], p) + } else { + d += encodeBlock(dst[d:], p) + } + } + return dst[:d] +} + +// inputMargin is the minimum number of extra input bytes to keep, inside +// encodeBlock's inner loop. On some architectures, this margin lets us +// implement a fast path for emitLiteral, where the copy of short (<= 16 byte) +// literals can be implemented as a single load to and store from a 16-byte +// register. That literal's actual length can be as short as 1 byte, so this +// can copy up to 15 bytes too much, but that's OK as subsequent iterations of +// the encoding loop will fix up the copy overrun, and this inputMargin ensures +// that we don't overrun the dst and src buffers. +const inputMargin = 16 - 1 + +// minNonLiteralBlockSize is the minimum size of the input to encodeBlock that +// could be encoded with a copy tag. This is the minimum with respect to the +// algorithm used by encodeBlock, not a minimum enforced by the file format. +// +// The encoded output must start with at least a 1 byte literal, as there are +// no previous bytes to copy. A minimal (1 byte) copy after that, generated +// from an emitCopy call in encodeBlock's main loop, would require at least +// another inputMargin bytes, for the reason above: we want any emitLiteral +// calls inside encodeBlock's main loop to use the fast path if possible, which +// requires being able to overrun by inputMargin bytes. Thus, +// minNonLiteralBlockSize equals 1 + 1 + inputMargin. +// +// The C++ code doesn't use this exact threshold, but it could, as discussed at +// https://groups.google.com/d/topic/snappy-compression/oGbhsdIJSJ8/discussion +// The difference between Go (2+inputMargin) and C++ (inputMargin) is purely an +// optimization. It should not affect the encoded form. This is tested by +// TestSameEncodingAsCppShortCopies. +const minNonLiteralBlockSize = 1 + 1 + inputMargin + +// MaxEncodedLen returns the maximum length of a snappy block, given its +// uncompressed length. +// +// It will return a negative value if srcLen is too large to encode. +func MaxEncodedLen(srcLen int) int { + n := uint64(srcLen) + if n > 0xffffffff { + return -1 + } + // Compressed data can be defined as: + // compressed := item* literal* + // item := literal* copy + // + // The trailing literal sequence has a space blowup of at most 62/60 + // since a literal of length 60 needs one tag byte + one extra byte + // for length information. + // + // Item blowup is trickier to measure. Suppose the "copy" op copies + // 4 bytes of data. Because of a special check in the encoding code, + // we produce a 4-byte copy only if the offset is < 65536. Therefore + // the copy op takes 3 bytes to encode, and this type of item leads + // to at most the 62/60 blowup for representing literals. + // + // Suppose the "copy" op copies 5 bytes of data. If the offset is big + // enough, it will take 5 bytes to encode the copy op. Therefore the + // worst case here is a one-byte literal followed by a five-byte copy. + // That is, 6 bytes of input turn into 7 bytes of "compressed" data. + // + // This last factor dominates the blowup, so the final estimate is: + n = 32 + n + n/6 + if n > 0xffffffff { + return -1 + } + return int(n) +} + +var errClosed = errors.New("snappy: Writer is closed") + +// NewWriter returns a new Writer that compresses to w. +// +// The Writer returned does not buffer writes. There is no need to Flush or +// Close such a Writer. +// +// Deprecated: the Writer returned is not suitable for many small writes, only +// for few large writes. Use NewBufferedWriter instead, which is efficient +// regardless of the frequency and shape of the writes, and remember to Close +// that Writer when done. +func NewWriter(w io.Writer) *Writer { + return &Writer{ + w: w, + obuf: make([]byte, obufLen), + } +} + +// NewBufferedWriter returns a new Writer that compresses to w, using the +// framing format described at +// https://github.com/google/snappy/blob/master/framing_format.txt +// +// The Writer returned buffers writes. Users must call Close to guarantee all +// data has been forwarded to the underlying io.Writer. They may also call +// Flush zero or more times before calling Close. +func NewBufferedWriter(w io.Writer) *Writer { + return &Writer{ + w: w, + ibuf: make([]byte, 0, maxBlockSize), + obuf: make([]byte, obufLen), + } +} + +// Writer is an io.Writer that can write Snappy-compressed bytes. +type Writer struct { + w io.Writer + err error + + // ibuf is a buffer for the incoming (uncompressed) bytes. + // + // Its use is optional. For backwards compatibility, Writers created by the + // NewWriter function have ibuf == nil, do not buffer incoming bytes, and + // therefore do not need to be Flush'ed or Close'd. + ibuf []byte + + // obuf is a buffer for the outgoing (compressed) bytes. + obuf []byte + + // wroteStreamHeader is whether we have written the stream header. + wroteStreamHeader bool +} + +// Reset discards the writer's state and switches the Snappy writer to write to +// w. This permits reusing a Writer rather than allocating a new one. +func (w *Writer) Reset(writer io.Writer) { + w.w = writer + w.err = nil + if w.ibuf != nil { + w.ibuf = w.ibuf[:0] + } + w.wroteStreamHeader = false +} + +// Write satisfies the io.Writer interface. +func (w *Writer) Write(p []byte) (nRet int, errRet error) { + if w.ibuf == nil { + // Do not buffer incoming bytes. This does not perform or compress well + // if the caller of Writer.Write writes many small slices. This + // behavior is therefore deprecated, but still supported for backwards + // compatibility with code that doesn't explicitly Flush or Close. + return w.write(p) + } + + // The remainder of this method is based on bufio.Writer.Write from the + // standard library. + + for len(p) > (cap(w.ibuf)-len(w.ibuf)) && w.err == nil { + var n int + if len(w.ibuf) == 0 { + // Large write, empty buffer. + // Write directly from p to avoid copy. + n, _ = w.write(p) + } else { + n = copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) + w.ibuf = w.ibuf[:len(w.ibuf)+n] + w.Flush() + } + nRet += n + p = p[n:] + } + if w.err != nil { + return nRet, w.err + } + n := copy(w.ibuf[len(w.ibuf):cap(w.ibuf)], p) + w.ibuf = w.ibuf[:len(w.ibuf)+n] + nRet += n + return nRet, nil +} + +func (w *Writer) write(p []byte) (nRet int, errRet error) { + if w.err != nil { + return 0, w.err + } + for len(p) > 0 { + obufStart := len(magicChunk) + if !w.wroteStreamHeader { + w.wroteStreamHeader = true + copy(w.obuf, magicChunk) + obufStart = 0 + } + + var uncompressed []byte + if len(p) > maxBlockSize { + uncompressed, p = p[:maxBlockSize], p[maxBlockSize:] + } else { + uncompressed, p = p, nil + } + checksum := crc(uncompressed) + + // Compress the buffer, discarding the result if the improvement + // isn't at least 12.5%. + compressed := Encode(w.obuf[obufHeaderLen:], uncompressed) + chunkType := uint8(chunkTypeCompressedData) + chunkLen := 4 + len(compressed) + obufEnd := obufHeaderLen + len(compressed) + if len(compressed) >= len(uncompressed)-len(uncompressed)/8 { + chunkType = chunkTypeUncompressedData + chunkLen = 4 + len(uncompressed) + obufEnd = obufHeaderLen + } + + // Fill in the per-chunk header that comes before the body. + w.obuf[len(magicChunk)+0] = chunkType + w.obuf[len(magicChunk)+1] = uint8(chunkLen >> 0) + w.obuf[len(magicChunk)+2] = uint8(chunkLen >> 8) + w.obuf[len(magicChunk)+3] = uint8(chunkLen >> 16) + w.obuf[len(magicChunk)+4] = uint8(checksum >> 0) + w.obuf[len(magicChunk)+5] = uint8(checksum >> 8) + w.obuf[len(magicChunk)+6] = uint8(checksum >> 16) + w.obuf[len(magicChunk)+7] = uint8(checksum >> 24) + + if _, err := w.w.Write(w.obuf[obufStart:obufEnd]); err != nil { + w.err = err + return nRet, err + } + if chunkType == chunkTypeUncompressedData { + if _, err := w.w.Write(uncompressed); err != nil { + w.err = err + return nRet, err + } + } + nRet += len(uncompressed) + } + return nRet, nil +} + +// Flush flushes the Writer to its underlying io.Writer. +func (w *Writer) Flush() error { + if w.err != nil { + return w.err + } + if len(w.ibuf) == 0 { + return nil + } + w.write(w.ibuf) + w.ibuf = w.ibuf[:0] + return w.err +} + +// Close calls Flush and then closes the Writer. +func (w *Writer) Close() error { + w.Flush() + ret := w.err + if w.err == nil { + w.err = errClosed + } + return ret +} diff --git a/vendor/github.com/golang/snappy/encode_amd64.go b/vendor/github.com/golang/snappy/encode_amd64.go new file mode 100644 index 00000000000..150d91bc8be --- /dev/null +++ b/vendor/github.com/golang/snappy/encode_amd64.go @@ -0,0 +1,29 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine +// +build gc +// +build !noasm + +package snappy + +// emitLiteral has the same semantics as in encode_other.go. +// +//go:noescape +func emitLiteral(dst, lit []byte) int + +// emitCopy has the same semantics as in encode_other.go. +// +//go:noescape +func emitCopy(dst []byte, offset, length int) int + +// extendMatch has the same semantics as in encode_other.go. +// +//go:noescape +func extendMatch(src []byte, i, j int) int + +// encodeBlock has the same semantics as in encode_other.go. +// +//go:noescape +func encodeBlock(dst, src []byte) (d int) diff --git a/vendor/github.com/golang/snappy/encode_amd64.s b/vendor/github.com/golang/snappy/encode_amd64.s new file mode 100644 index 00000000000..adfd979fe27 --- /dev/null +++ b/vendor/github.com/golang/snappy/encode_amd64.s @@ -0,0 +1,730 @@ +// Copyright 2016 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !appengine +// +build gc +// +build !noasm + +#include "textflag.h" + +// The XXX lines assemble on Go 1.4, 1.5 and 1.7, but not 1.6, due to a +// Go toolchain regression. See https://github.com/golang/go/issues/15426 and +// https://github.com/golang/snappy/issues/29 +// +// As a workaround, the package was built with a known good assembler, and +// those instructions were disassembled by "objdump -d" to yield the +// 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 +// style comments, in AT&T asm syntax. Note that rsp here is a physical +// register, not Go/asm's SP pseudo-register (see https://golang.org/doc/asm). +// The instructions were then encoded as "BYTE $0x.." sequences, which assemble +// fine on Go 1.6. + +// The asm code generally follows the pure Go code in encode_other.go, except +// where marked with a "!!!". + +// ---------------------------------------------------------------------------- + +// func emitLiteral(dst, lit []byte) int +// +// All local variables fit into registers. The register allocation: +// - AX len(lit) +// - BX n +// - DX return value +// - DI &dst[i] +// - R10 &lit[0] +// +// The 24 bytes of stack space is to call runtime·memmove. +// +// The unusual register allocation of local variables, such as R10 for the +// source pointer, matches the allocation used at the call site in encodeBlock, +// which makes it easier to manually inline this function. +TEXT ·emitLiteral(SB), NOSPLIT, $24-56 + MOVQ dst_base+0(FP), DI + MOVQ lit_base+24(FP), R10 + MOVQ lit_len+32(FP), AX + MOVQ AX, DX + MOVL AX, BX + SUBL $1, BX + + CMPL BX, $60 + JLT oneByte + CMPL BX, $256 + JLT twoBytes + +threeBytes: + MOVB $0xf4, 0(DI) + MOVW BX, 1(DI) + ADDQ $3, DI + ADDQ $3, DX + JMP memmove + +twoBytes: + MOVB $0xf0, 0(DI) + MOVB BX, 1(DI) + ADDQ $2, DI + ADDQ $2, DX + JMP memmove + +oneByte: + SHLB $2, BX + MOVB BX, 0(DI) + ADDQ $1, DI + ADDQ $1, DX + +memmove: + MOVQ DX, ret+48(FP) + + // copy(dst[i:], lit) + // + // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push + // DI, R10 and AX as arguments. + MOVQ DI, 0(SP) + MOVQ R10, 8(SP) + MOVQ AX, 16(SP) + CALL runtime·memmove(SB) + RET + +// ---------------------------------------------------------------------------- + +// func emitCopy(dst []byte, offset, length int) int +// +// All local variables fit into registers. The register allocation: +// - AX length +// - SI &dst[0] +// - DI &dst[i] +// - R11 offset +// +// The unusual register allocation of local variables, such as R11 for the +// offset, matches the allocation used at the call site in encodeBlock, which +// makes it easier to manually inline this function. +TEXT ·emitCopy(SB), NOSPLIT, $0-48 + MOVQ dst_base+0(FP), DI + MOVQ DI, SI + MOVQ offset+24(FP), R11 + MOVQ length+32(FP), AX + +loop0: + // for length >= 68 { etc } + CMPL AX, $68 + JLT step1 + + // Emit a length 64 copy, encoded as 3 bytes. + MOVB $0xfe, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + SUBL $64, AX + JMP loop0 + +step1: + // if length > 64 { etc } + CMPL AX, $64 + JLE step2 + + // Emit a length 60 copy, encoded as 3 bytes. + MOVB $0xee, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + SUBL $60, AX + +step2: + // if length >= 12 || offset >= 2048 { goto step3 } + CMPL AX, $12 + JGE step3 + CMPL R11, $2048 + JGE step3 + + // Emit the remaining copy, encoded as 2 bytes. + MOVB R11, 1(DI) + SHRL $8, R11 + SHLB $5, R11 + SUBB $4, AX + SHLB $2, AX + ORB AX, R11 + ORB $1, R11 + MOVB R11, 0(DI) + ADDQ $2, DI + + // Return the number of bytes written. + SUBQ SI, DI + MOVQ DI, ret+40(FP) + RET + +step3: + // Emit the remaining copy, encoded as 3 bytes. + SUBL $1, AX + SHLB $2, AX + ORB $2, AX + MOVB AX, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + + // Return the number of bytes written. + SUBQ SI, DI + MOVQ DI, ret+40(FP) + RET + +// ---------------------------------------------------------------------------- + +// func extendMatch(src []byte, i, j int) int +// +// All local variables fit into registers. The register allocation: +// - DX &src[0] +// - SI &src[j] +// - R13 &src[len(src) - 8] +// - R14 &src[len(src)] +// - R15 &src[i] +// +// The unusual register allocation of local variables, such as R15 for a source +// pointer, matches the allocation used at the call site in encodeBlock, which +// makes it easier to manually inline this function. +TEXT ·extendMatch(SB), NOSPLIT, $0-48 + MOVQ src_base+0(FP), DX + MOVQ src_len+8(FP), R14 + MOVQ i+24(FP), R15 + MOVQ j+32(FP), SI + ADDQ DX, R14 + ADDQ DX, R15 + ADDQ DX, SI + MOVQ R14, R13 + SUBQ $8, R13 + +cmp8: + // As long as we are 8 or more bytes before the end of src, we can load and + // compare 8 bytes at a time. If those 8 bytes are equal, repeat. + CMPQ SI, R13 + JA cmp1 + MOVQ (R15), AX + MOVQ (SI), BX + CMPQ AX, BX + JNE bsf + ADDQ $8, R15 + ADDQ $8, SI + JMP cmp8 + +bsf: + // If those 8 bytes were not equal, XOR the two 8 byte values, and return + // the index of the first byte that differs. The BSF instruction finds the + // least significant 1 bit, the amd64 architecture is little-endian, and + // the shift by 3 converts a bit index to a byte index. + XORQ AX, BX + BSFQ BX, BX + SHRQ $3, BX + ADDQ BX, SI + + // Convert from &src[ret] to ret. + SUBQ DX, SI + MOVQ SI, ret+40(FP) + RET + +cmp1: + // In src's tail, compare 1 byte at a time. + CMPQ SI, R14 + JAE extendMatchEnd + MOVB (R15), AX + MOVB (SI), BX + CMPB AX, BX + JNE extendMatchEnd + ADDQ $1, R15 + ADDQ $1, SI + JMP cmp1 + +extendMatchEnd: + // Convert from &src[ret] to ret. + SUBQ DX, SI + MOVQ SI, ret+40(FP) + RET + +// ---------------------------------------------------------------------------- + +// func encodeBlock(dst, src []byte) (d int) +// +// All local variables fit into registers, other than "var table". The register +// allocation: +// - AX . . +// - BX . . +// - CX 56 shift (note that amd64 shifts by non-immediates must use CX). +// - DX 64 &src[0], tableSize +// - SI 72 &src[s] +// - DI 80 &dst[d] +// - R9 88 sLimit +// - R10 . &src[nextEmit] +// - R11 96 prevHash, currHash, nextHash, offset +// - R12 104 &src[base], skip +// - R13 . &src[nextS], &src[len(src) - 8] +// - R14 . len(src), bytesBetweenHashLookups, &src[len(src)], x +// - R15 112 candidate +// +// The second column (56, 64, etc) is the stack offset to spill the registers +// when calling other functions. We could pack this slightly tighter, but it's +// simpler to have a dedicated spill map independent of the function called. +// +// "var table [maxTableSize]uint16" takes up 32768 bytes of stack space. An +// extra 56 bytes, to call other functions, and an extra 64 bytes, to spill +// local variables (registers) during calls gives 32768 + 56 + 64 = 32888. +TEXT ·encodeBlock(SB), 0, $32888-56 + MOVQ dst_base+0(FP), DI + MOVQ src_base+24(FP), SI + MOVQ src_len+32(FP), R14 + + // shift, tableSize := uint32(32-8), 1<<8 + MOVQ $24, CX + MOVQ $256, DX + +calcShift: + // for ; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { + // shift-- + // } + CMPQ DX, $16384 + JGE varTable + CMPQ DX, R14 + JGE varTable + SUBQ $1, CX + SHLQ $1, DX + JMP calcShift + +varTable: + // var table [maxTableSize]uint16 + // + // In the asm code, unlike the Go code, we can zero-initialize only the + // first tableSize elements. Each uint16 element is 2 bytes and each MOVOU + // writes 16 bytes, so we can do only tableSize/8 writes instead of the + // 2048 writes that would zero-initialize all of table's 32768 bytes. + SHRQ $3, DX + LEAQ table-32768(SP), BX + PXOR X0, X0 + +memclr: + MOVOU X0, 0(BX) + ADDQ $16, BX + SUBQ $1, DX + JNZ memclr + + // !!! DX = &src[0] + MOVQ SI, DX + + // sLimit := len(src) - inputMargin + MOVQ R14, R9 + SUBQ $15, R9 + + // !!! Pre-emptively spill CX, DX and R9 to the stack. Their values don't + // change for the rest of the function. + MOVQ CX, 56(SP) + MOVQ DX, 64(SP) + MOVQ R9, 88(SP) + + // nextEmit := 0 + MOVQ DX, R10 + + // s := 1 + ADDQ $1, SI + + // nextHash := hash(load32(src, s), shift) + MOVL 0(SI), R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + +outer: + // for { etc } + + // skip := 32 + MOVQ $32, R12 + + // nextS := s + MOVQ SI, R13 + + // candidate := 0 + MOVQ $0, R15 + +inner0: + // for { etc } + + // s := nextS + MOVQ R13, SI + + // bytesBetweenHashLookups := skip >> 5 + MOVQ R12, R14 + SHRQ $5, R14 + + // nextS = s + bytesBetweenHashLookups + ADDQ R14, R13 + + // skip += bytesBetweenHashLookups + ADDQ R14, R12 + + // if nextS > sLimit { goto emitRemainder } + MOVQ R13, AX + SUBQ DX, AX + CMPQ AX, R9 + JA emitRemainder + + // candidate = int(table[nextHash]) + // XXX: MOVWQZX table-32768(SP)(R11*2), R15 + // XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 + BYTE $0x4e + BYTE $0x0f + BYTE $0xb7 + BYTE $0x7c + BYTE $0x5c + BYTE $0x78 + + // table[nextHash] = uint16(s) + MOVQ SI, AX + SUBQ DX, AX + + // XXX: MOVW AX, table-32768(SP)(R11*2) + // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) + BYTE $0x66 + BYTE $0x42 + BYTE $0x89 + BYTE $0x44 + BYTE $0x5c + BYTE $0x78 + + // nextHash = hash(load32(src, nextS), shift) + MOVL 0(R13), R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + + // if load32(src, s) != load32(src, candidate) { continue } break + MOVL 0(SI), AX + MOVL (DX)(R15*1), BX + CMPL AX, BX + JNE inner0 + +fourByteMatch: + // As per the encode_other.go code: + // + // A 4-byte match has been found. We'll later see etc. + + // !!! Jump to a fast path for short (<= 16 byte) literals. See the comment + // on inputMargin in encode.go. + MOVQ SI, AX + SUBQ R10, AX + CMPQ AX, $16 + JLE emitLiteralFastPath + + // ---------------------------------------- + // Begin inline of the emitLiteral call. + // + // d += emitLiteral(dst[d:], src[nextEmit:s]) + + MOVL AX, BX + SUBL $1, BX + + CMPL BX, $60 + JLT inlineEmitLiteralOneByte + CMPL BX, $256 + JLT inlineEmitLiteralTwoBytes + +inlineEmitLiteralThreeBytes: + MOVB $0xf4, 0(DI) + MOVW BX, 1(DI) + ADDQ $3, DI + JMP inlineEmitLiteralMemmove + +inlineEmitLiteralTwoBytes: + MOVB $0xf0, 0(DI) + MOVB BX, 1(DI) + ADDQ $2, DI + JMP inlineEmitLiteralMemmove + +inlineEmitLiteralOneByte: + SHLB $2, BX + MOVB BX, 0(DI) + ADDQ $1, DI + +inlineEmitLiteralMemmove: + // Spill local variables (registers) onto the stack; call; unspill. + // + // copy(dst[i:], lit) + // + // This means calling runtime·memmove(&dst[i], &lit[0], len(lit)), so we push + // DI, R10 and AX as arguments. + MOVQ DI, 0(SP) + MOVQ R10, 8(SP) + MOVQ AX, 16(SP) + ADDQ AX, DI // Finish the "d +=" part of "d += emitLiteral(etc)". + MOVQ SI, 72(SP) + MOVQ DI, 80(SP) + MOVQ R15, 112(SP) + CALL runtime·memmove(SB) + MOVQ 56(SP), CX + MOVQ 64(SP), DX + MOVQ 72(SP), SI + MOVQ 80(SP), DI + MOVQ 88(SP), R9 + MOVQ 112(SP), R15 + JMP inner1 + +inlineEmitLiteralEnd: + // End inline of the emitLiteral call. + // ---------------------------------------- + +emitLiteralFastPath: + // !!! Emit the 1-byte encoding "uint8(len(lit)-1)<<2". + MOVB AX, BX + SUBB $1, BX + SHLB $2, BX + MOVB BX, (DI) + ADDQ $1, DI + + // !!! Implement the copy from lit to dst as a 16-byte load and store. + // (Encode's documentation says that dst and src must not overlap.) + // + // This always copies 16 bytes, instead of only len(lit) bytes, but that's + // OK. Subsequent iterations will fix up the overrun. + // + // Note that on amd64, it is legal and cheap to issue unaligned 8-byte or + // 16-byte loads and stores. This technique probably wouldn't be as + // effective on architectures that are fussier about alignment. + MOVOU 0(R10), X0 + MOVOU X0, 0(DI) + ADDQ AX, DI + +inner1: + // for { etc } + + // base := s + MOVQ SI, R12 + + // !!! offset := base - candidate + MOVQ R12, R11 + SUBQ R15, R11 + SUBQ DX, R11 + + // ---------------------------------------- + // Begin inline of the extendMatch call. + // + // s = extendMatch(src, candidate+4, s+4) + + // !!! R14 = &src[len(src)] + MOVQ src_len+32(FP), R14 + ADDQ DX, R14 + + // !!! R13 = &src[len(src) - 8] + MOVQ R14, R13 + SUBQ $8, R13 + + // !!! R15 = &src[candidate + 4] + ADDQ $4, R15 + ADDQ DX, R15 + + // !!! s += 4 + ADDQ $4, SI + +inlineExtendMatchCmp8: + // As long as we are 8 or more bytes before the end of src, we can load and + // compare 8 bytes at a time. If those 8 bytes are equal, repeat. + CMPQ SI, R13 + JA inlineExtendMatchCmp1 + MOVQ (R15), AX + MOVQ (SI), BX + CMPQ AX, BX + JNE inlineExtendMatchBSF + ADDQ $8, R15 + ADDQ $8, SI + JMP inlineExtendMatchCmp8 + +inlineExtendMatchBSF: + // If those 8 bytes were not equal, XOR the two 8 byte values, and return + // the index of the first byte that differs. The BSF instruction finds the + // least significant 1 bit, the amd64 architecture is little-endian, and + // the shift by 3 converts a bit index to a byte index. + XORQ AX, BX + BSFQ BX, BX + SHRQ $3, BX + ADDQ BX, SI + JMP inlineExtendMatchEnd + +inlineExtendMatchCmp1: + // In src's tail, compare 1 byte at a time. + CMPQ SI, R14 + JAE inlineExtendMatchEnd + MOVB (R15), AX + MOVB (SI), BX + CMPB AX, BX + JNE inlineExtendMatchEnd + ADDQ $1, R15 + ADDQ $1, SI + JMP inlineExtendMatchCmp1 + +inlineExtendMatchEnd: + // End inline of the extendMatch call. + // ---------------------------------------- + + // ---------------------------------------- + // Begin inline of the emitCopy call. + // + // d += emitCopy(dst[d:], base-candidate, s-base) + + // !!! length := s - base + MOVQ SI, AX + SUBQ R12, AX + +inlineEmitCopyLoop0: + // for length >= 68 { etc } + CMPL AX, $68 + JLT inlineEmitCopyStep1 + + // Emit a length 64 copy, encoded as 3 bytes. + MOVB $0xfe, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + SUBL $64, AX + JMP inlineEmitCopyLoop0 + +inlineEmitCopyStep1: + // if length > 64 { etc } + CMPL AX, $64 + JLE inlineEmitCopyStep2 + + // Emit a length 60 copy, encoded as 3 bytes. + MOVB $0xee, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + SUBL $60, AX + +inlineEmitCopyStep2: + // if length >= 12 || offset >= 2048 { goto inlineEmitCopyStep3 } + CMPL AX, $12 + JGE inlineEmitCopyStep3 + CMPL R11, $2048 + JGE inlineEmitCopyStep3 + + // Emit the remaining copy, encoded as 2 bytes. + MOVB R11, 1(DI) + SHRL $8, R11 + SHLB $5, R11 + SUBB $4, AX + SHLB $2, AX + ORB AX, R11 + ORB $1, R11 + MOVB R11, 0(DI) + ADDQ $2, DI + JMP inlineEmitCopyEnd + +inlineEmitCopyStep3: + // Emit the remaining copy, encoded as 3 bytes. + SUBL $1, AX + SHLB $2, AX + ORB $2, AX + MOVB AX, 0(DI) + MOVW R11, 1(DI) + ADDQ $3, DI + +inlineEmitCopyEnd: + // End inline of the emitCopy call. + // ---------------------------------------- + + // nextEmit = s + MOVQ SI, R10 + + // if s >= sLimit { goto emitRemainder } + MOVQ SI, AX + SUBQ DX, AX + CMPQ AX, R9 + JAE emitRemainder + + // As per the encode_other.go code: + // + // We could immediately etc. + + // x := load64(src, s-1) + MOVQ -1(SI), R14 + + // prevHash := hash(uint32(x>>0), shift) + MOVL R14, R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + + // table[prevHash] = uint16(s-1) + MOVQ SI, AX + SUBQ DX, AX + SUBQ $1, AX + + // XXX: MOVW AX, table-32768(SP)(R11*2) + // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) + BYTE $0x66 + BYTE $0x42 + BYTE $0x89 + BYTE $0x44 + BYTE $0x5c + BYTE $0x78 + + // currHash := hash(uint32(x>>8), shift) + SHRQ $8, R14 + MOVL R14, R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + + // candidate = int(table[currHash]) + // XXX: MOVWQZX table-32768(SP)(R11*2), R15 + // XXX: 4e 0f b7 7c 5c 78 movzwq 0x78(%rsp,%r11,2),%r15 + BYTE $0x4e + BYTE $0x0f + BYTE $0xb7 + BYTE $0x7c + BYTE $0x5c + BYTE $0x78 + + // table[currHash] = uint16(s) + ADDQ $1, AX + + // XXX: MOVW AX, table-32768(SP)(R11*2) + // XXX: 66 42 89 44 5c 78 mov %ax,0x78(%rsp,%r11,2) + BYTE $0x66 + BYTE $0x42 + BYTE $0x89 + BYTE $0x44 + BYTE $0x5c + BYTE $0x78 + + // if uint32(x>>8) == load32(src, candidate) { continue } + MOVL (DX)(R15*1), BX + CMPL R14, BX + JEQ inner1 + + // nextHash = hash(uint32(x>>16), shift) + SHRQ $8, R14 + MOVL R14, R11 + IMULL $0x1e35a7bd, R11 + SHRL CX, R11 + + // s++ + ADDQ $1, SI + + // break out of the inner1 for loop, i.e. continue the outer loop. + JMP outer + +emitRemainder: + // if nextEmit < len(src) { etc } + MOVQ src_len+32(FP), AX + ADDQ DX, AX + CMPQ R10, AX + JEQ encodeBlockEnd + + // d += emitLiteral(dst[d:], src[nextEmit:]) + // + // Push args. + MOVQ DI, 0(SP) + MOVQ $0, 8(SP) // Unnecessary, as the callee ignores it, but conservative. + MOVQ $0, 16(SP) // Unnecessary, as the callee ignores it, but conservative. + MOVQ R10, 24(SP) + SUBQ R10, AX + MOVQ AX, 32(SP) + MOVQ AX, 40(SP) // Unnecessary, as the callee ignores it, but conservative. + + // Spill local variables (registers) onto the stack; call; unspill. + MOVQ DI, 80(SP) + CALL ·emitLiteral(SB) + MOVQ 80(SP), DI + + // Finish the "d +=" part of "d += emitLiteral(etc)". + ADDQ 48(SP), DI + +encodeBlockEnd: + MOVQ dst_base+0(FP), AX + SUBQ AX, DI + MOVQ DI, d+48(FP) + RET diff --git a/vendor/github.com/golang/snappy/encode_other.go b/vendor/github.com/golang/snappy/encode_other.go new file mode 100644 index 00000000000..dbcae905e6e --- /dev/null +++ b/vendor/github.com/golang/snappy/encode_other.go @@ -0,0 +1,238 @@ +// Copyright 2016 The Snappy-Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// +build !amd64 appengine !gc noasm + +package snappy + +func load32(b []byte, i int) uint32 { + b = b[i : i+4 : len(b)] // Help the compiler eliminate bounds checks on the next line. + return uint32(b[0]) | uint32(b[1])<<8 | uint32(b[2])<<16 | uint32(b[3])<<24 +} + +func load64(b []byte, i int) uint64 { + b = b[i : i+8 : len(b)] // Help the compiler eliminate bounds checks on the next line. + return uint64(b[0]) | uint64(b[1])<<8 | uint64(b[2])<<16 | uint64(b[3])<<24 | + uint64(b[4])<<32 | uint64(b[5])<<40 | uint64(b[6])<<48 | uint64(b[7])<<56 +} + +// emitLiteral writes a literal chunk and returns the number of bytes written. +// +// It assumes that: +// dst is long enough to hold the encoded bytes +// 1 <= len(lit) && len(lit) <= 65536 +func emitLiteral(dst, lit []byte) int { + i, n := 0, uint(len(lit)-1) + switch { + case n < 60: + dst[0] = uint8(n)<<2 | tagLiteral + i = 1 + case n < 1<<8: + dst[0] = 60<<2 | tagLiteral + dst[1] = uint8(n) + i = 2 + default: + dst[0] = 61<<2 | tagLiteral + dst[1] = uint8(n) + dst[2] = uint8(n >> 8) + i = 3 + } + return i + copy(dst[i:], lit) +} + +// emitCopy writes a copy chunk and returns the number of bytes written. +// +// It assumes that: +// dst is long enough to hold the encoded bytes +// 1 <= offset && offset <= 65535 +// 4 <= length && length <= 65535 +func emitCopy(dst []byte, offset, length int) int { + i := 0 + // The maximum length for a single tagCopy1 or tagCopy2 op is 64 bytes. The + // threshold for this loop is a little higher (at 68 = 64 + 4), and the + // length emitted down below is is a little lower (at 60 = 64 - 4), because + // it's shorter to encode a length 67 copy as a length 60 tagCopy2 followed + // by a length 7 tagCopy1 (which encodes as 3+2 bytes) than to encode it as + // a length 64 tagCopy2 followed by a length 3 tagCopy2 (which encodes as + // 3+3 bytes). The magic 4 in the 64±4 is because the minimum length for a + // tagCopy1 op is 4 bytes, which is why a length 3 copy has to be an + // encodes-as-3-bytes tagCopy2 instead of an encodes-as-2-bytes tagCopy1. + for length >= 68 { + // Emit a length 64 copy, encoded as 3 bytes. + dst[i+0] = 63<<2 | tagCopy2 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + i += 3 + length -= 64 + } + if length > 64 { + // Emit a length 60 copy, encoded as 3 bytes. + dst[i+0] = 59<<2 | tagCopy2 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + i += 3 + length -= 60 + } + if length >= 12 || offset >= 2048 { + // Emit the remaining copy, encoded as 3 bytes. + dst[i+0] = uint8(length-1)<<2 | tagCopy2 + dst[i+1] = uint8(offset) + dst[i+2] = uint8(offset >> 8) + return i + 3 + } + // Emit the remaining copy, encoded as 2 bytes. + dst[i+0] = uint8(offset>>8)<<5 | uint8(length-4)<<2 | tagCopy1 + dst[i+1] = uint8(offset) + return i + 2 +} + +// extendMatch returns the largest k such that k <= len(src) and that +// src[i:i+k-j] and src[j:k] have the same contents. +// +// It assumes that: +// 0 <= i && i < j && j <= len(src) +func extendMatch(src []byte, i, j int) int { + for ; j < len(src) && src[i] == src[j]; i, j = i+1, j+1 { + } + return j +} + +func hash(u, shift uint32) uint32 { + return (u * 0x1e35a7bd) >> shift +} + +// encodeBlock encodes a non-empty src to a guaranteed-large-enough dst. It +// assumes that the varint-encoded length of the decompressed bytes has already +// been written. +// +// It also assumes that: +// len(dst) >= MaxEncodedLen(len(src)) && +// minNonLiteralBlockSize <= len(src) && len(src) <= maxBlockSize +func encodeBlock(dst, src []byte) (d int) { + // Initialize the hash table. Its size ranges from 1<<8 to 1<<14 inclusive. + // The table element type is uint16, as s < sLimit and sLimit < len(src) + // and len(src) <= maxBlockSize and maxBlockSize == 65536. + const ( + maxTableSize = 1 << 14 + // tableMask is redundant, but helps the compiler eliminate bounds + // checks. + tableMask = maxTableSize - 1 + ) + shift := uint32(32 - 8) + for tableSize := 1 << 8; tableSize < maxTableSize && tableSize < len(src); tableSize *= 2 { + shift-- + } + // In Go, all array elements are zero-initialized, so there is no advantage + // to a smaller tableSize per se. However, it matches the C++ algorithm, + // and in the asm versions of this code, we can get away with zeroing only + // the first tableSize elements. + var table [maxTableSize]uint16 + + // sLimit is when to stop looking for offset/length copies. The inputMargin + // lets us use a fast path for emitLiteral in the main loop, while we are + // looking for copies. + sLimit := len(src) - inputMargin + + // nextEmit is where in src the next emitLiteral should start from. + nextEmit := 0 + + // The encoded form must start with a literal, as there are no previous + // bytes to copy, so we start looking for hash matches at s == 1. + s := 1 + nextHash := hash(load32(src, s), shift) + + for { + // Copied from the C++ snappy implementation: + // + // Heuristic match skipping: If 32 bytes are scanned with no matches + // found, start looking only at every other byte. If 32 more bytes are + // scanned (or skipped), look at every third byte, etc.. When a match + // is found, immediately go back to looking at every byte. This is a + // small loss (~5% performance, ~0.1% density) for compressible data + // due to more bookkeeping, but for non-compressible data (such as + // JPEG) it's a huge win since the compressor quickly "realizes" the + // data is incompressible and doesn't bother looking for matches + // everywhere. + // + // The "skip" variable keeps track of how many bytes there are since + // the last match; dividing it by 32 (ie. right-shifting by five) gives + // the number of bytes to move ahead for each iteration. + skip := 32 + + nextS := s + candidate := 0 + for { + s = nextS + bytesBetweenHashLookups := skip >> 5 + nextS = s + bytesBetweenHashLookups + skip += bytesBetweenHashLookups + if nextS > sLimit { + goto emitRemainder + } + candidate = int(table[nextHash&tableMask]) + table[nextHash&tableMask] = uint16(s) + nextHash = hash(load32(src, nextS), shift) + if load32(src, s) == load32(src, candidate) { + break + } + } + + // A 4-byte match has been found. We'll later see if more than 4 bytes + // match. But, prior to the match, src[nextEmit:s] are unmatched. Emit + // them as literal bytes. + d += emitLiteral(dst[d:], src[nextEmit:s]) + + // Call emitCopy, and then see if another emitCopy could be our next + // move. Repeat until we find no match for the input immediately after + // what was consumed by the last emitCopy call. + // + // If we exit this loop normally then we need to call emitLiteral next, + // though we don't yet know how big the literal will be. We handle that + // by proceeding to the next iteration of the main loop. We also can + // exit this loop via goto if we get close to exhausting the input. + for { + // Invariant: we have a 4-byte match at s, and no need to emit any + // literal bytes prior to s. + base := s + + // Extend the 4-byte match as long as possible. + // + // This is an inlined version of: + // s = extendMatch(src, candidate+4, s+4) + s += 4 + for i := candidate + 4; s < len(src) && src[i] == src[s]; i, s = i+1, s+1 { + } + + d += emitCopy(dst[d:], base-candidate, s-base) + nextEmit = s + if s >= sLimit { + goto emitRemainder + } + + // We could immediately start working at s now, but to improve + // compression we first update the hash table at s-1 and at s. If + // another emitCopy is not our next move, also calculate nextHash + // at s+1. At least on GOARCH=amd64, these three hash calculations + // are faster as one load64 call (with some shifts) instead of + // three load32 calls. + x := load64(src, s-1) + prevHash := hash(uint32(x>>0), shift) + table[prevHash&tableMask] = uint16(s - 1) + currHash := hash(uint32(x>>8), shift) + candidate = int(table[currHash&tableMask]) + table[currHash&tableMask] = uint16(s) + if uint32(x>>8) != load32(src, candidate) { + nextHash = hash(uint32(x>>16), shift) + s++ + break + } + } + } + +emitRemainder: + if nextEmit < len(src) { + d += emitLiteral(dst[d:], src[nextEmit:]) + } + return d +} diff --git a/vendor/github.com/golang/snappy/snappy.go b/vendor/github.com/golang/snappy/snappy.go new file mode 100644 index 00000000000..ece692ea461 --- /dev/null +++ b/vendor/github.com/golang/snappy/snappy.go @@ -0,0 +1,98 @@ +// Copyright 2011 The Snappy-Go 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 snappy implements the Snappy compression format. It aims for very +// high speeds and reasonable compression. +// +// There are actually two Snappy formats: block and stream. They are related, +// but different: trying to decompress block-compressed data as a Snappy stream +// will fail, and vice versa. The block format is the Decode and Encode +// functions and the stream format is the Reader and Writer types. +// +// The block format, the more common case, is used when the complete size (the +// number of bytes) of the original data is known upfront, at the time +// compression starts. The stream format, also known as the framing format, is +// for when that isn't always true. +// +// The canonical, C++ implementation is at https://github.com/google/snappy and +// it only implements the block format. +package snappy // import "github.com/golang/snappy" + +import ( + "hash/crc32" +) + +/* +Each encoded block begins with the varint-encoded length of the decoded data, +followed by a sequence of chunks. Chunks begin and end on byte boundaries. The +first byte of each chunk is broken into its 2 least and 6 most significant bits +called l and m: l ranges in [0, 4) and m ranges in [0, 64). l is the chunk tag. +Zero means a literal tag. All other values mean a copy tag. + +For literal tags: + - If m < 60, the next 1 + m bytes are literal bytes. + - Otherwise, let n be the little-endian unsigned integer denoted by the next + m - 59 bytes. The next 1 + n bytes after that are literal bytes. + +For copy tags, length bytes are copied from offset bytes ago, in the style of +Lempel-Ziv compression algorithms. In particular: + - For l == 1, the offset ranges in [0, 1<<11) and the length in [4, 12). + The length is 4 + the low 3 bits of m. The high 3 bits of m form bits 8-10 + of the offset. The next byte is bits 0-7 of the offset. + - For l == 2, the offset ranges in [0, 1<<16) and the length in [1, 65). + The length is 1 + m. The offset is the little-endian unsigned integer + denoted by the next 2 bytes. + - For l == 3, this tag is a legacy format that is no longer issued by most + encoders. Nonetheless, the offset ranges in [0, 1<<32) and the length in + [1, 65). The length is 1 + m. The offset is the little-endian unsigned + integer denoted by the next 4 bytes. +*/ +const ( + tagLiteral = 0x00 + tagCopy1 = 0x01 + tagCopy2 = 0x02 + tagCopy4 = 0x03 +) + +const ( + checksumSize = 4 + chunkHeaderSize = 4 + magicChunk = "\xff\x06\x00\x00" + magicBody + magicBody = "sNaPpY" + + // maxBlockSize is the maximum size of the input to encodeBlock. It is not + // part of the wire format per se, but some parts of the encoder assume + // that an offset fits into a uint16. + // + // Also, for the framing format (Writer type instead of Encode function), + // https://github.com/google/snappy/blob/master/framing_format.txt says + // that "the uncompressed data in a chunk must be no longer than 65536 + // bytes". + maxBlockSize = 65536 + + // maxEncodedLenOfMaxBlockSize equals MaxEncodedLen(maxBlockSize), but is + // hard coded to be a const instead of a variable, so that obufLen can also + // be a const. Their equivalence is confirmed by + // TestMaxEncodedLenOfMaxBlockSize. + maxEncodedLenOfMaxBlockSize = 76490 + + obufHeaderLen = len(magicChunk) + checksumSize + chunkHeaderSize + obufLen = obufHeaderLen + maxEncodedLenOfMaxBlockSize +) + +const ( + chunkTypeCompressedData = 0x00 + chunkTypeUncompressedData = 0x01 + chunkTypePadding = 0xfe + chunkTypeStreamIdentifier = 0xff +) + +var crcTable = crc32.MakeTable(crc32.Castagnoli) + +// crc implements the checksum specified in section 3 of +// https://github.com/google/snappy/blob/master/framing_format.txt +func crc(b []byte) uint32 { + c := crc32.Update(0, crcTable, b) + return uint32(c>>15|c<<17) + 0xa282ead8 +} diff --git a/vendor/github.com/pierrec/lz4/LICENSE b/vendor/github.com/pierrec/lz4/LICENSE new file mode 100644 index 00000000000..bd899d8353d --- /dev/null +++ b/vendor/github.com/pierrec/lz4/LICENSE @@ -0,0 +1,28 @@ +Copyright (c) 2015, Pierre Curto +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of xxHash nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + diff --git a/vendor/github.com/pierrec/lz4/block.go b/vendor/github.com/pierrec/lz4/block.go new file mode 100644 index 00000000000..00b1111b92b --- /dev/null +++ b/vendor/github.com/pierrec/lz4/block.go @@ -0,0 +1,397 @@ +package lz4 + +import ( + "encoding/binary" + "errors" +) + +var ( + // ErrInvalidSourceShortBuffer is returned by UncompressBlock or CompressBLock when a compressed + // block is corrupted or the destination buffer is not large enough for the uncompressed data. + ErrInvalidSourceShortBuffer = errors.New("lz4: invalid source or destination buffer too short") + // ErrInvalid is returned when reading an invalid LZ4 archive. + ErrInvalid = errors.New("lz4: bad magic number") +) + +// blockHash hashes 4 bytes into a value < winSize. +func blockHash(x uint32) uint32 { + const hasher uint32 = 2654435761 // Knuth multiplicative hash. + return x * hasher >> hashShift +} + +// CompressBlockBound returns the maximum size of a given buffer of size n, when not compressible. +func CompressBlockBound(n int) int { + return n + n/255 + 16 +} + +// UncompressBlock uncompresses the source buffer into the destination one, +// and returns the uncompressed size. +// +// The destination buffer must be sized appropriately. +// +// An error is returned if the source data is invalid or the destination buffer is too small. +func UncompressBlock(src, dst []byte) (si int, err error) { + defer func() { + // It is now faster to let the runtime panic and recover on out of bound slice access + // than checking indices as we go along. + if recover() != nil { + err = ErrInvalidSourceShortBuffer + } + }() + sn := len(src) + if sn == 0 { + return 0, nil + } + var di int + + for { + // Literals and match lengths (token). + b := int(src[si]) + si++ + + // Literals. + if lLen := b >> 4; lLen > 0 { + if lLen == 0xF { + for src[si] == 0xFF { + lLen += 0xFF + si++ + } + lLen += int(src[si]) + si++ + } + i := si + si += lLen + di += copy(dst[di:], src[i:si]) + + if si >= sn { + return di, nil + } + } + + si++ + _ = src[si] // Bound check elimination. + offset := int(src[si-1]) | int(src[si])<<8 + si++ + + // Match. + mLen := b & 0xF + if mLen == 0xF { + for src[si] == 0xFF { + mLen += 0xFF + si++ + } + mLen += int(src[si]) + si++ + } + mLen += minMatch + + // Copy the match. + i := di - offset + if offset > 0 && mLen >= offset { + // Efficiently copy the match dst[di-offset:di] into the dst slice. + bytesToCopy := offset * (mLen / offset) + expanded := dst[i:] + for n := offset; n <= bytesToCopy+offset; n *= 2 { + copy(expanded[n:], expanded[:n]) + } + di += bytesToCopy + mLen -= bytesToCopy + } + di += copy(dst[di:], dst[i:i+mLen]) + } +} + +// CompressBlock compresses the source buffer into the destination one. +// This is the fast version of LZ4 compression and also the default one. +// The size of hashTable must be at least 64Kb. +// +// The size of the compressed data is returned. If it is 0 and no error, then the data is incompressible. +// +// An error is returned if the destination buffer is too small. +func CompressBlock(src, dst []byte, hashTable []int) (di int, err error) { + defer func() { + if recover() != nil { + err = ErrInvalidSourceShortBuffer + } + }() + + sn, dn := len(src)-mfLimit, len(dst) + if sn <= 0 || dn == 0 { + return 0, nil + } + var si int + + // Fast scan strategy: the hash table only stores the last 4 bytes sequences. + // const accInit = 1 << skipStrength + + anchor := si // Position of the current literals. + // acc := accInit // Variable step: improves performance on non-compressible data. + + for si < sn { + // Hash the next 4 bytes (sequence)... + match := binary.LittleEndian.Uint32(src[si:]) + h := blockHash(match) + + ref := hashTable[h] + hashTable[h] = si + if ref >= sn { // Invalid reference (dirty hashtable). + si++ + continue + } + offset := si - ref + if offset <= 0 || offset >= winSize || // Out of window. + match != binary.LittleEndian.Uint32(src[ref:]) { // Hash collision on different matches. + // si += acc >> skipStrength + // acc++ + si++ + continue + } + + // Match found. + // acc = accInit + lLen := si - anchor // Literal length. + + // Encode match length part 1. + si += minMatch + mLen := si // Match length has minMatch already. + // Find the longest match, first looking by batches of 8 bytes. + for si < sn && binary.LittleEndian.Uint64(src[si:]) == binary.LittleEndian.Uint64(src[si-offset:]) { + si += 8 + } + // Then byte by byte. + for si < sn && src[si] == src[si-offset] { + si++ + } + + mLen = si - mLen + if mLen < 0xF { + dst[di] = byte(mLen) + } else { + dst[di] = 0xF + } + + // Encode literals length. + if lLen < 0xF { + dst[di] |= byte(lLen << 4) + } else { + dst[di] |= 0xF0 + di++ + l := lLen - 0xF + for ; l >= 0xFF; l -= 0xFF { + dst[di] = 0xFF + di++ + } + dst[di] = byte(l) + } + di++ + + // Literals. + copy(dst[di:], src[anchor:anchor+lLen]) + di += lLen + 2 + anchor = si + + // Encode offset. + _ = dst[di] // Bound check elimination. + dst[di-2], dst[di-1] = byte(offset), byte(offset>>8) + + // Encode match length part 2. + if mLen >= 0xF { + for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF { + dst[di] = 0xFF + di++ + } + dst[di] = byte(mLen) + di++ + } + } + + if anchor == 0 { + // Incompressible. + return 0, nil + } + + // Last literals. + lLen := len(src) - anchor + if lLen < 0xF { + dst[di] = byte(lLen << 4) + } else { + dst[di] = 0xF0 + di++ + for lLen -= 0xF; lLen >= 0xFF; lLen -= 0xFF { + dst[di] = 0xFF + di++ + } + dst[di] = byte(lLen) + } + di++ + + // Write the last literals. + if di >= anchor { + // Incompressible. + return 0, nil + } + di += copy(dst[di:], src[anchor:]) + return di, nil +} + +// CompressBlockHC compresses the source buffer src into the destination dst +// with max search depth (use 0 or negative value for no max). +// +// CompressBlockHC compression ratio is better than CompressBlock but it is also slower. +// +// The size of the compressed data is returned. If it is 0 and no error, then the data is not compressible. +// +// An error is returned if the destination buffer is too small. +func CompressBlockHC(src, dst []byte, depth int) (di int, err error) { + defer func() { + if recover() != nil { + err = ErrInvalidSourceShortBuffer + } + }() + + sn, dn := len(src)-mfLimit, len(dst) + if sn <= 0 || dn == 0 { + return 0, nil + } + var si int + + // hashTable: stores the last position found for a given hash + // chaingTable: stores previous positions for a given hash + var hashTable, chainTable [winSize]int + + if depth <= 0 { + depth = winSize + } + + anchor := si + for si < sn { + // Hash the next 4 bytes (sequence). + match := binary.LittleEndian.Uint32(src[si:]) + h := blockHash(match) + + // Follow the chain until out of window and give the longest match. + mLen := 0 + offset := 0 + for next, try := hashTable[h], depth; try > 0 && next > 0 && si-next < winSize; next = chainTable[next&winMask] { + // The first (mLen==0) or next byte (mLen>=minMatch) at current match length + // must match to improve on the match length. + if src[next+mLen] != src[si+mLen] { + continue + } + ml := 0 + // Compare the current position with a previous with the same hash. + for ml < sn-si && binary.LittleEndian.Uint64(src[next+ml:]) == binary.LittleEndian.Uint64(src[si+ml:]) { + ml += 8 + } + for ml < sn-si && src[next+ml] == src[si+ml] { + ml++ + } + if ml < minMatch || ml <= mLen { + // Match too small ( winStart { + winStart = ws + } + for si, ml := winStart, si+mLen; si < ml; { + match >>= 8 + match |= uint32(src[si+3]) << 24 + h := blockHash(match) + chainTable[si&winMask] = hashTable[h] + hashTable[h] = si + si++ + } + + lLen := si - anchor + si += mLen + mLen -= minMatch // Match length does not include minMatch. + + if mLen < 0xF { + dst[di] = byte(mLen) + } else { + dst[di] = 0xF + } + + // Encode literals length. + if lLen < 0xF { + dst[di] |= byte(lLen << 4) + } else { + dst[di] |= 0xF0 + di++ + l := lLen - 0xF + for ; l >= 0xFF; l -= 0xFF { + dst[di] = 0xFF + di++ + } + dst[di] = byte(l) + } + di++ + + // Literals. + copy(dst[di:], src[anchor:anchor+lLen]) + di += lLen + anchor = si + + // Encode offset. + di += 2 + dst[di-2], dst[di-1] = byte(offset), byte(offset>>8) + + // Encode match length part 2. + if mLen >= 0xF { + for mLen -= 0xF; mLen >= 0xFF; mLen -= 0xFF { + dst[di] = 0xFF + di++ + } + dst[di] = byte(mLen) + di++ + } + } + + if anchor == 0 { + // Incompressible. + return 0, nil + } + + // Last literals. + lLen := len(src) - anchor + if lLen < 0xF { + dst[di] = byte(lLen << 4) + } else { + dst[di] = 0xF0 + di++ + lLen -= 0xF + for ; lLen >= 0xFF; lLen -= 0xFF { + dst[di] = 0xFF + di++ + } + dst[di] = byte(lLen) + } + di++ + + // Write the last literals. + if di >= anchor { + // Incompressible. + return 0, nil + } + di += copy(dst[di:], src[anchor:]) + return di, nil +} diff --git a/vendor/github.com/pierrec/lz4/debug.go b/vendor/github.com/pierrec/lz4/debug.go new file mode 100644 index 00000000000..bc5e78d40f0 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/debug.go @@ -0,0 +1,23 @@ +// +build lz4debug + +package lz4 + +import ( + "fmt" + "os" + "path/filepath" + "runtime" +) + +const debugFlag = true + +func debug(args ...interface{}) { + _, file, line, _ := runtime.Caller(1) + file = filepath.Base(file) + + f := fmt.Sprintf("LZ4: %s:%d %s", file, line, args[0]) + if f[len(f)-1] != '\n' { + f += "\n" + } + fmt.Fprintf(os.Stderr, f, args[1:]...) +} diff --git a/vendor/github.com/pierrec/lz4/debug_stub.go b/vendor/github.com/pierrec/lz4/debug_stub.go new file mode 100644 index 00000000000..44211ad9645 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/debug_stub.go @@ -0,0 +1,7 @@ +// +build !lz4debug + +package lz4 + +const debugFlag = false + +func debug(args ...interface{}) {} diff --git a/vendor/github.com/pierrec/lz4/internal/xxh32/xxh32zero.go b/vendor/github.com/pierrec/lz4/internal/xxh32/xxh32zero.go new file mode 100644 index 00000000000..850a6fdf614 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/internal/xxh32/xxh32zero.go @@ -0,0 +1,222 @@ +// Package xxh32 implements the very fast XXH hashing algorithm (32 bits version). +// (https://github.com/Cyan4973/XXH/) +package xxh32 + +import ( + "encoding/binary" +) + +const ( + prime32_1 uint32 = 2654435761 + prime32_2 uint32 = 2246822519 + prime32_3 uint32 = 3266489917 + prime32_4 uint32 = 668265263 + prime32_5 uint32 = 374761393 + + prime32_1plus2 uint32 = 606290984 + prime32_minus1 uint32 = 1640531535 +) + +// XXHZero represents an xxhash32 object with seed 0. +type XXHZero struct { + v1 uint32 + v2 uint32 + v3 uint32 + v4 uint32 + totalLen uint64 + buf [16]byte + bufused int +} + +// Sum appends the current hash to b and returns the resulting slice. +// It does not change the underlying hash state. +func (xxh XXHZero) Sum(b []byte) []byte { + h32 := xxh.Sum32() + return append(b, byte(h32), byte(h32>>8), byte(h32>>16), byte(h32>>24)) +} + +// Reset resets the Hash to its initial state. +func (xxh *XXHZero) Reset() { + xxh.v1 = prime32_1plus2 + xxh.v2 = prime32_2 + xxh.v3 = 0 + xxh.v4 = prime32_minus1 + xxh.totalLen = 0 + xxh.bufused = 0 +} + +// Size returns the number of bytes returned by Sum(). +func (xxh *XXHZero) Size() int { + return 4 +} + +// BlockSize gives the minimum number of bytes accepted by Write(). +func (xxh *XXHZero) BlockSize() int { + return 1 +} + +// Write adds input bytes to the Hash. +// It never returns an error. +func (xxh *XXHZero) Write(input []byte) (int, error) { + if xxh.totalLen == 0 { + xxh.Reset() + } + n := len(input) + m := xxh.bufused + + xxh.totalLen += uint64(n) + + r := len(xxh.buf) - m + if n < r { + copy(xxh.buf[m:], input) + xxh.bufused += len(input) + return n, nil + } + + p := 0 + // Causes compiler to work directly from registers instead of stack: + v1, v2, v3, v4 := xxh.v1, xxh.v2, xxh.v3, xxh.v4 + if m > 0 { + // some data left from previous update + copy(xxh.buf[xxh.bufused:], input[:r]) + xxh.bufused += len(input) - r + + // fast rotl(13) + buf := xxh.buf[:16] // BCE hint. + v1 = rol13(v1+binary.LittleEndian.Uint32(buf[:])*prime32_2) * prime32_1 + v2 = rol13(v2+binary.LittleEndian.Uint32(buf[4:])*prime32_2) * prime32_1 + v3 = rol13(v3+binary.LittleEndian.Uint32(buf[8:])*prime32_2) * prime32_1 + v4 = rol13(v4+binary.LittleEndian.Uint32(buf[12:])*prime32_2) * prime32_1 + p = r + xxh.bufused = 0 + } + + for n := n - 16; p <= n; p += 16 { + sub := input[p:][:16] //BCE hint for compiler + v1 = rol13(v1+binary.LittleEndian.Uint32(sub[:])*prime32_2) * prime32_1 + v2 = rol13(v2+binary.LittleEndian.Uint32(sub[4:])*prime32_2) * prime32_1 + v3 = rol13(v3+binary.LittleEndian.Uint32(sub[8:])*prime32_2) * prime32_1 + v4 = rol13(v4+binary.LittleEndian.Uint32(sub[12:])*prime32_2) * prime32_1 + } + xxh.v1, xxh.v2, xxh.v3, xxh.v4 = v1, v2, v3, v4 + + copy(xxh.buf[xxh.bufused:], input[p:]) + xxh.bufused += len(input) - p + + return n, nil +} + +// Sum32 returns the 32 bits Hash value. +func (xxh *XXHZero) Sum32() uint32 { + h32 := uint32(xxh.totalLen) + if h32 >= 16 { + h32 += rol1(xxh.v1) + rol7(xxh.v2) + rol12(xxh.v3) + rol18(xxh.v4) + } else { + h32 += prime32_5 + } + + p := 0 + n := xxh.bufused + buf := xxh.buf + for n := n - 4; p <= n; p += 4 { + h32 += binary.LittleEndian.Uint32(buf[p:p+4]) * prime32_3 + h32 = rol17(h32) * prime32_4 + } + for ; p < n; p++ { + h32 += uint32(buf[p]) * prime32_5 + h32 = rol11(h32) * prime32_1 + } + + h32 ^= h32 >> 15 + h32 *= prime32_2 + h32 ^= h32 >> 13 + h32 *= prime32_3 + h32 ^= h32 >> 16 + + return h32 +} + +// ChecksumZero returns the 32bits Hash value. +func ChecksumZero(input []byte) uint32 { + n := len(input) + h32 := uint32(n) + + if n < 16 { + h32 += prime32_5 + } else { + v1 := prime32_1plus2 + v2 := prime32_2 + v3 := uint32(0) + v4 := prime32_minus1 + p := 0 + for n := n - 16; p <= n; p += 16 { + sub := input[p:][:16] //BCE hint for compiler + v1 = rol13(v1+binary.LittleEndian.Uint32(sub[:])*prime32_2) * prime32_1 + v2 = rol13(v2+binary.LittleEndian.Uint32(sub[4:])*prime32_2) * prime32_1 + v3 = rol13(v3+binary.LittleEndian.Uint32(sub[8:])*prime32_2) * prime32_1 + v4 = rol13(v4+binary.LittleEndian.Uint32(sub[12:])*prime32_2) * prime32_1 + } + input = input[p:] + n -= p + h32 += rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) + } + + p := 0 + for n := n - 4; p <= n; p += 4 { + h32 += binary.LittleEndian.Uint32(input[p:p+4]) * prime32_3 + h32 = rol17(h32) * prime32_4 + } + for p < n { + h32 += uint32(input[p]) * prime32_5 + h32 = rol11(h32) * prime32_1 + p++ + } + + h32 ^= h32 >> 15 + h32 *= prime32_2 + h32 ^= h32 >> 13 + h32 *= prime32_3 + h32 ^= h32 >> 16 + + return h32 +} + +// Uint32Zero hashes x with seed 0. +func Uint32Zero(x uint32) uint32 { + h := prime32_5 + 4 + x*prime32_3 + h = rol17(h) * prime32_4 + h ^= h >> 15 + h *= prime32_2 + h ^= h >> 13 + h *= prime32_3 + h ^= h >> 16 + return h +} + +func rol1(u uint32) uint32 { + return u<<1 | u>>31 +} + +func rol7(u uint32) uint32 { + return u<<7 | u>>25 +} + +func rol11(u uint32) uint32 { + return u<<11 | u>>21 +} + +func rol12(u uint32) uint32 { + return u<<12 | u>>20 +} + +func rol13(u uint32) uint32 { + return u<<13 | u>>19 +} + +func rol17(u uint32) uint32 { + return u<<17 | u>>15 +} + +func rol18(u uint32) uint32 { + return u<<18 | u>>14 +} diff --git a/vendor/github.com/pierrec/lz4/lz4.go b/vendor/github.com/pierrec/lz4/lz4.go new file mode 100644 index 00000000000..35802756c48 --- /dev/null +++ b/vendor/github.com/pierrec/lz4/lz4.go @@ -0,0 +1,68 @@ +// Package lz4 implements reading and writing lz4 compressed data (a frame), +// as specified in http://fastcompression.blogspot.fr/2013/04/lz4-streaming-format-final.html. +// +// Although the block level compression and decompression functions are exposed and are fully compatible +// with the lz4 block format definition, they are low level and should not be used directly. +// For a complete description of an lz4 compressed block, see: +// http://fastcompression.blogspot.fr/2011/05/lz4-explained.html +// +// See https://github.com/Cyan4973/lz4 for the reference C implementation. +// +package lz4 + +const ( + // Extension is the LZ4 frame file name extension + Extension = ".lz4" + // Version is the LZ4 frame format version + Version = 1 + + frameMagic uint32 = 0x184D2204 + frameSkipMagic uint32 = 0x184D2A50 + + // The following constants are used to setup the compression algorithm. + minMatch = 4 // the minimum size of the match sequence size (4 bytes) + winSizeLog = 16 // LZ4 64Kb window size limit + winSize = 1 << winSizeLog + winMask = winSize - 1 // 64Kb window of previous data for dependent blocks + compressedBlockFlag = 1 << 31 + compressedBlockMask = compressedBlockFlag - 1 + + // hashLog determines the size of the hash table used to quickly find a previous match position. + // Its value influences the compression speed and memory usage, the lower the faster, + // but at the expense of the compression ratio. + // 16 seems to be the best compromise. + hashLog = 16 + hashTableSize = 1 << hashLog + hashShift = uint((minMatch * 8) - hashLog) + + mfLimit = 8 + minMatch // The last match cannot start within the last 12 bytes. + skipStrength = 6 // variable step for fast scan +) + +// map the block max size id with its value in bytes: 64Kb, 256Kb, 1Mb and 4Mb. +var ( + bsMapID = map[byte]int{4: 64 << 10, 5: 256 << 10, 6: 1 << 20, 7: 4 << 20} + bsMapValue = make(map[int]byte, len(bsMapID)) +) + +// Reversed. +func init() { + for i, v := range bsMapID { + bsMapValue[v] = i + } +} + +// Header describes the various flags that can be set on a Writer or obtained from a Reader. +// The default values match those of the LZ4 frame format definition +// (http://fastcompression.blogspot.com/2013/04/lz4-streaming-format-final.html). +// +// NB. in a Reader, in case of concatenated frames, the Header values may change between Read() calls. +// It is the caller responsibility to check them if necessary. +type Header struct { + BlockChecksum bool // Compressed blocks checksum flag. + NoChecksum bool // Frame checksum flag. + BlockMaxSize int // Size of the uncompressed data block (one of [64KB, 256KB, 1MB, 4MB]). Default=4MB. + Size uint64 // Frame total size. It is _not_ computed by the Writer. + CompressionLevel int // Compression level (higher is better, use 0 for fastest compression). + done bool // Header processed flag (Read or Write and checked). +} diff --git a/vendor/github.com/pierrec/lz4/lz4_go1.10.go b/vendor/github.com/pierrec/lz4/lz4_go1.10.go new file mode 100644 index 00000000000..9a0fb00709d --- /dev/null +++ b/vendor/github.com/pierrec/lz4/lz4_go1.10.go @@ -0,0 +1,29 @@ +//+build go1.10 + +package lz4 + +import ( + "fmt" + "strings" +) + +func (h Header) String() string { + var s strings.Builder + + s.WriteString(fmt.Sprintf("%T{", h)) + if h.BlockChecksum { + s.WriteString("BlockChecksum: true ") + } + if h.NoChecksum { + s.WriteString("NoChecksum: true ") + } + if bs := h.BlockMaxSize; bs != 0 && bs != 4<<20 { + s.WriteString(fmt.Sprintf("BlockMaxSize: %d ", bs)) + } + if l := h.CompressionLevel; l != 0 { + s.WriteString(fmt.Sprintf("CompressionLevel: %d ", l)) + } + s.WriteByte('}') + + return s.String() +} diff --git a/vendor/github.com/pierrec/lz4/lz4_notgo1.10.go b/vendor/github.com/pierrec/lz4/lz4_notgo1.10.go new file mode 100644 index 00000000000..12c761a2e7f --- /dev/null +++ b/vendor/github.com/pierrec/lz4/lz4_notgo1.10.go @@ -0,0 +1,29 @@ +//+build !go1.10 + +package lz4 + +import ( + "bytes" + "fmt" +) + +func (h Header) String() string { + var s bytes.Buffer + + s.WriteString(fmt.Sprintf("%T{", h)) + if h.BlockChecksum { + s.WriteString("BlockChecksum: true ") + } + if h.NoChecksum { + s.WriteString("NoChecksum: true ") + } + if bs := h.BlockMaxSize; bs != 0 && bs != 4<<20 { + s.WriteString(fmt.Sprintf("BlockMaxSize: %d ", bs)) + } + if l := h.CompressionLevel; l != 0 { + s.WriteString(fmt.Sprintf("CompressionLevel: %d ", l)) + } + s.WriteByte('}') + + return s.String() +} diff --git a/vendor/github.com/pierrec/lz4/reader.go b/vendor/github.com/pierrec/lz4/reader.go new file mode 100644 index 00000000000..f08db47df7b --- /dev/null +++ b/vendor/github.com/pierrec/lz4/reader.go @@ -0,0 +1,295 @@ +package lz4 + +import ( + "encoding/binary" + "fmt" + "io" + "io/ioutil" + + "github.com/pierrec/lz4/internal/xxh32" +) + +// Reader implements the LZ4 frame decoder. +// The Header is set after the first call to Read(). +// The Header may change between Read() calls in case of concatenated frames. +type Reader struct { + Header + + buf [8]byte // Scrap buffer. + pos int64 // Current position in src. + src io.Reader // Source. + zdata []byte // Compressed data. + data []byte // Uncompressed data. + idx int // Index of unread bytes into data. + checksum xxh32.XXHZero // Frame hash. +} + +// NewReader returns a new LZ4 frame decoder. +// No access to the underlying io.Reader is performed. +func NewReader(src io.Reader) *Reader { + r := &Reader{src: src} + return r +} + +// readHeader checks the frame magic number and parses the frame descriptoz. +// Skippable frames are supported even as a first frame although the LZ4 +// specifications recommends skippable frames not to be used as first frames. +func (z *Reader) readHeader(first bool) error { + defer z.checksum.Reset() + + buf := z.buf[:] + for { + magic, err := z.readUint32() + if err != nil { + z.pos += 4 + if !first && err == io.ErrUnexpectedEOF { + return io.EOF + } + return err + } + if magic == frameMagic { + break + } + if magic>>8 != frameSkipMagic>>8 { + return ErrInvalid + } + skipSize, err := z.readUint32() + if err != nil { + return err + } + z.pos += 4 + m, err := io.CopyN(ioutil.Discard, z.src, int64(skipSize)) + if err != nil { + return err + } + z.pos += m + } + + // Header. + if _, err := io.ReadFull(z.src, buf[:2]); err != nil { + return err + } + z.pos += 8 + + b := buf[0] + if v := b >> 6; v != Version { + return fmt.Errorf("lz4: invalid version: got %d; expected %d", v, Version) + } + if b>>5&1 == 0 { + return fmt.Errorf("lz4: block dependency not supported") + } + z.BlockChecksum = b>>4&1 > 0 + frameSize := b>>3&1 > 0 + z.NoChecksum = b>>2&1 == 0 + + bmsID := buf[1] >> 4 & 0x7 + bSize, ok := bsMapID[bmsID] + if !ok { + return fmt.Errorf("lz4: invalid block max size ID: %d", bmsID) + } + z.BlockMaxSize = bSize + + // Allocate the compressed/uncompressed buffers. + // The compressed buffer cannot exceed the uncompressed one. + if n := 2 * bSize; cap(z.zdata) < n { + z.zdata = make([]byte, n, n) + } + if debugFlag { + debug("header block max size id=%d size=%d", bmsID, bSize) + } + z.zdata = z.zdata[:bSize] + z.data = z.zdata[:cap(z.zdata)][bSize:] + z.idx = len(z.data) + + z.checksum.Write(buf[0:2]) + + if frameSize { + buf := buf[:8] + if _, err := io.ReadFull(z.src, buf); err != nil { + return err + } + z.Size = binary.LittleEndian.Uint64(buf) + z.pos += 8 + z.checksum.Write(buf) + } + + // Header checksum. + if _, err := io.ReadFull(z.src, buf[:1]); err != nil { + return err + } + z.pos++ + if h := byte(z.checksum.Sum32() >> 8 & 0xFF); h != buf[0] { + return fmt.Errorf("lz4: invalid header checksum: got %x; expected %x", buf[0], h) + } + + z.Header.done = true + if debugFlag { + debug("header read: %v", z.Header) + } + + return nil +} + +// Read decompresses data from the underlying source into the supplied buffer. +// +// Since there can be multiple streams concatenated, Header values may +// change between calls to Read(). If that is the case, no data is actually read from +// the underlying io.Reader, to allow for potential input buffer resizing. +func (z *Reader) Read(buf []byte) (int, error) { + if debugFlag { + debug("Read buf len=%d", len(buf)) + } + if !z.Header.done { + if err := z.readHeader(true); err != nil { + return 0, err + } + if debugFlag { + debug("header read OK compressed buffer %d / %d uncompressed buffer %d : %d index=%d", + len(z.zdata), cap(z.zdata), len(z.data), cap(z.data), z.idx) + } + } + + if len(buf) == 0 { + return 0, nil + } + + if z.idx == len(z.data) { + // No data ready for reading, process the next block. + if debugFlag { + debug("reading block from writer") + } + // Block length: 0 = end of frame, highest bit set: uncompressed. + bLen, err := z.readUint32() + if err != nil { + return 0, err + } + z.pos += 4 + + if bLen == 0 { + // End of frame reached. + if !z.NoChecksum { + // Validate the frame checksum. + checksum, err := z.readUint32() + if err != nil { + return 0, err + } + if debugFlag { + debug("frame checksum got=%x / want=%x", z.checksum.Sum32(), checksum) + } + z.pos += 4 + if h := z.checksum.Sum32(); checksum != h { + return 0, fmt.Errorf("lz4: invalid frame checksum: got %x; expected %x", h, checksum) + } + } + + // Get ready for the next concatenated frame and keep the position. + pos := z.pos + z.Reset(z.src) + z.pos = pos + + // Since multiple frames can be concatenated, check for more. + return 0, z.readHeader(false) + } + + if debugFlag { + debug("raw block size %d", bLen) + } + if bLen&compressedBlockFlag > 0 { + // Uncompressed block. + bLen &= compressedBlockMask + if debugFlag { + debug("uncompressed block size %d", bLen) + } + if int(bLen) > cap(z.data) { + return 0, fmt.Errorf("lz4: invalid block size: %d", bLen) + } + z.data = z.data[:bLen] + if _, err := io.ReadFull(z.src, z.data); err != nil { + return 0, err + } + z.pos += int64(bLen) + + if z.BlockChecksum { + checksum, err := z.readUint32() + if err != nil { + return 0, err + } + z.pos += 4 + + if h := xxh32.ChecksumZero(z.data); h != checksum { + return 0, fmt.Errorf("lz4: invalid block checksum: got %x; expected %x", h, checksum) + } + } + + } else { + // Compressed block. + if debugFlag { + debug("compressed block size %d", bLen) + } + if int(bLen) > cap(z.data) { + return 0, fmt.Errorf("lz4: invalid block size: %d", bLen) + } + zdata := z.zdata[:bLen] + if _, err := io.ReadFull(z.src, zdata); err != nil { + return 0, err + } + z.pos += int64(bLen) + + if z.BlockChecksum { + checksum, err := z.readUint32() + if err != nil { + return 0, err + } + z.pos += 4 + + if h := xxh32.ChecksumZero(zdata); h != checksum { + return 0, fmt.Errorf("lz4: invalid block checksum: got %x; expected %x", h, checksum) + } + } + + n, err := UncompressBlock(zdata, z.data) + if err != nil { + return 0, err + } + z.data = z.data[:n] + } + + if !z.NoChecksum { + z.checksum.Write(z.data) + if debugFlag { + debug("current frame checksum %x", z.checksum.Sum32()) + } + } + z.idx = 0 + } + + n := copy(buf, z.data[z.idx:]) + z.idx += n + if debugFlag { + debug("copied %d bytes to input", n) + } + + return n, nil +} + +// Reset discards the Reader's state and makes it equivalent to the +// result of its original state from NewReader, but reading from r instead. +// This permits reusing a Reader rather than allocating a new one. +func (z *Reader) Reset(r io.Reader) { + z.Header = Header{} + z.pos = 0 + z.src = r + z.zdata = z.zdata[:0] + z.data = z.data[:0] + z.idx = 0 + z.checksum.Reset() +} + +// readUint32 reads an uint32 into the supplied buffer. +// The idea is to make use of the already allocated buffers avoiding additional allocations. +func (z *Reader) readUint32() (uint32, error) { + buf := z.buf[:4] + _, err := io.ReadFull(z.src, buf) + x := binary.LittleEndian.Uint32(buf) + return x, err +} diff --git a/vendor/github.com/pierrec/lz4/writer.go b/vendor/github.com/pierrec/lz4/writer.go new file mode 100644 index 00000000000..0120438025d --- /dev/null +++ b/vendor/github.com/pierrec/lz4/writer.go @@ -0,0 +1,267 @@ +package lz4 + +import ( + "encoding/binary" + "fmt" + "io" + + "github.com/pierrec/lz4/internal/xxh32" +) + +// Writer implements the LZ4 frame encoder. +type Writer struct { + Header + + buf [19]byte // magic number(4) + header(flags(2)+[Size(8)+DictID(4)]+checksum(1)) does not exceed 19 bytes + dst io.Writer // Destination. + checksum xxh32.XXHZero // Frame checksum. + zdata []byte // Compressed data. + data []byte // Data to be compressed. + idx int // Index into data. + hashtable [winSize]int // Hash table used in CompressBlock(). +} + +// NewWriter returns a new LZ4 frame encoder. +// No access to the underlying io.Writer is performed. +// The supplied Header is checked at the first Write. +// It is ok to change it before the first Write but then not until a Reset() is performed. +func NewWriter(dst io.Writer) *Writer { + return &Writer{dst: dst} +} + +// writeHeader builds and writes the header (magic+header) to the underlying io.Writer. +func (z *Writer) writeHeader() error { + // Default to 4Mb if BlockMaxSize is not set. + if z.Header.BlockMaxSize == 0 { + z.Header.BlockMaxSize = bsMapID[7] + } + // The only option that needs to be validated. + bSize := z.Header.BlockMaxSize + bSizeID, ok := bsMapValue[bSize] + if !ok { + return fmt.Errorf("lz4: invalid block max size: %d", bSize) + } + // Allocate the compressed/uncompressed buffers. + // The compressed buffer cannot exceed the uncompressed one. + if n := 2 * bSize; cap(z.zdata) < n { + z.zdata = make([]byte, n, n) + } + z.zdata = z.zdata[:bSize] + z.data = z.zdata[:cap(z.zdata)][bSize:] + z.idx = 0 + + // Size is optional. + buf := z.buf[:] + + // Set the fixed size data: magic number, block max size and flags. + binary.LittleEndian.PutUint32(buf[0:], frameMagic) + flg := byte(Version << 6) + flg |= 1 << 5 // No block dependency. + if z.Header.BlockChecksum { + flg |= 1 << 4 + } + if z.Header.Size > 0 { + flg |= 1 << 3 + } + if !z.Header.NoChecksum { + flg |= 1 << 2 + } + buf[4] = flg + buf[5] = bSizeID << 4 + + // Current buffer size: magic(4) + flags(1) + block max size (1). + n := 6 + // Optional items. + if z.Header.Size > 0 { + binary.LittleEndian.PutUint64(buf[n:], z.Header.Size) + n += 8 + } + + // The header checksum includes the flags, block max size and optional Size. + buf[n] = byte(xxh32.ChecksumZero(buf[4:n]) >> 8 & 0xFF) + z.checksum.Reset() + + // Header ready, write it out. + if _, err := z.dst.Write(buf[0 : n+1]); err != nil { + return err + } + z.Header.done = true + if debugFlag { + debug("wrote header %v", z.Header) + } + + return nil +} + +// Write compresses data from the supplied buffer into the underlying io.Writer. +// Write does not return until the data has been written. +func (z *Writer) Write(buf []byte) (int, error) { + if !z.Header.done { + if err := z.writeHeader(); err != nil { + return 0, err + } + } + if debugFlag { + debug("input buffer len=%d index=%d", len(buf), z.idx) + } + + zn := len(z.data) + var n int + for len(buf) > 0 { + if z.idx == 0 && len(buf) >= zn { + // Avoid a copy as there is enough data for a block. + if err := z.compressBlock(buf[:zn]); err != nil { + return n, err + } + n += zn + buf = buf[zn:] + continue + } + // Accumulate the data to be compressed. + m := copy(z.data[z.idx:], buf) + n += m + z.idx += m + buf = buf[m:] + if debugFlag { + debug("%d bytes copied to buf, current index %d", n, z.idx) + } + + if z.idx < len(z.data) { + // Buffer not filled. + if debugFlag { + debug("need more data for compression") + } + return n, nil + } + + // Buffer full. + if err := z.compressBlock(z.data); err != nil { + return n, err + } + z.idx = 0 + } + + return n, nil +} + +// compressBlock compresses a block. +func (z *Writer) compressBlock(data []byte) error { + if !z.NoChecksum { + z.checksum.Write(data) + } + + // The compressed block size cannot exceed the input's. + var zn int + var err error + + if level := z.Header.CompressionLevel; level != 0 { + zn, err = CompressBlockHC(data, z.zdata, level) + } else { + zn, err = CompressBlock(data, z.zdata, z.hashtable[:]) + } + + var zdata []byte + var bLen uint32 + if debugFlag { + debug("block compression %d => %d", len(data), zn) + } + if err == nil && zn > 0 && zn < len(data) { + // Compressible and compressed size smaller than uncompressed: ok! + bLen = uint32(zn) + zdata = z.zdata[:zn] + } else { + // Uncompressed block. + bLen = uint32(len(data)) | compressedBlockFlag + zdata = data + } + if debugFlag { + debug("block compression to be written len=%d data len=%d", bLen, len(zdata)) + } + + // Write the block. + if err := z.writeUint32(bLen); err != nil { + return err + } + if _, err := z.dst.Write(zdata); err != nil { + return err + } + + if z.BlockChecksum { + checksum := xxh32.ChecksumZero(zdata) + if debugFlag { + debug("block checksum %x", checksum) + } + if err := z.writeUint32(checksum); err != nil { + return err + } + } + if debugFlag { + debug("current frame checksum %x", z.checksum.Sum32()) + } + + return nil +} + +// Flush flushes any pending compressed data to the underlying writer. +// Flush does not return until the data has been written. +// If the underlying writer returns an error, Flush returns that error. +func (z *Writer) Flush() error { + if debugFlag { + debug("flush with index %d", z.idx) + } + if z.idx == 0 { + return nil + } + + return z.compressBlock(z.data[:z.idx]) +} + +// Close closes the Writer, flushing any unwritten data to the underlying io.Writer, but does not close the underlying io.Writer. +func (z *Writer) Close() error { + if !z.Header.done { + if err := z.writeHeader(); err != nil { + return err + } + } + + if err := z.Flush(); err != nil { + return err + } + + if debugFlag { + debug("writing last empty block") + } + if err := z.writeUint32(0); err != nil { + return err + } + if !z.NoChecksum { + checksum := z.checksum.Sum32() + if debugFlag { + debug("stream checksum %x", checksum) + } + if err := z.writeUint32(checksum); err != nil { + return err + } + } + return nil +} + +// Reset clears the state of the Writer z such that it is equivalent to its +// initial state from NewWriter, but instead writing to w. +// No access to the underlying io.Writer is performed. +func (z *Writer) Reset(w io.Writer) { + z.Header = Header{} + z.dst = w + z.checksum.Reset() + z.zdata = z.zdata[:0] + z.data = z.data[:0] + z.idx = 0 +} + +// writeUint32 writes a uint32 to the underlying writer. +func (z *Writer) writeUint32(x uint32) error { + buf := z.buf[:4] + binary.LittleEndian.PutUint32(buf, x) + _, err := z.dst.Write(buf) + return err +} diff --git a/vendor/github.com/rcrowley/go-metrics/LICENSE b/vendor/github.com/rcrowley/go-metrics/LICENSE new file mode 100644 index 00000000000..363fa9ee77b --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/LICENSE @@ -0,0 +1,29 @@ +Copyright 2012 Richard Crowley. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + +THIS SOFTWARE IS PROVIDED BY RICHARD CROWLEY ``AS IS'' AND ANY EXPRESS +OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL RICHARD CROWLEY OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. + +The views and conclusions contained in the software and documentation +are those of the authors and should not be interpreted as representing +official policies, either expressed or implied, of Richard Crowley. diff --git a/vendor/github.com/rcrowley/go-metrics/counter.go b/vendor/github.com/rcrowley/go-metrics/counter.go new file mode 100644 index 00000000000..bb7b039cb57 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/counter.go @@ -0,0 +1,112 @@ +package metrics + +import "sync/atomic" + +// Counters hold an int64 value that can be incremented and decremented. +type Counter interface { + Clear() + Count() int64 + Dec(int64) + Inc(int64) + Snapshot() Counter +} + +// GetOrRegisterCounter returns an existing Counter or constructs and registers +// a new StandardCounter. +func GetOrRegisterCounter(name string, r Registry) Counter { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewCounter).(Counter) +} + +// NewCounter constructs a new StandardCounter. +func NewCounter() Counter { + if UseNilMetrics { + return NilCounter{} + } + return &StandardCounter{0} +} + +// NewRegisteredCounter constructs and registers a new StandardCounter. +func NewRegisteredCounter(name string, r Registry) Counter { + c := NewCounter() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// CounterSnapshot is a read-only copy of another Counter. +type CounterSnapshot int64 + +// Clear panics. +func (CounterSnapshot) Clear() { + panic("Clear called on a CounterSnapshot") +} + +// Count returns the count at the time the snapshot was taken. +func (c CounterSnapshot) Count() int64 { return int64(c) } + +// Dec panics. +func (CounterSnapshot) Dec(int64) { + panic("Dec called on a CounterSnapshot") +} + +// Inc panics. +func (CounterSnapshot) Inc(int64) { + panic("Inc called on a CounterSnapshot") +} + +// Snapshot returns the snapshot. +func (c CounterSnapshot) Snapshot() Counter { return c } + +// NilCounter is a no-op Counter. +type NilCounter struct{} + +// Clear is a no-op. +func (NilCounter) Clear() {} + +// Count is a no-op. +func (NilCounter) Count() int64 { return 0 } + +// Dec is a no-op. +func (NilCounter) Dec(i int64) {} + +// Inc is a no-op. +func (NilCounter) Inc(i int64) {} + +// Snapshot is a no-op. +func (NilCounter) Snapshot() Counter { return NilCounter{} } + +// StandardCounter is the standard implementation of a Counter and uses the +// sync/atomic package to manage a single int64 value. +type StandardCounter struct { + count int64 +} + +// Clear sets the counter to zero. +func (c *StandardCounter) Clear() { + atomic.StoreInt64(&c.count, 0) +} + +// Count returns the current count. +func (c *StandardCounter) Count() int64 { + return atomic.LoadInt64(&c.count) +} + +// Dec decrements the counter by the given amount. +func (c *StandardCounter) Dec(i int64) { + atomic.AddInt64(&c.count, -i) +} + +// Inc increments the counter by the given amount. +func (c *StandardCounter) Inc(i int64) { + atomic.AddInt64(&c.count, i) +} + +// Snapshot returns a read-only copy of the counter. +func (c *StandardCounter) Snapshot() Counter { + return CounterSnapshot(c.Count()) +} diff --git a/vendor/github.com/rcrowley/go-metrics/debug.go b/vendor/github.com/rcrowley/go-metrics/debug.go new file mode 100644 index 00000000000..043ccefab61 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/debug.go @@ -0,0 +1,76 @@ +package metrics + +import ( + "runtime/debug" + "time" +) + +var ( + debugMetrics struct { + GCStats struct { + LastGC Gauge + NumGC Gauge + Pause Histogram + //PauseQuantiles Histogram + PauseTotal Gauge + } + ReadGCStats Timer + } + gcStats debug.GCStats +) + +// Capture new values for the Go garbage collector statistics exported in +// debug.GCStats. This is designed to be called as a goroutine. +func CaptureDebugGCStats(r Registry, d time.Duration) { + for _ = range time.Tick(d) { + CaptureDebugGCStatsOnce(r) + } +} + +// Capture new values for the Go garbage collector statistics exported in +// debug.GCStats. This is designed to be called in a background goroutine. +// Giving a registry which has not been given to RegisterDebugGCStats will +// panic. +// +// Be careful (but much less so) with this because debug.ReadGCStats calls +// the C function runtime·lock(runtime·mheap) which, while not a stop-the-world +// operation, isn't something you want to be doing all the time. +func CaptureDebugGCStatsOnce(r Registry) { + lastGC := gcStats.LastGC + t := time.Now() + debug.ReadGCStats(&gcStats) + debugMetrics.ReadGCStats.UpdateSince(t) + + debugMetrics.GCStats.LastGC.Update(int64(gcStats.LastGC.UnixNano())) + debugMetrics.GCStats.NumGC.Update(int64(gcStats.NumGC)) + if lastGC != gcStats.LastGC && 0 < len(gcStats.Pause) { + debugMetrics.GCStats.Pause.Update(int64(gcStats.Pause[0])) + } + //debugMetrics.GCStats.PauseQuantiles.Update(gcStats.PauseQuantiles) + debugMetrics.GCStats.PauseTotal.Update(int64(gcStats.PauseTotal)) +} + +// Register metrics for the Go garbage collector statistics exported in +// debug.GCStats. The metrics are named by their fully-qualified Go symbols, +// i.e. debug.GCStats.PauseTotal. +func RegisterDebugGCStats(r Registry) { + debugMetrics.GCStats.LastGC = NewGauge() + debugMetrics.GCStats.NumGC = NewGauge() + debugMetrics.GCStats.Pause = NewHistogram(NewExpDecaySample(1028, 0.015)) + //debugMetrics.GCStats.PauseQuantiles = NewHistogram(NewExpDecaySample(1028, 0.015)) + debugMetrics.GCStats.PauseTotal = NewGauge() + debugMetrics.ReadGCStats = NewTimer() + + r.Register("debug.GCStats.LastGC", debugMetrics.GCStats.LastGC) + r.Register("debug.GCStats.NumGC", debugMetrics.GCStats.NumGC) + r.Register("debug.GCStats.Pause", debugMetrics.GCStats.Pause) + //r.Register("debug.GCStats.PauseQuantiles", debugMetrics.GCStats.PauseQuantiles) + r.Register("debug.GCStats.PauseTotal", debugMetrics.GCStats.PauseTotal) + r.Register("debug.ReadGCStats", debugMetrics.ReadGCStats) +} + +// Allocate an initial slice for gcStats.Pause to avoid allocations during +// normal operation. +func init() { + gcStats.Pause = make([]time.Duration, 11) +} diff --git a/vendor/github.com/rcrowley/go-metrics/ewma.go b/vendor/github.com/rcrowley/go-metrics/ewma.go new file mode 100644 index 00000000000..a8183dd7e21 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/ewma.go @@ -0,0 +1,138 @@ +package metrics + +import ( + "math" + "sync" + "sync/atomic" +) + +// EWMAs continuously calculate an exponentially-weighted moving average +// based on an outside source of clock ticks. +type EWMA interface { + Rate() float64 + Snapshot() EWMA + Tick() + Update(int64) +} + +// NewEWMA constructs a new EWMA with the given alpha. +func NewEWMA(alpha float64) EWMA { + if UseNilMetrics { + return NilEWMA{} + } + return &StandardEWMA{alpha: alpha} +} + +// NewEWMA1 constructs a new EWMA for a one-minute moving average. +func NewEWMA1() EWMA { + return NewEWMA(1 - math.Exp(-5.0/60.0/1)) +} + +// NewEWMA5 constructs a new EWMA for a five-minute moving average. +func NewEWMA5() EWMA { + return NewEWMA(1 - math.Exp(-5.0/60.0/5)) +} + +// NewEWMA15 constructs a new EWMA for a fifteen-minute moving average. +func NewEWMA15() EWMA { + return NewEWMA(1 - math.Exp(-5.0/60.0/15)) +} + +// EWMASnapshot is a read-only copy of another EWMA. +type EWMASnapshot float64 + +// Rate returns the rate of events per second at the time the snapshot was +// taken. +func (a EWMASnapshot) Rate() float64 { return float64(a) } + +// Snapshot returns the snapshot. +func (a EWMASnapshot) Snapshot() EWMA { return a } + +// Tick panics. +func (EWMASnapshot) Tick() { + panic("Tick called on an EWMASnapshot") +} + +// Update panics. +func (EWMASnapshot) Update(int64) { + panic("Update called on an EWMASnapshot") +} + +// NilEWMA is a no-op EWMA. +type NilEWMA struct{} + +// Rate is a no-op. +func (NilEWMA) Rate() float64 { return 0.0 } + +// Snapshot is a no-op. +func (NilEWMA) Snapshot() EWMA { return NilEWMA{} } + +// Tick is a no-op. +func (NilEWMA) Tick() {} + +// Update is a no-op. +func (NilEWMA) Update(n int64) {} + +// StandardEWMA is the standard implementation of an EWMA and tracks the number +// of uncounted events and processes them on each tick. It uses the +// sync/atomic package to manage uncounted events. +type StandardEWMA struct { + uncounted int64 // /!\ this should be the first member to ensure 64-bit alignment + alpha float64 + rate uint64 + init uint32 + mutex sync.Mutex +} + +// Rate returns the moving average rate of events per second. +func (a *StandardEWMA) Rate() float64 { + currentRate := math.Float64frombits(atomic.LoadUint64(&a.rate)) * float64(1e9) + return currentRate +} + +// Snapshot returns a read-only copy of the EWMA. +func (a *StandardEWMA) Snapshot() EWMA { + return EWMASnapshot(a.Rate()) +} + +// Tick ticks the clock to update the moving average. It assumes it is called +// every five seconds. +func (a *StandardEWMA) Tick() { + // Optimization to avoid mutex locking in the hot-path. + if atomic.LoadUint32(&a.init) == 1 { + a.updateRate(a.fetchInstantRate()) + } else { + // Slow-path: this is only needed on the first Tick() and preserves transactional updating + // of init and rate in the else block. The first conditional is needed below because + // a different thread could have set a.init = 1 between the time of the first atomic load and when + // the lock was acquired. + a.mutex.Lock() + if atomic.LoadUint32(&a.init) == 1 { + // The fetchInstantRate() uses atomic loading, which is unecessary in this critical section + // but again, this section is only invoked on the first successful Tick() operation. + a.updateRate(a.fetchInstantRate()) + } else { + atomic.StoreUint32(&a.init, 1) + atomic.StoreUint64(&a.rate, math.Float64bits(a.fetchInstantRate())) + } + a.mutex.Unlock() + } +} + +func (a *StandardEWMA) fetchInstantRate() float64 { + count := atomic.LoadInt64(&a.uncounted) + atomic.AddInt64(&a.uncounted, -count) + instantRate := float64(count) / float64(5e9) + return instantRate +} + +func (a *StandardEWMA) updateRate(instantRate float64) { + currentRate := math.Float64frombits(atomic.LoadUint64(&a.rate)) + currentRate += a.alpha * (instantRate - currentRate) + atomic.StoreUint64(&a.rate, math.Float64bits(currentRate)) +} + +// Update adds n uncounted events. +func (a *StandardEWMA) Update(n int64) { + atomic.AddInt64(&a.uncounted, n) +} diff --git a/vendor/github.com/rcrowley/go-metrics/gauge.go b/vendor/github.com/rcrowley/go-metrics/gauge.go new file mode 100644 index 00000000000..cb57a93889f --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/gauge.go @@ -0,0 +1,120 @@ +package metrics + +import "sync/atomic" + +// Gauges hold an int64 value that can be set arbitrarily. +type Gauge interface { + Snapshot() Gauge + Update(int64) + Value() int64 +} + +// GetOrRegisterGauge returns an existing Gauge or constructs and registers a +// new StandardGauge. +func GetOrRegisterGauge(name string, r Registry) Gauge { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewGauge).(Gauge) +} + +// NewGauge constructs a new StandardGauge. +func NewGauge() Gauge { + if UseNilMetrics { + return NilGauge{} + } + return &StandardGauge{0} +} + +// NewRegisteredGauge constructs and registers a new StandardGauge. +func NewRegisteredGauge(name string, r Registry) Gauge { + c := NewGauge() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// NewFunctionalGauge constructs a new FunctionalGauge. +func NewFunctionalGauge(f func() int64) Gauge { + if UseNilMetrics { + return NilGauge{} + } + return &FunctionalGauge{value: f} +} + +// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge. +func NewRegisteredFunctionalGauge(name string, r Registry, f func() int64) Gauge { + c := NewFunctionalGauge(f) + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// GaugeSnapshot is a read-only copy of another Gauge. +type GaugeSnapshot int64 + +// Snapshot returns the snapshot. +func (g GaugeSnapshot) Snapshot() Gauge { return g } + +// Update panics. +func (GaugeSnapshot) Update(int64) { + panic("Update called on a GaugeSnapshot") +} + +// Value returns the value at the time the snapshot was taken. +func (g GaugeSnapshot) Value() int64 { return int64(g) } + +// NilGauge is a no-op Gauge. +type NilGauge struct{} + +// Snapshot is a no-op. +func (NilGauge) Snapshot() Gauge { return NilGauge{} } + +// Update is a no-op. +func (NilGauge) Update(v int64) {} + +// Value is a no-op. +func (NilGauge) Value() int64 { return 0 } + +// StandardGauge is the standard implementation of a Gauge and uses the +// sync/atomic package to manage a single int64 value. +type StandardGauge struct { + value int64 +} + +// Snapshot returns a read-only copy of the gauge. +func (g *StandardGauge) Snapshot() Gauge { + return GaugeSnapshot(g.Value()) +} + +// Update updates the gauge's value. +func (g *StandardGauge) Update(v int64) { + atomic.StoreInt64(&g.value, v) +} + +// Value returns the gauge's current value. +func (g *StandardGauge) Value() int64 { + return atomic.LoadInt64(&g.value) +} + +// FunctionalGauge returns value from given function +type FunctionalGauge struct { + value func() int64 +} + +// Value returns the gauge's current value. +func (g FunctionalGauge) Value() int64 { + return g.value() +} + +// Snapshot returns the snapshot. +func (g FunctionalGauge) Snapshot() Gauge { return GaugeSnapshot(g.Value()) } + +// Update panics. +func (FunctionalGauge) Update(int64) { + panic("Update called on a FunctionalGauge") +} diff --git a/vendor/github.com/rcrowley/go-metrics/gauge_float64.go b/vendor/github.com/rcrowley/go-metrics/gauge_float64.go new file mode 100644 index 00000000000..3962e6db09a --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/gauge_float64.go @@ -0,0 +1,125 @@ +package metrics + +import ( + "math" + "sync/atomic" +) + +// GaugeFloat64s hold a float64 value that can be set arbitrarily. +type GaugeFloat64 interface { + Snapshot() GaugeFloat64 + Update(float64) + Value() float64 +} + +// GetOrRegisterGaugeFloat64 returns an existing GaugeFloat64 or constructs and registers a +// new StandardGaugeFloat64. +func GetOrRegisterGaugeFloat64(name string, r Registry) GaugeFloat64 { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewGaugeFloat64()).(GaugeFloat64) +} + +// NewGaugeFloat64 constructs a new StandardGaugeFloat64. +func NewGaugeFloat64() GaugeFloat64 { + if UseNilMetrics { + return NilGaugeFloat64{} + } + return &StandardGaugeFloat64{ + value: 0.0, + } +} + +// NewRegisteredGaugeFloat64 constructs and registers a new StandardGaugeFloat64. +func NewRegisteredGaugeFloat64(name string, r Registry) GaugeFloat64 { + c := NewGaugeFloat64() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// NewFunctionalGauge constructs a new FunctionalGauge. +func NewFunctionalGaugeFloat64(f func() float64) GaugeFloat64 { + if UseNilMetrics { + return NilGaugeFloat64{} + } + return &FunctionalGaugeFloat64{value: f} +} + +// NewRegisteredFunctionalGauge constructs and registers a new StandardGauge. +func NewRegisteredFunctionalGaugeFloat64(name string, r Registry, f func() float64) GaugeFloat64 { + c := NewFunctionalGaugeFloat64(f) + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// GaugeFloat64Snapshot is a read-only copy of another GaugeFloat64. +type GaugeFloat64Snapshot float64 + +// Snapshot returns the snapshot. +func (g GaugeFloat64Snapshot) Snapshot() GaugeFloat64 { return g } + +// Update panics. +func (GaugeFloat64Snapshot) Update(float64) { + panic("Update called on a GaugeFloat64Snapshot") +} + +// Value returns the value at the time the snapshot was taken. +func (g GaugeFloat64Snapshot) Value() float64 { return float64(g) } + +// NilGauge is a no-op Gauge. +type NilGaugeFloat64 struct{} + +// Snapshot is a no-op. +func (NilGaugeFloat64) Snapshot() GaugeFloat64 { return NilGaugeFloat64{} } + +// Update is a no-op. +func (NilGaugeFloat64) Update(v float64) {} + +// Value is a no-op. +func (NilGaugeFloat64) Value() float64 { return 0.0 } + +// StandardGaugeFloat64 is the standard implementation of a GaugeFloat64 and uses +// sync.Mutex to manage a single float64 value. +type StandardGaugeFloat64 struct { + value uint64 +} + +// Snapshot returns a read-only copy of the gauge. +func (g *StandardGaugeFloat64) Snapshot() GaugeFloat64 { + return GaugeFloat64Snapshot(g.Value()) +} + +// Update updates the gauge's value. +func (g *StandardGaugeFloat64) Update(v float64) { + atomic.StoreUint64(&g.value, math.Float64bits(v)) +} + +// Value returns the gauge's current value. +func (g *StandardGaugeFloat64) Value() float64 { + return math.Float64frombits(atomic.LoadUint64(&g.value)) +} + +// FunctionalGaugeFloat64 returns value from given function +type FunctionalGaugeFloat64 struct { + value func() float64 +} + +// Value returns the gauge's current value. +func (g FunctionalGaugeFloat64) Value() float64 { + return g.value() +} + +// Snapshot returns the snapshot. +func (g FunctionalGaugeFloat64) Snapshot() GaugeFloat64 { return GaugeFloat64Snapshot(g.Value()) } + +// Update panics. +func (FunctionalGaugeFloat64) Update(float64) { + panic("Update called on a FunctionalGaugeFloat64") +} diff --git a/vendor/github.com/rcrowley/go-metrics/graphite.go b/vendor/github.com/rcrowley/go-metrics/graphite.go new file mode 100644 index 00000000000..abd0a7d2918 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/graphite.go @@ -0,0 +1,113 @@ +package metrics + +import ( + "bufio" + "fmt" + "log" + "net" + "strconv" + "strings" + "time" +) + +// GraphiteConfig provides a container with configuration parameters for +// the Graphite exporter +type GraphiteConfig struct { + Addr *net.TCPAddr // Network address to connect to + Registry Registry // Registry to be exported + FlushInterval time.Duration // Flush interval + DurationUnit time.Duration // Time conversion unit for durations + Prefix string // Prefix to be prepended to metric names + Percentiles []float64 // Percentiles to export from timers and histograms +} + +// Graphite is a blocking exporter function which reports metrics in r +// to a graphite server located at addr, flushing them every d duration +// and prepending metric names with prefix. +func Graphite(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) { + GraphiteWithConfig(GraphiteConfig{ + Addr: addr, + Registry: r, + FlushInterval: d, + DurationUnit: time.Nanosecond, + Prefix: prefix, + Percentiles: []float64{0.5, 0.75, 0.95, 0.99, 0.999}, + }) +} + +// GraphiteWithConfig is a blocking exporter function just like Graphite, +// but it takes a GraphiteConfig instead. +func GraphiteWithConfig(c GraphiteConfig) { + log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015") + for _ = range time.Tick(c.FlushInterval) { + if err := graphite(&c); nil != err { + log.Println(err) + } + } +} + +// GraphiteOnce performs a single submission to Graphite, returning a +// non-nil error on failed connections. This can be used in a loop +// similar to GraphiteWithConfig for custom error handling. +func GraphiteOnce(c GraphiteConfig) error { + log.Printf("WARNING: This go-metrics client has been DEPRECATED! It has been moved to https://github.com/cyberdelia/go-metrics-graphite and will be removed from rcrowley/go-metrics on August 12th 2015") + return graphite(&c) +} + +func graphite(c *GraphiteConfig) error { + now := time.Now().Unix() + du := float64(c.DurationUnit) + conn, err := net.DialTCP("tcp", nil, c.Addr) + if nil != err { + return err + } + defer conn.Close() + w := bufio.NewWriter(conn) + c.Registry.Each(func(name string, i interface{}) { + switch metric := i.(type) { + case Counter: + fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, metric.Count(), now) + case Gauge: + fmt.Fprintf(w, "%s.%s.value %d %d\n", c.Prefix, name, metric.Value(), now) + case GaugeFloat64: + fmt.Fprintf(w, "%s.%s.value %f %d\n", c.Prefix, name, metric.Value(), now) + case Histogram: + h := metric.Snapshot() + ps := h.Percentiles(c.Percentiles) + fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, h.Count(), now) + fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, h.Min(), now) + fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, h.Max(), now) + fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, h.Mean(), now) + fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, h.StdDev(), now) + for psIdx, psKey := range c.Percentiles { + key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1) + fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now) + } + case Meter: + m := metric.Snapshot() + fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, m.Count(), now) + fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, m.Rate1(), now) + fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, m.Rate5(), now) + fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, m.Rate15(), now) + fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, m.RateMean(), now) + case Timer: + t := metric.Snapshot() + ps := t.Percentiles(c.Percentiles) + fmt.Fprintf(w, "%s.%s.count %d %d\n", c.Prefix, name, t.Count(), now) + fmt.Fprintf(w, "%s.%s.min %d %d\n", c.Prefix, name, t.Min()/int64(du), now) + fmt.Fprintf(w, "%s.%s.max %d %d\n", c.Prefix, name, t.Max()/int64(du), now) + fmt.Fprintf(w, "%s.%s.mean %.2f %d\n", c.Prefix, name, t.Mean()/du, now) + fmt.Fprintf(w, "%s.%s.std-dev %.2f %d\n", c.Prefix, name, t.StdDev()/du, now) + for psIdx, psKey := range c.Percentiles { + key := strings.Replace(strconv.FormatFloat(psKey*100.0, 'f', -1, 64), ".", "", 1) + fmt.Fprintf(w, "%s.%s.%s-percentile %.2f %d\n", c.Prefix, name, key, ps[psIdx], now) + } + fmt.Fprintf(w, "%s.%s.one-minute %.2f %d\n", c.Prefix, name, t.Rate1(), now) + fmt.Fprintf(w, "%s.%s.five-minute %.2f %d\n", c.Prefix, name, t.Rate5(), now) + fmt.Fprintf(w, "%s.%s.fifteen-minute %.2f %d\n", c.Prefix, name, t.Rate15(), now) + fmt.Fprintf(w, "%s.%s.mean-rate %.2f %d\n", c.Prefix, name, t.RateMean(), now) + } + w.Flush() + }) + return nil +} diff --git a/vendor/github.com/rcrowley/go-metrics/healthcheck.go b/vendor/github.com/rcrowley/go-metrics/healthcheck.go new file mode 100644 index 00000000000..445131caee5 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/healthcheck.go @@ -0,0 +1,61 @@ +package metrics + +// Healthchecks hold an error value describing an arbitrary up/down status. +type Healthcheck interface { + Check() + Error() error + Healthy() + Unhealthy(error) +} + +// NewHealthcheck constructs a new Healthcheck which will use the given +// function to update its status. +func NewHealthcheck(f func(Healthcheck)) Healthcheck { + if UseNilMetrics { + return NilHealthcheck{} + } + return &StandardHealthcheck{nil, f} +} + +// NilHealthcheck is a no-op. +type NilHealthcheck struct{} + +// Check is a no-op. +func (NilHealthcheck) Check() {} + +// Error is a no-op. +func (NilHealthcheck) Error() error { return nil } + +// Healthy is a no-op. +func (NilHealthcheck) Healthy() {} + +// Unhealthy is a no-op. +func (NilHealthcheck) Unhealthy(error) {} + +// StandardHealthcheck is the standard implementation of a Healthcheck and +// stores the status and a function to call to update the status. +type StandardHealthcheck struct { + err error + f func(Healthcheck) +} + +// Check runs the healthcheck function to update the healthcheck's status. +func (h *StandardHealthcheck) Check() { + h.f(h) +} + +// Error returns the healthcheck's status, which will be nil if it is healthy. +func (h *StandardHealthcheck) Error() error { + return h.err +} + +// Healthy marks the healthcheck as healthy. +func (h *StandardHealthcheck) Healthy() { + h.err = nil +} + +// Unhealthy marks the healthcheck as unhealthy. The error is stored and +// may be retrieved by the Error method. +func (h *StandardHealthcheck) Unhealthy(err error) { + h.err = err +} diff --git a/vendor/github.com/rcrowley/go-metrics/histogram.go b/vendor/github.com/rcrowley/go-metrics/histogram.go new file mode 100644 index 00000000000..dbc837fe4d9 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/histogram.go @@ -0,0 +1,202 @@ +package metrics + +// Histograms calculate distribution statistics from a series of int64 values. +type Histogram interface { + Clear() + Count() int64 + Max() int64 + Mean() float64 + Min() int64 + Percentile(float64) float64 + Percentiles([]float64) []float64 + Sample() Sample + Snapshot() Histogram + StdDev() float64 + Sum() int64 + Update(int64) + Variance() float64 +} + +// GetOrRegisterHistogram returns an existing Histogram or constructs and +// registers a new StandardHistogram. +func GetOrRegisterHistogram(name string, r Registry, s Sample) Histogram { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, func() Histogram { return NewHistogram(s) }).(Histogram) +} + +// NewHistogram constructs a new StandardHistogram from a Sample. +func NewHistogram(s Sample) Histogram { + if UseNilMetrics { + return NilHistogram{} + } + return &StandardHistogram{sample: s} +} + +// NewRegisteredHistogram constructs and registers a new StandardHistogram from +// a Sample. +func NewRegisteredHistogram(name string, r Registry, s Sample) Histogram { + c := NewHistogram(s) + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// HistogramSnapshot is a read-only copy of another Histogram. +type HistogramSnapshot struct { + sample *SampleSnapshot +} + +// Clear panics. +func (*HistogramSnapshot) Clear() { + panic("Clear called on a HistogramSnapshot") +} + +// Count returns the number of samples recorded at the time the snapshot was +// taken. +func (h *HistogramSnapshot) Count() int64 { return h.sample.Count() } + +// Max returns the maximum value in the sample at the time the snapshot was +// taken. +func (h *HistogramSnapshot) Max() int64 { return h.sample.Max() } + +// Mean returns the mean of the values in the sample at the time the snapshot +// was taken. +func (h *HistogramSnapshot) Mean() float64 { return h.sample.Mean() } + +// Min returns the minimum value in the sample at the time the snapshot was +// taken. +func (h *HistogramSnapshot) Min() int64 { return h.sample.Min() } + +// Percentile returns an arbitrary percentile of values in the sample at the +// time the snapshot was taken. +func (h *HistogramSnapshot) Percentile(p float64) float64 { + return h.sample.Percentile(p) +} + +// Percentiles returns a slice of arbitrary percentiles of values in the sample +// at the time the snapshot was taken. +func (h *HistogramSnapshot) Percentiles(ps []float64) []float64 { + return h.sample.Percentiles(ps) +} + +// Sample returns the Sample underlying the histogram. +func (h *HistogramSnapshot) Sample() Sample { return h.sample } + +// Snapshot returns the snapshot. +func (h *HistogramSnapshot) Snapshot() Histogram { return h } + +// StdDev returns the standard deviation of the values in the sample at the +// time the snapshot was taken. +func (h *HistogramSnapshot) StdDev() float64 { return h.sample.StdDev() } + +// Sum returns the sum in the sample at the time the snapshot was taken. +func (h *HistogramSnapshot) Sum() int64 { return h.sample.Sum() } + +// Update panics. +func (*HistogramSnapshot) Update(int64) { + panic("Update called on a HistogramSnapshot") +} + +// Variance returns the variance of inputs at the time the snapshot was taken. +func (h *HistogramSnapshot) Variance() float64 { return h.sample.Variance() } + +// NilHistogram is a no-op Histogram. +type NilHistogram struct{} + +// Clear is a no-op. +func (NilHistogram) Clear() {} + +// Count is a no-op. +func (NilHistogram) Count() int64 { return 0 } + +// Max is a no-op. +func (NilHistogram) Max() int64 { return 0 } + +// Mean is a no-op. +func (NilHistogram) Mean() float64 { return 0.0 } + +// Min is a no-op. +func (NilHistogram) Min() int64 { return 0 } + +// Percentile is a no-op. +func (NilHistogram) Percentile(p float64) float64 { return 0.0 } + +// Percentiles is a no-op. +func (NilHistogram) Percentiles(ps []float64) []float64 { + return make([]float64, len(ps)) +} + +// Sample is a no-op. +func (NilHistogram) Sample() Sample { return NilSample{} } + +// Snapshot is a no-op. +func (NilHistogram) Snapshot() Histogram { return NilHistogram{} } + +// StdDev is a no-op. +func (NilHistogram) StdDev() float64 { return 0.0 } + +// Sum is a no-op. +func (NilHistogram) Sum() int64 { return 0 } + +// Update is a no-op. +func (NilHistogram) Update(v int64) {} + +// Variance is a no-op. +func (NilHistogram) Variance() float64 { return 0.0 } + +// StandardHistogram is the standard implementation of a Histogram and uses a +// Sample to bound its memory use. +type StandardHistogram struct { + sample Sample +} + +// Clear clears the histogram and its sample. +func (h *StandardHistogram) Clear() { h.sample.Clear() } + +// Count returns the number of samples recorded since the histogram was last +// cleared. +func (h *StandardHistogram) Count() int64 { return h.sample.Count() } + +// Max returns the maximum value in the sample. +func (h *StandardHistogram) Max() int64 { return h.sample.Max() } + +// Mean returns the mean of the values in the sample. +func (h *StandardHistogram) Mean() float64 { return h.sample.Mean() } + +// Min returns the minimum value in the sample. +func (h *StandardHistogram) Min() int64 { return h.sample.Min() } + +// Percentile returns an arbitrary percentile of the values in the sample. +func (h *StandardHistogram) Percentile(p float64) float64 { + return h.sample.Percentile(p) +} + +// Percentiles returns a slice of arbitrary percentiles of the values in the +// sample. +func (h *StandardHistogram) Percentiles(ps []float64) []float64 { + return h.sample.Percentiles(ps) +} + +// Sample returns the Sample underlying the histogram. +func (h *StandardHistogram) Sample() Sample { return h.sample } + +// Snapshot returns a read-only copy of the histogram. +func (h *StandardHistogram) Snapshot() Histogram { + return &HistogramSnapshot{sample: h.sample.Snapshot().(*SampleSnapshot)} +} + +// StdDev returns the standard deviation of the values in the sample. +func (h *StandardHistogram) StdDev() float64 { return h.sample.StdDev() } + +// Sum returns the sum in the sample. +func (h *StandardHistogram) Sum() int64 { return h.sample.Sum() } + +// Update samples a new value. +func (h *StandardHistogram) Update(v int64) { h.sample.Update(v) } + +// Variance returns the variance of the values in the sample. +func (h *StandardHistogram) Variance() float64 { return h.sample.Variance() } diff --git a/vendor/github.com/rcrowley/go-metrics/json.go b/vendor/github.com/rcrowley/go-metrics/json.go new file mode 100644 index 00000000000..174b9477e92 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/json.go @@ -0,0 +1,31 @@ +package metrics + +import ( + "encoding/json" + "io" + "time" +) + +// MarshalJSON returns a byte slice containing a JSON representation of all +// the metrics in the Registry. +func (r *StandardRegistry) MarshalJSON() ([]byte, error) { + return json.Marshal(r.GetAll()) +} + +// WriteJSON writes metrics from the given registry periodically to the +// specified io.Writer as JSON. +func WriteJSON(r Registry, d time.Duration, w io.Writer) { + for _ = range time.Tick(d) { + WriteJSONOnce(r, w) + } +} + +// WriteJSONOnce writes metrics from the given registry to the specified +// io.Writer as JSON. +func WriteJSONOnce(r Registry, w io.Writer) { + json.NewEncoder(w).Encode(r) +} + +func (p *PrefixedRegistry) MarshalJSON() ([]byte, error) { + return json.Marshal(p.GetAll()) +} diff --git a/vendor/github.com/rcrowley/go-metrics/log.go b/vendor/github.com/rcrowley/go-metrics/log.go new file mode 100644 index 00000000000..f8074c04576 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/log.go @@ -0,0 +1,80 @@ +package metrics + +import ( + "time" +) + +type Logger interface { + Printf(format string, v ...interface{}) +} + +func Log(r Registry, freq time.Duration, l Logger) { + LogScaled(r, freq, time.Nanosecond, l) +} + +// Output each metric in the given registry periodically using the given +// logger. Print timings in `scale` units (eg time.Millisecond) rather than nanos. +func LogScaled(r Registry, freq time.Duration, scale time.Duration, l Logger) { + du := float64(scale) + duSuffix := scale.String()[1:] + + for _ = range time.Tick(freq) { + r.Each(func(name string, i interface{}) { + switch metric := i.(type) { + case Counter: + l.Printf("counter %s\n", name) + l.Printf(" count: %9d\n", metric.Count()) + case Gauge: + l.Printf("gauge %s\n", name) + l.Printf(" value: %9d\n", metric.Value()) + case GaugeFloat64: + l.Printf("gauge %s\n", name) + l.Printf(" value: %f\n", metric.Value()) + case Healthcheck: + metric.Check() + l.Printf("healthcheck %s\n", name) + l.Printf(" error: %v\n", metric.Error()) + case Histogram: + h := metric.Snapshot() + ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + l.Printf("histogram %s\n", name) + l.Printf(" count: %9d\n", h.Count()) + l.Printf(" min: %9d\n", h.Min()) + l.Printf(" max: %9d\n", h.Max()) + l.Printf(" mean: %12.2f\n", h.Mean()) + l.Printf(" stddev: %12.2f\n", h.StdDev()) + l.Printf(" median: %12.2f\n", ps[0]) + l.Printf(" 75%%: %12.2f\n", ps[1]) + l.Printf(" 95%%: %12.2f\n", ps[2]) + l.Printf(" 99%%: %12.2f\n", ps[3]) + l.Printf(" 99.9%%: %12.2f\n", ps[4]) + case Meter: + m := metric.Snapshot() + l.Printf("meter %s\n", name) + l.Printf(" count: %9d\n", m.Count()) + l.Printf(" 1-min rate: %12.2f\n", m.Rate1()) + l.Printf(" 5-min rate: %12.2f\n", m.Rate5()) + l.Printf(" 15-min rate: %12.2f\n", m.Rate15()) + l.Printf(" mean rate: %12.2f\n", m.RateMean()) + case Timer: + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + l.Printf("timer %s\n", name) + l.Printf(" count: %9d\n", t.Count()) + l.Printf(" min: %12.2f%s\n", float64(t.Min())/du, duSuffix) + l.Printf(" max: %12.2f%s\n", float64(t.Max())/du, duSuffix) + l.Printf(" mean: %12.2f%s\n", t.Mean()/du, duSuffix) + l.Printf(" stddev: %12.2f%s\n", t.StdDev()/du, duSuffix) + l.Printf(" median: %12.2f%s\n", ps[0]/du, duSuffix) + l.Printf(" 75%%: %12.2f%s\n", ps[1]/du, duSuffix) + l.Printf(" 95%%: %12.2f%s\n", ps[2]/du, duSuffix) + l.Printf(" 99%%: %12.2f%s\n", ps[3]/du, duSuffix) + l.Printf(" 99.9%%: %12.2f%s\n", ps[4]/du, duSuffix) + l.Printf(" 1-min rate: %12.2f\n", t.Rate1()) + l.Printf(" 5-min rate: %12.2f\n", t.Rate5()) + l.Printf(" 15-min rate: %12.2f\n", t.Rate15()) + l.Printf(" mean rate: %12.2f\n", t.RateMean()) + } + }) + } +} diff --git a/vendor/github.com/rcrowley/go-metrics/meter.go b/vendor/github.com/rcrowley/go-metrics/meter.go new file mode 100644 index 00000000000..223669bcb29 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/meter.go @@ -0,0 +1,251 @@ +package metrics + +import ( + "math" + "sync" + "sync/atomic" + "time" +) + +// Meters count events to produce exponentially-weighted moving average rates +// at one-, five-, and fifteen-minutes and a mean rate. +type Meter interface { + Count() int64 + Mark(int64) + Rate1() float64 + Rate5() float64 + Rate15() float64 + RateMean() float64 + Snapshot() Meter + Stop() +} + +// GetOrRegisterMeter returns an existing Meter or constructs and registers a +// new StandardMeter. +// Be sure to unregister the meter from the registry once it is of no use to +// allow for garbage collection. +func GetOrRegisterMeter(name string, r Registry) Meter { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewMeter).(Meter) +} + +// NewMeter constructs a new StandardMeter and launches a goroutine. +// Be sure to call Stop() once the meter is of no use to allow for garbage collection. +func NewMeter() Meter { + if UseNilMetrics { + return NilMeter{} + } + m := newStandardMeter() + arbiter.Lock() + defer arbiter.Unlock() + arbiter.meters[m] = struct{}{} + if !arbiter.started { + arbiter.started = true + go arbiter.tick() + } + return m +} + +// NewMeter constructs and registers a new StandardMeter and launches a +// goroutine. +// Be sure to unregister the meter from the registry once it is of no use to +// allow for garbage collection. +func NewRegisteredMeter(name string, r Registry) Meter { + c := NewMeter() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// MeterSnapshot is a read-only copy of another Meter. +type MeterSnapshot struct { + count int64 + rate1, rate5, rate15, rateMean uint64 +} + +// Count returns the count of events at the time the snapshot was taken. +func (m *MeterSnapshot) Count() int64 { return m.count } + +// Mark panics. +func (*MeterSnapshot) Mark(n int64) { + panic("Mark called on a MeterSnapshot") +} + +// Rate1 returns the one-minute moving average rate of events per second at the +// time the snapshot was taken. +func (m *MeterSnapshot) Rate1() float64 { return math.Float64frombits(m.rate1) } + +// Rate5 returns the five-minute moving average rate of events per second at +// the time the snapshot was taken. +func (m *MeterSnapshot) Rate5() float64 { return math.Float64frombits(m.rate5) } + +// Rate15 returns the fifteen-minute moving average rate of events per second +// at the time the snapshot was taken. +func (m *MeterSnapshot) Rate15() float64 { return math.Float64frombits(m.rate15) } + +// RateMean returns the meter's mean rate of events per second at the time the +// snapshot was taken. +func (m *MeterSnapshot) RateMean() float64 { return math.Float64frombits(m.rateMean) } + +// Snapshot returns the snapshot. +func (m *MeterSnapshot) Snapshot() Meter { return m } + +// Stop is a no-op. +func (m *MeterSnapshot) Stop() {} + +// NilMeter is a no-op Meter. +type NilMeter struct{} + +// Count is a no-op. +func (NilMeter) Count() int64 { return 0 } + +// Mark is a no-op. +func (NilMeter) Mark(n int64) {} + +// Rate1 is a no-op. +func (NilMeter) Rate1() float64 { return 0.0 } + +// Rate5 is a no-op. +func (NilMeter) Rate5() float64 { return 0.0 } + +// Rate15is a no-op. +func (NilMeter) Rate15() float64 { return 0.0 } + +// RateMean is a no-op. +func (NilMeter) RateMean() float64 { return 0.0 } + +// Snapshot is a no-op. +func (NilMeter) Snapshot() Meter { return NilMeter{} } + +// Stop is a no-op. +func (NilMeter) Stop() {} + +// StandardMeter is the standard implementation of a Meter. +type StandardMeter struct { + snapshot *MeterSnapshot + a1, a5, a15 EWMA + startTime time.Time + stopped uint32 +} + +func newStandardMeter() *StandardMeter { + return &StandardMeter{ + snapshot: &MeterSnapshot{}, + a1: NewEWMA1(), + a5: NewEWMA5(), + a15: NewEWMA15(), + startTime: time.Now(), + } +} + +// Stop stops the meter, Mark() will be a no-op if you use it after being stopped. +func (m *StandardMeter) Stop() { + if atomic.CompareAndSwapUint32(&m.stopped, 0, 1) { + arbiter.Lock() + delete(arbiter.meters, m) + arbiter.Unlock() + } +} + +// Count returns the number of events recorded. +func (m *StandardMeter) Count() int64 { + return atomic.LoadInt64(&m.snapshot.count) +} + +// Mark records the occurance of n events. +func (m *StandardMeter) Mark(n int64) { + if atomic.LoadUint32(&m.stopped) == 1 { + return + } + + atomic.AddInt64(&m.snapshot.count, n) + + m.a1.Update(n) + m.a5.Update(n) + m.a15.Update(n) + m.updateSnapshot() +} + +// Rate1 returns the one-minute moving average rate of events per second. +func (m *StandardMeter) Rate1() float64 { + return math.Float64frombits(atomic.LoadUint64(&m.snapshot.rate1)) +} + +// Rate5 returns the five-minute moving average rate of events per second. +func (m *StandardMeter) Rate5() float64 { + return math.Float64frombits(atomic.LoadUint64(&m.snapshot.rate5)) +} + +// Rate15 returns the fifteen-minute moving average rate of events per second. +func (m *StandardMeter) Rate15() float64 { + return math.Float64frombits(atomic.LoadUint64(&m.snapshot.rate15)) +} + +// RateMean returns the meter's mean rate of events per second. +func (m *StandardMeter) RateMean() float64 { + return math.Float64frombits(atomic.LoadUint64(&m.snapshot.rateMean)) +} + +// Snapshot returns a read-only copy of the meter. +func (m *StandardMeter) Snapshot() Meter { + copiedSnapshot := MeterSnapshot{ + count: atomic.LoadInt64(&m.snapshot.count), + rate1: atomic.LoadUint64(&m.snapshot.rate1), + rate5: atomic.LoadUint64(&m.snapshot.rate5), + rate15: atomic.LoadUint64(&m.snapshot.rate15), + rateMean: atomic.LoadUint64(&m.snapshot.rateMean), + } + return &copiedSnapshot +} + +func (m *StandardMeter) updateSnapshot() { + rate1 := math.Float64bits(m.a1.Rate()) + rate5 := math.Float64bits(m.a5.Rate()) + rate15 := math.Float64bits(m.a15.Rate()) + rateMean := math.Float64bits(float64(m.Count()) / time.Since(m.startTime).Seconds()) + + atomic.StoreUint64(&m.snapshot.rate1, rate1) + atomic.StoreUint64(&m.snapshot.rate5, rate5) + atomic.StoreUint64(&m.snapshot.rate15, rate15) + atomic.StoreUint64(&m.snapshot.rateMean, rateMean) +} + +func (m *StandardMeter) tick() { + m.a1.Tick() + m.a5.Tick() + m.a15.Tick() + m.updateSnapshot() +} + +// meterArbiter ticks meters every 5s from a single goroutine. +// meters are references in a set for future stopping. +type meterArbiter struct { + sync.RWMutex + started bool + meters map[*StandardMeter]struct{} + ticker *time.Ticker +} + +var arbiter = meterArbiter{ticker: time.NewTicker(5e9), meters: make(map[*StandardMeter]struct{})} + +// Ticks meters on the scheduled interval +func (ma *meterArbiter) tick() { + for { + select { + case <-ma.ticker.C: + ma.tickMeters() + } + } +} + +func (ma *meterArbiter) tickMeters() { + ma.RLock() + defer ma.RUnlock() + for meter := range ma.meters { + meter.tick() + } +} diff --git a/vendor/github.com/rcrowley/go-metrics/metrics.go b/vendor/github.com/rcrowley/go-metrics/metrics.go new file mode 100644 index 00000000000..b97a49ed123 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/metrics.go @@ -0,0 +1,13 @@ +// Go port of Coda Hale's Metrics library +// +// +// +// Coda Hale's original work: +package metrics + +// UseNilMetrics is checked by the constructor functions for all of the +// standard metrics. If it is true, the metric returned is a stub. +// +// This global kill-switch helps quantify the observer effect and makes +// for less cluttered pprof profiles. +var UseNilMetrics bool = false diff --git a/vendor/github.com/rcrowley/go-metrics/opentsdb.go b/vendor/github.com/rcrowley/go-metrics/opentsdb.go new file mode 100644 index 00000000000..266b6c93d21 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/opentsdb.go @@ -0,0 +1,119 @@ +package metrics + +import ( + "bufio" + "fmt" + "log" + "net" + "os" + "strings" + "time" +) + +var shortHostName string = "" + +// OpenTSDBConfig provides a container with configuration parameters for +// the OpenTSDB exporter +type OpenTSDBConfig struct { + Addr *net.TCPAddr // Network address to connect to + Registry Registry // Registry to be exported + FlushInterval time.Duration // Flush interval + DurationUnit time.Duration // Time conversion unit for durations + Prefix string // Prefix to be prepended to metric names +} + +// OpenTSDB is a blocking exporter function which reports metrics in r +// to a TSDB server located at addr, flushing them every d duration +// and prepending metric names with prefix. +func OpenTSDB(r Registry, d time.Duration, prefix string, addr *net.TCPAddr) { + OpenTSDBWithConfig(OpenTSDBConfig{ + Addr: addr, + Registry: r, + FlushInterval: d, + DurationUnit: time.Nanosecond, + Prefix: prefix, + }) +} + +// OpenTSDBWithConfig is a blocking exporter function just like OpenTSDB, +// but it takes a OpenTSDBConfig instead. +func OpenTSDBWithConfig(c OpenTSDBConfig) { + for _ = range time.Tick(c.FlushInterval) { + if err := openTSDB(&c); nil != err { + log.Println(err) + } + } +} + +func getShortHostname() string { + if shortHostName == "" { + host, _ := os.Hostname() + if index := strings.Index(host, "."); index > 0 { + shortHostName = host[:index] + } else { + shortHostName = host + } + } + return shortHostName +} + +func openTSDB(c *OpenTSDBConfig) error { + shortHostname := getShortHostname() + now := time.Now().Unix() + du := float64(c.DurationUnit) + conn, err := net.DialTCP("tcp", nil, c.Addr) + if nil != err { + return err + } + defer conn.Close() + w := bufio.NewWriter(conn) + c.Registry.Each(func(name string, i interface{}) { + switch metric := i.(type) { + case Counter: + fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, metric.Count(), shortHostname) + case Gauge: + fmt.Fprintf(w, "put %s.%s.value %d %d host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname) + case GaugeFloat64: + fmt.Fprintf(w, "put %s.%s.value %d %f host=%s\n", c.Prefix, name, now, metric.Value(), shortHostname) + case Histogram: + h := metric.Snapshot() + ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, h.Count(), shortHostname) + fmt.Fprintf(w, "put %s.%s.min %d %d host=%s\n", c.Prefix, name, now, h.Min(), shortHostname) + fmt.Fprintf(w, "put %s.%s.max %d %d host=%s\n", c.Prefix, name, now, h.Max(), shortHostname) + fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, h.Mean(), shortHostname) + fmt.Fprintf(w, "put %s.%s.std-dev %d %.2f host=%s\n", c.Prefix, name, now, h.StdDev(), shortHostname) + fmt.Fprintf(w, "put %s.%s.50-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[0], shortHostname) + fmt.Fprintf(w, "put %s.%s.75-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[1], shortHostname) + fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2], shortHostname) + fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3], shortHostname) + fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4], shortHostname) + case Meter: + m := metric.Snapshot() + fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, m.Count(), shortHostname) + fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate1(), shortHostname) + fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate5(), shortHostname) + fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, m.Rate15(), shortHostname) + fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, m.RateMean(), shortHostname) + case Timer: + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + fmt.Fprintf(w, "put %s.%s.count %d %d host=%s\n", c.Prefix, name, now, t.Count(), shortHostname) + fmt.Fprintf(w, "put %s.%s.min %d %d host=%s\n", c.Prefix, name, now, t.Min()/int64(du), shortHostname) + fmt.Fprintf(w, "put %s.%s.max %d %d host=%s\n", c.Prefix, name, now, t.Max()/int64(du), shortHostname) + fmt.Fprintf(w, "put %s.%s.mean %d %.2f host=%s\n", c.Prefix, name, now, t.Mean()/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.std-dev %d %.2f host=%s\n", c.Prefix, name, now, t.StdDev()/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.50-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[0]/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.75-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[1]/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.95-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[2]/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.99-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[3]/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.999-percentile %d %.2f host=%s\n", c.Prefix, name, now, ps[4]/du, shortHostname) + fmt.Fprintf(w, "put %s.%s.one-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate1(), shortHostname) + fmt.Fprintf(w, "put %s.%s.five-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate5(), shortHostname) + fmt.Fprintf(w, "put %s.%s.fifteen-minute %d %.2f host=%s\n", c.Prefix, name, now, t.Rate15(), shortHostname) + fmt.Fprintf(w, "put %s.%s.mean-rate %d %.2f host=%s\n", c.Prefix, name, now, t.RateMean(), shortHostname) + } + w.Flush() + }) + return nil +} diff --git a/vendor/github.com/rcrowley/go-metrics/registry.go b/vendor/github.com/rcrowley/go-metrics/registry.go new file mode 100644 index 00000000000..b3bab64e15b --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/registry.go @@ -0,0 +1,363 @@ +package metrics + +import ( + "fmt" + "reflect" + "strings" + "sync" +) + +// DuplicateMetric is the error returned by Registry.Register when a metric +// already exists. If you mean to Register that metric you must first +// Unregister the existing metric. +type DuplicateMetric string + +func (err DuplicateMetric) Error() string { + return fmt.Sprintf("duplicate metric: %s", string(err)) +} + +// A Registry holds references to a set of metrics by name and can iterate +// over them, calling callback functions provided by the user. +// +// This is an interface so as to encourage other structs to implement +// the Registry API as appropriate. +type Registry interface { + + // Call the given function for each registered metric. + Each(func(string, interface{})) + + // Get the metric by the given name or nil if none is registered. + Get(string) interface{} + + // GetAll metrics in the Registry. + GetAll() map[string]map[string]interface{} + + // Gets an existing metric or registers the given one. + // The interface can be the metric to register if not found in registry, + // or a function returning the metric for lazy instantiation. + GetOrRegister(string, interface{}) interface{} + + // Register the given metric under the given name. + Register(string, interface{}) error + + // Run all registered healthchecks. + RunHealthchecks() + + // Unregister the metric with the given name. + Unregister(string) + + // Unregister all metrics. (Mostly for testing.) + UnregisterAll() +} + +// The standard implementation of a Registry is a mutex-protected map +// of names to metrics. +type StandardRegistry struct { + metrics map[string]interface{} + mutex sync.RWMutex +} + +// Create a new registry. +func NewRegistry() Registry { + return &StandardRegistry{metrics: make(map[string]interface{})} +} + +// Call the given function for each registered metric. +func (r *StandardRegistry) Each(f func(string, interface{})) { + for name, i := range r.registered() { + f(name, i) + } +} + +// Get the metric by the given name or nil if none is registered. +func (r *StandardRegistry) Get(name string) interface{} { + r.mutex.RLock() + defer r.mutex.RUnlock() + return r.metrics[name] +} + +// Gets an existing metric or creates and registers a new one. Threadsafe +// alternative to calling Get and Register on failure. +// The interface can be the metric to register if not found in registry, +// or a function returning the metric for lazy instantiation. +func (r *StandardRegistry) GetOrRegister(name string, i interface{}) interface{} { + // access the read lock first which should be re-entrant + r.mutex.RLock() + metric, ok := r.metrics[name] + r.mutex.RUnlock() + if ok { + return metric + } + + // only take the write lock if we'll be modifying the metrics map + r.mutex.Lock() + defer r.mutex.Unlock() + if metric, ok := r.metrics[name]; ok { + return metric + } + if v := reflect.ValueOf(i); v.Kind() == reflect.Func { + i = v.Call(nil)[0].Interface() + } + r.register(name, i) + return i +} + +// Register the given metric under the given name. Returns a DuplicateMetric +// if a metric by the given name is already registered. +func (r *StandardRegistry) Register(name string, i interface{}) error { + r.mutex.Lock() + defer r.mutex.Unlock() + return r.register(name, i) +} + +// Run all registered healthchecks. +func (r *StandardRegistry) RunHealthchecks() { + r.mutex.RLock() + defer r.mutex.RUnlock() + for _, i := range r.metrics { + if h, ok := i.(Healthcheck); ok { + h.Check() + } + } +} + +// GetAll metrics in the Registry +func (r *StandardRegistry) GetAll() map[string]map[string]interface{} { + data := make(map[string]map[string]interface{}) + r.Each(func(name string, i interface{}) { + values := make(map[string]interface{}) + switch metric := i.(type) { + case Counter: + values["count"] = metric.Count() + case Gauge: + values["value"] = metric.Value() + case GaugeFloat64: + values["value"] = metric.Value() + case Healthcheck: + values["error"] = nil + metric.Check() + if err := metric.Error(); nil != err { + values["error"] = metric.Error().Error() + } + case Histogram: + h := metric.Snapshot() + ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + values["count"] = h.Count() + values["min"] = h.Min() + values["max"] = h.Max() + values["mean"] = h.Mean() + values["stddev"] = h.StdDev() + values["median"] = ps[0] + values["75%"] = ps[1] + values["95%"] = ps[2] + values["99%"] = ps[3] + values["99.9%"] = ps[4] + case Meter: + m := metric.Snapshot() + values["count"] = m.Count() + values["1m.rate"] = m.Rate1() + values["5m.rate"] = m.Rate5() + values["15m.rate"] = m.Rate15() + values["mean.rate"] = m.RateMean() + case Timer: + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + values["count"] = t.Count() + values["min"] = t.Min() + values["max"] = t.Max() + values["mean"] = t.Mean() + values["stddev"] = t.StdDev() + values["median"] = ps[0] + values["75%"] = ps[1] + values["95%"] = ps[2] + values["99%"] = ps[3] + values["99.9%"] = ps[4] + values["1m.rate"] = t.Rate1() + values["5m.rate"] = t.Rate5() + values["15m.rate"] = t.Rate15() + values["mean.rate"] = t.RateMean() + } + data[name] = values + }) + return data +} + +// Unregister the metric with the given name. +func (r *StandardRegistry) Unregister(name string) { + r.mutex.Lock() + defer r.mutex.Unlock() + r.stop(name) + delete(r.metrics, name) +} + +// Unregister all metrics. (Mostly for testing.) +func (r *StandardRegistry) UnregisterAll() { + r.mutex.Lock() + defer r.mutex.Unlock() + for name, _ := range r.metrics { + r.stop(name) + delete(r.metrics, name) + } +} + +func (r *StandardRegistry) register(name string, i interface{}) error { + if _, ok := r.metrics[name]; ok { + return DuplicateMetric(name) + } + switch i.(type) { + case Counter, Gauge, GaugeFloat64, Healthcheck, Histogram, Meter, Timer: + r.metrics[name] = i + } + return nil +} + +func (r *StandardRegistry) registered() map[string]interface{} { + r.mutex.Lock() + defer r.mutex.Unlock() + metrics := make(map[string]interface{}, len(r.metrics)) + for name, i := range r.metrics { + metrics[name] = i + } + return metrics +} + +func (r *StandardRegistry) stop(name string) { + if i, ok := r.metrics[name]; ok { + if s, ok := i.(Stoppable); ok { + s.Stop() + } + } +} + +// Stoppable defines the metrics which has to be stopped. +type Stoppable interface { + Stop() +} + +type PrefixedRegistry struct { + underlying Registry + prefix string +} + +func NewPrefixedRegistry(prefix string) Registry { + return &PrefixedRegistry{ + underlying: NewRegistry(), + prefix: prefix, + } +} + +func NewPrefixedChildRegistry(parent Registry, prefix string) Registry { + return &PrefixedRegistry{ + underlying: parent, + prefix: prefix, + } +} + +// Call the given function for each registered metric. +func (r *PrefixedRegistry) Each(fn func(string, interface{})) { + wrappedFn := func(prefix string) func(string, interface{}) { + return func(name string, iface interface{}) { + if strings.HasPrefix(name, prefix) { + fn(name, iface) + } else { + return + } + } + } + + baseRegistry, prefix := findPrefix(r, "") + baseRegistry.Each(wrappedFn(prefix)) +} + +func findPrefix(registry Registry, prefix string) (Registry, string) { + switch r := registry.(type) { + case *PrefixedRegistry: + return findPrefix(r.underlying, r.prefix+prefix) + case *StandardRegistry: + return r, prefix + } + return nil, "" +} + +// Get the metric by the given name or nil if none is registered. +func (r *PrefixedRegistry) Get(name string) interface{} { + realName := r.prefix + name + return r.underlying.Get(realName) +} + +// Gets an existing metric or registers the given one. +// The interface can be the metric to register if not found in registry, +// or a function returning the metric for lazy instantiation. +func (r *PrefixedRegistry) GetOrRegister(name string, metric interface{}) interface{} { + realName := r.prefix + name + return r.underlying.GetOrRegister(realName, metric) +} + +// Register the given metric under the given name. The name will be prefixed. +func (r *PrefixedRegistry) Register(name string, metric interface{}) error { + realName := r.prefix + name + return r.underlying.Register(realName, metric) +} + +// Run all registered healthchecks. +func (r *PrefixedRegistry) RunHealthchecks() { + r.underlying.RunHealthchecks() +} + +// GetAll metrics in the Registry +func (r *PrefixedRegistry) GetAll() map[string]map[string]interface{} { + return r.underlying.GetAll() +} + +// Unregister the metric with the given name. The name will be prefixed. +func (r *PrefixedRegistry) Unregister(name string) { + realName := r.prefix + name + r.underlying.Unregister(realName) +} + +// Unregister all metrics. (Mostly for testing.) +func (r *PrefixedRegistry) UnregisterAll() { + r.underlying.UnregisterAll() +} + +var DefaultRegistry Registry = NewRegistry() + +// Call the given function for each registered metric. +func Each(f func(string, interface{})) { + DefaultRegistry.Each(f) +} + +// Get the metric by the given name or nil if none is registered. +func Get(name string) interface{} { + return DefaultRegistry.Get(name) +} + +// Gets an existing metric or creates and registers a new one. Threadsafe +// alternative to calling Get and Register on failure. +func GetOrRegister(name string, i interface{}) interface{} { + return DefaultRegistry.GetOrRegister(name, i) +} + +// Register the given metric under the given name. Returns a DuplicateMetric +// if a metric by the given name is already registered. +func Register(name string, i interface{}) error { + return DefaultRegistry.Register(name, i) +} + +// Register the given metric under the given name. Panics if a metric by the +// given name is already registered. +func MustRegister(name string, i interface{}) { + if err := Register(name, i); err != nil { + panic(err) + } +} + +// Run all registered healthchecks. +func RunHealthchecks() { + DefaultRegistry.RunHealthchecks() +} + +// Unregister the metric with the given name. +func Unregister(name string) { + DefaultRegistry.Unregister(name) +} diff --git a/vendor/github.com/rcrowley/go-metrics/runtime.go b/vendor/github.com/rcrowley/go-metrics/runtime.go new file mode 100644 index 00000000000..11c6b785a0f --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/runtime.go @@ -0,0 +1,212 @@ +package metrics + +import ( + "runtime" + "runtime/pprof" + "time" +) + +var ( + memStats runtime.MemStats + runtimeMetrics struct { + MemStats struct { + Alloc Gauge + BuckHashSys Gauge + DebugGC Gauge + EnableGC Gauge + Frees Gauge + HeapAlloc Gauge + HeapIdle Gauge + HeapInuse Gauge + HeapObjects Gauge + HeapReleased Gauge + HeapSys Gauge + LastGC Gauge + Lookups Gauge + Mallocs Gauge + MCacheInuse Gauge + MCacheSys Gauge + MSpanInuse Gauge + MSpanSys Gauge + NextGC Gauge + NumGC Gauge + GCCPUFraction GaugeFloat64 + PauseNs Histogram + PauseTotalNs Gauge + StackInuse Gauge + StackSys Gauge + Sys Gauge + TotalAlloc Gauge + } + NumCgoCall Gauge + NumGoroutine Gauge + NumThread Gauge + ReadMemStats Timer + } + frees uint64 + lookups uint64 + mallocs uint64 + numGC uint32 + numCgoCalls int64 + + threadCreateProfile = pprof.Lookup("threadcreate") +) + +// Capture new values for the Go runtime statistics exported in +// runtime.MemStats. This is designed to be called as a goroutine. +func CaptureRuntimeMemStats(r Registry, d time.Duration) { + for _ = range time.Tick(d) { + CaptureRuntimeMemStatsOnce(r) + } +} + +// Capture new values for the Go runtime statistics exported in +// runtime.MemStats. This is designed to be called in a background +// goroutine. Giving a registry which has not been given to +// RegisterRuntimeMemStats will panic. +// +// Be very careful with this because runtime.ReadMemStats calls the C +// functions runtime·semacquire(&runtime·worldsema) and runtime·stoptheworld() +// and that last one does what it says on the tin. +func CaptureRuntimeMemStatsOnce(r Registry) { + t := time.Now() + runtime.ReadMemStats(&memStats) // This takes 50-200us. + runtimeMetrics.ReadMemStats.UpdateSince(t) + + runtimeMetrics.MemStats.Alloc.Update(int64(memStats.Alloc)) + runtimeMetrics.MemStats.BuckHashSys.Update(int64(memStats.BuckHashSys)) + if memStats.DebugGC { + runtimeMetrics.MemStats.DebugGC.Update(1) + } else { + runtimeMetrics.MemStats.DebugGC.Update(0) + } + if memStats.EnableGC { + runtimeMetrics.MemStats.EnableGC.Update(1) + } else { + runtimeMetrics.MemStats.EnableGC.Update(0) + } + + runtimeMetrics.MemStats.Frees.Update(int64(memStats.Frees - frees)) + runtimeMetrics.MemStats.HeapAlloc.Update(int64(memStats.HeapAlloc)) + runtimeMetrics.MemStats.HeapIdle.Update(int64(memStats.HeapIdle)) + runtimeMetrics.MemStats.HeapInuse.Update(int64(memStats.HeapInuse)) + runtimeMetrics.MemStats.HeapObjects.Update(int64(memStats.HeapObjects)) + runtimeMetrics.MemStats.HeapReleased.Update(int64(memStats.HeapReleased)) + runtimeMetrics.MemStats.HeapSys.Update(int64(memStats.HeapSys)) + runtimeMetrics.MemStats.LastGC.Update(int64(memStats.LastGC)) + runtimeMetrics.MemStats.Lookups.Update(int64(memStats.Lookups - lookups)) + runtimeMetrics.MemStats.Mallocs.Update(int64(memStats.Mallocs - mallocs)) + runtimeMetrics.MemStats.MCacheInuse.Update(int64(memStats.MCacheInuse)) + runtimeMetrics.MemStats.MCacheSys.Update(int64(memStats.MCacheSys)) + runtimeMetrics.MemStats.MSpanInuse.Update(int64(memStats.MSpanInuse)) + runtimeMetrics.MemStats.MSpanSys.Update(int64(memStats.MSpanSys)) + runtimeMetrics.MemStats.NextGC.Update(int64(memStats.NextGC)) + runtimeMetrics.MemStats.NumGC.Update(int64(memStats.NumGC - numGC)) + runtimeMetrics.MemStats.GCCPUFraction.Update(gcCPUFraction(&memStats)) + + // + i := numGC % uint32(len(memStats.PauseNs)) + ii := memStats.NumGC % uint32(len(memStats.PauseNs)) + if memStats.NumGC-numGC >= uint32(len(memStats.PauseNs)) { + for i = 0; i < uint32(len(memStats.PauseNs)); i++ { + runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) + } + } else { + if i > ii { + for ; i < uint32(len(memStats.PauseNs)); i++ { + runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) + } + i = 0 + } + for ; i < ii; i++ { + runtimeMetrics.MemStats.PauseNs.Update(int64(memStats.PauseNs[i])) + } + } + frees = memStats.Frees + lookups = memStats.Lookups + mallocs = memStats.Mallocs + numGC = memStats.NumGC + + runtimeMetrics.MemStats.PauseTotalNs.Update(int64(memStats.PauseTotalNs)) + runtimeMetrics.MemStats.StackInuse.Update(int64(memStats.StackInuse)) + runtimeMetrics.MemStats.StackSys.Update(int64(memStats.StackSys)) + runtimeMetrics.MemStats.Sys.Update(int64(memStats.Sys)) + runtimeMetrics.MemStats.TotalAlloc.Update(int64(memStats.TotalAlloc)) + + currentNumCgoCalls := numCgoCall() + runtimeMetrics.NumCgoCall.Update(currentNumCgoCalls - numCgoCalls) + numCgoCalls = currentNumCgoCalls + + runtimeMetrics.NumGoroutine.Update(int64(runtime.NumGoroutine())) + + runtimeMetrics.NumThread.Update(int64(threadCreateProfile.Count())) +} + +// Register runtimeMetrics for the Go runtime statistics exported in runtime and +// specifically runtime.MemStats. The runtimeMetrics are named by their +// fully-qualified Go symbols, i.e. runtime.MemStats.Alloc. +func RegisterRuntimeMemStats(r Registry) { + runtimeMetrics.MemStats.Alloc = NewGauge() + runtimeMetrics.MemStats.BuckHashSys = NewGauge() + runtimeMetrics.MemStats.DebugGC = NewGauge() + runtimeMetrics.MemStats.EnableGC = NewGauge() + runtimeMetrics.MemStats.Frees = NewGauge() + runtimeMetrics.MemStats.HeapAlloc = NewGauge() + runtimeMetrics.MemStats.HeapIdle = NewGauge() + runtimeMetrics.MemStats.HeapInuse = NewGauge() + runtimeMetrics.MemStats.HeapObjects = NewGauge() + runtimeMetrics.MemStats.HeapReleased = NewGauge() + runtimeMetrics.MemStats.HeapSys = NewGauge() + runtimeMetrics.MemStats.LastGC = NewGauge() + runtimeMetrics.MemStats.Lookups = NewGauge() + runtimeMetrics.MemStats.Mallocs = NewGauge() + runtimeMetrics.MemStats.MCacheInuse = NewGauge() + runtimeMetrics.MemStats.MCacheSys = NewGauge() + runtimeMetrics.MemStats.MSpanInuse = NewGauge() + runtimeMetrics.MemStats.MSpanSys = NewGauge() + runtimeMetrics.MemStats.NextGC = NewGauge() + runtimeMetrics.MemStats.NumGC = NewGauge() + runtimeMetrics.MemStats.GCCPUFraction = NewGaugeFloat64() + runtimeMetrics.MemStats.PauseNs = NewHistogram(NewExpDecaySample(1028, 0.015)) + runtimeMetrics.MemStats.PauseTotalNs = NewGauge() + runtimeMetrics.MemStats.StackInuse = NewGauge() + runtimeMetrics.MemStats.StackSys = NewGauge() + runtimeMetrics.MemStats.Sys = NewGauge() + runtimeMetrics.MemStats.TotalAlloc = NewGauge() + runtimeMetrics.NumCgoCall = NewGauge() + runtimeMetrics.NumGoroutine = NewGauge() + runtimeMetrics.NumThread = NewGauge() + runtimeMetrics.ReadMemStats = NewTimer() + + r.Register("runtime.MemStats.Alloc", runtimeMetrics.MemStats.Alloc) + r.Register("runtime.MemStats.BuckHashSys", runtimeMetrics.MemStats.BuckHashSys) + r.Register("runtime.MemStats.DebugGC", runtimeMetrics.MemStats.DebugGC) + r.Register("runtime.MemStats.EnableGC", runtimeMetrics.MemStats.EnableGC) + r.Register("runtime.MemStats.Frees", runtimeMetrics.MemStats.Frees) + r.Register("runtime.MemStats.HeapAlloc", runtimeMetrics.MemStats.HeapAlloc) + r.Register("runtime.MemStats.HeapIdle", runtimeMetrics.MemStats.HeapIdle) + r.Register("runtime.MemStats.HeapInuse", runtimeMetrics.MemStats.HeapInuse) + r.Register("runtime.MemStats.HeapObjects", runtimeMetrics.MemStats.HeapObjects) + r.Register("runtime.MemStats.HeapReleased", runtimeMetrics.MemStats.HeapReleased) + r.Register("runtime.MemStats.HeapSys", runtimeMetrics.MemStats.HeapSys) + r.Register("runtime.MemStats.LastGC", runtimeMetrics.MemStats.LastGC) + r.Register("runtime.MemStats.Lookups", runtimeMetrics.MemStats.Lookups) + r.Register("runtime.MemStats.Mallocs", runtimeMetrics.MemStats.Mallocs) + r.Register("runtime.MemStats.MCacheInuse", runtimeMetrics.MemStats.MCacheInuse) + r.Register("runtime.MemStats.MCacheSys", runtimeMetrics.MemStats.MCacheSys) + r.Register("runtime.MemStats.MSpanInuse", runtimeMetrics.MemStats.MSpanInuse) + r.Register("runtime.MemStats.MSpanSys", runtimeMetrics.MemStats.MSpanSys) + r.Register("runtime.MemStats.NextGC", runtimeMetrics.MemStats.NextGC) + r.Register("runtime.MemStats.NumGC", runtimeMetrics.MemStats.NumGC) + r.Register("runtime.MemStats.GCCPUFraction", runtimeMetrics.MemStats.GCCPUFraction) + r.Register("runtime.MemStats.PauseNs", runtimeMetrics.MemStats.PauseNs) + r.Register("runtime.MemStats.PauseTotalNs", runtimeMetrics.MemStats.PauseTotalNs) + r.Register("runtime.MemStats.StackInuse", runtimeMetrics.MemStats.StackInuse) + r.Register("runtime.MemStats.StackSys", runtimeMetrics.MemStats.StackSys) + r.Register("runtime.MemStats.Sys", runtimeMetrics.MemStats.Sys) + r.Register("runtime.MemStats.TotalAlloc", runtimeMetrics.MemStats.TotalAlloc) + r.Register("runtime.NumCgoCall", runtimeMetrics.NumCgoCall) + r.Register("runtime.NumGoroutine", runtimeMetrics.NumGoroutine) + r.Register("runtime.NumThread", runtimeMetrics.NumThread) + r.Register("runtime.ReadMemStats", runtimeMetrics.ReadMemStats) +} diff --git a/vendor/github.com/rcrowley/go-metrics/runtime_cgo.go b/vendor/github.com/rcrowley/go-metrics/runtime_cgo.go new file mode 100644 index 00000000000..e3391f4e89f --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/runtime_cgo.go @@ -0,0 +1,10 @@ +// +build cgo +// +build !appengine + +package metrics + +import "runtime" + +func numCgoCall() int64 { + return runtime.NumCgoCall() +} diff --git a/vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go b/vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go new file mode 100644 index 00000000000..ca12c05bac7 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/runtime_gccpufraction.go @@ -0,0 +1,9 @@ +// +build go1.5 + +package metrics + +import "runtime" + +func gcCPUFraction(memStats *runtime.MemStats) float64 { + return memStats.GCCPUFraction +} diff --git a/vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go b/vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go new file mode 100644 index 00000000000..616a3b4751b --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/runtime_no_cgo.go @@ -0,0 +1,7 @@ +// +build !cgo appengine + +package metrics + +func numCgoCall() int64 { + return 0 +} diff --git a/vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go b/vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go new file mode 100644 index 00000000000..be96aa6f1be --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/runtime_no_gccpufraction.go @@ -0,0 +1,9 @@ +// +build !go1.5 + +package metrics + +import "runtime" + +func gcCPUFraction(memStats *runtime.MemStats) float64 { + return 0 +} diff --git a/vendor/github.com/rcrowley/go-metrics/sample.go b/vendor/github.com/rcrowley/go-metrics/sample.go new file mode 100644 index 00000000000..fecee5ef68b --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/sample.go @@ -0,0 +1,616 @@ +package metrics + +import ( + "math" + "math/rand" + "sort" + "sync" + "time" +) + +const rescaleThreshold = time.Hour + +// Samples maintain a statistically-significant selection of values from +// a stream. +type Sample interface { + Clear() + Count() int64 + Max() int64 + Mean() float64 + Min() int64 + Percentile(float64) float64 + Percentiles([]float64) []float64 + Size() int + Snapshot() Sample + StdDev() float64 + Sum() int64 + Update(int64) + Values() []int64 + Variance() float64 +} + +// ExpDecaySample is an exponentially-decaying sample using a forward-decaying +// priority reservoir. See Cormode et al's "Forward Decay: A Practical Time +// Decay Model for Streaming Systems". +// +// +type ExpDecaySample struct { + alpha float64 + count int64 + mutex sync.Mutex + reservoirSize int + t0, t1 time.Time + values *expDecaySampleHeap +} + +// NewExpDecaySample constructs a new exponentially-decaying sample with the +// given reservoir size and alpha. +func NewExpDecaySample(reservoirSize int, alpha float64) Sample { + if UseNilMetrics { + return NilSample{} + } + s := &ExpDecaySample{ + alpha: alpha, + reservoirSize: reservoirSize, + t0: time.Now(), + values: newExpDecaySampleHeap(reservoirSize), + } + s.t1 = s.t0.Add(rescaleThreshold) + return s +} + +// Clear clears all samples. +func (s *ExpDecaySample) Clear() { + s.mutex.Lock() + defer s.mutex.Unlock() + s.count = 0 + s.t0 = time.Now() + s.t1 = s.t0.Add(rescaleThreshold) + s.values.Clear() +} + +// Count returns the number of samples recorded, which may exceed the +// reservoir size. +func (s *ExpDecaySample) Count() int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.count +} + +// Max returns the maximum value in the sample, which may not be the maximum +// value ever to be part of the sample. +func (s *ExpDecaySample) Max() int64 { + return SampleMax(s.Values()) +} + +// Mean returns the mean of the values in the sample. +func (s *ExpDecaySample) Mean() float64 { + return SampleMean(s.Values()) +} + +// Min returns the minimum value in the sample, which may not be the minimum +// value ever to be part of the sample. +func (s *ExpDecaySample) Min() int64 { + return SampleMin(s.Values()) +} + +// Percentile returns an arbitrary percentile of values in the sample. +func (s *ExpDecaySample) Percentile(p float64) float64 { + return SamplePercentile(s.Values(), p) +} + +// Percentiles returns a slice of arbitrary percentiles of values in the +// sample. +func (s *ExpDecaySample) Percentiles(ps []float64) []float64 { + return SamplePercentiles(s.Values(), ps) +} + +// Size returns the size of the sample, which is at most the reservoir size. +func (s *ExpDecaySample) Size() int { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.values.Size() +} + +// Snapshot returns a read-only copy of the sample. +func (s *ExpDecaySample) Snapshot() Sample { + s.mutex.Lock() + defer s.mutex.Unlock() + vals := s.values.Values() + values := make([]int64, len(vals)) + for i, v := range vals { + values[i] = v.v + } + return &SampleSnapshot{ + count: s.count, + values: values, + } +} + +// StdDev returns the standard deviation of the values in the sample. +func (s *ExpDecaySample) StdDev() float64 { + return SampleStdDev(s.Values()) +} + +// Sum returns the sum of the values in the sample. +func (s *ExpDecaySample) Sum() int64 { + return SampleSum(s.Values()) +} + +// Update samples a new value. +func (s *ExpDecaySample) Update(v int64) { + s.update(time.Now(), v) +} + +// Values returns a copy of the values in the sample. +func (s *ExpDecaySample) Values() []int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + vals := s.values.Values() + values := make([]int64, len(vals)) + for i, v := range vals { + values[i] = v.v + } + return values +} + +// Variance returns the variance of the values in the sample. +func (s *ExpDecaySample) Variance() float64 { + return SampleVariance(s.Values()) +} + +// update samples a new value at a particular timestamp. This is a method all +// its own to facilitate testing. +func (s *ExpDecaySample) update(t time.Time, v int64) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.count++ + if s.values.Size() == s.reservoirSize { + s.values.Pop() + } + s.values.Push(expDecaySample{ + k: math.Exp(t.Sub(s.t0).Seconds()*s.alpha) / rand.Float64(), + v: v, + }) + if t.After(s.t1) { + values := s.values.Values() + t0 := s.t0 + s.values.Clear() + s.t0 = t + s.t1 = s.t0.Add(rescaleThreshold) + for _, v := range values { + v.k = v.k * math.Exp(-s.alpha*s.t0.Sub(t0).Seconds()) + s.values.Push(v) + } + } +} + +// NilSample is a no-op Sample. +type NilSample struct{} + +// Clear is a no-op. +func (NilSample) Clear() {} + +// Count is a no-op. +func (NilSample) Count() int64 { return 0 } + +// Max is a no-op. +func (NilSample) Max() int64 { return 0 } + +// Mean is a no-op. +func (NilSample) Mean() float64 { return 0.0 } + +// Min is a no-op. +func (NilSample) Min() int64 { return 0 } + +// Percentile is a no-op. +func (NilSample) Percentile(p float64) float64 { return 0.0 } + +// Percentiles is a no-op. +func (NilSample) Percentiles(ps []float64) []float64 { + return make([]float64, len(ps)) +} + +// Size is a no-op. +func (NilSample) Size() int { return 0 } + +// Sample is a no-op. +func (NilSample) Snapshot() Sample { return NilSample{} } + +// StdDev is a no-op. +func (NilSample) StdDev() float64 { return 0.0 } + +// Sum is a no-op. +func (NilSample) Sum() int64 { return 0 } + +// Update is a no-op. +func (NilSample) Update(v int64) {} + +// Values is a no-op. +func (NilSample) Values() []int64 { return []int64{} } + +// Variance is a no-op. +func (NilSample) Variance() float64 { return 0.0 } + +// SampleMax returns the maximum value of the slice of int64. +func SampleMax(values []int64) int64 { + if 0 == len(values) { + return 0 + } + var max int64 = math.MinInt64 + for _, v := range values { + if max < v { + max = v + } + } + return max +} + +// SampleMean returns the mean value of the slice of int64. +func SampleMean(values []int64) float64 { + if 0 == len(values) { + return 0.0 + } + return float64(SampleSum(values)) / float64(len(values)) +} + +// SampleMin returns the minimum value of the slice of int64. +func SampleMin(values []int64) int64 { + if 0 == len(values) { + return 0 + } + var min int64 = math.MaxInt64 + for _, v := range values { + if min > v { + min = v + } + } + return min +} + +// SamplePercentiles returns an arbitrary percentile of the slice of int64. +func SamplePercentile(values int64Slice, p float64) float64 { + return SamplePercentiles(values, []float64{p})[0] +} + +// SamplePercentiles returns a slice of arbitrary percentiles of the slice of +// int64. +func SamplePercentiles(values int64Slice, ps []float64) []float64 { + scores := make([]float64, len(ps)) + size := len(values) + if size > 0 { + sort.Sort(values) + for i, p := range ps { + pos := p * float64(size+1) + if pos < 1.0 { + scores[i] = float64(values[0]) + } else if pos >= float64(size) { + scores[i] = float64(values[size-1]) + } else { + lower := float64(values[int(pos)-1]) + upper := float64(values[int(pos)]) + scores[i] = lower + (pos-math.Floor(pos))*(upper-lower) + } + } + } + return scores +} + +// SampleSnapshot is a read-only copy of another Sample. +type SampleSnapshot struct { + count int64 + values []int64 +} + +func NewSampleSnapshot(count int64, values []int64) *SampleSnapshot { + return &SampleSnapshot{ + count: count, + values: values, + } +} + +// Clear panics. +func (*SampleSnapshot) Clear() { + panic("Clear called on a SampleSnapshot") +} + +// Count returns the count of inputs at the time the snapshot was taken. +func (s *SampleSnapshot) Count() int64 { return s.count } + +// Max returns the maximal value at the time the snapshot was taken. +func (s *SampleSnapshot) Max() int64 { return SampleMax(s.values) } + +// Mean returns the mean value at the time the snapshot was taken. +func (s *SampleSnapshot) Mean() float64 { return SampleMean(s.values) } + +// Min returns the minimal value at the time the snapshot was taken. +func (s *SampleSnapshot) Min() int64 { return SampleMin(s.values) } + +// Percentile returns an arbitrary percentile of values at the time the +// snapshot was taken. +func (s *SampleSnapshot) Percentile(p float64) float64 { + return SamplePercentile(s.values, p) +} + +// Percentiles returns a slice of arbitrary percentiles of values at the time +// the snapshot was taken. +func (s *SampleSnapshot) Percentiles(ps []float64) []float64 { + return SamplePercentiles(s.values, ps) +} + +// Size returns the size of the sample at the time the snapshot was taken. +func (s *SampleSnapshot) Size() int { return len(s.values) } + +// Snapshot returns the snapshot. +func (s *SampleSnapshot) Snapshot() Sample { return s } + +// StdDev returns the standard deviation of values at the time the snapshot was +// taken. +func (s *SampleSnapshot) StdDev() float64 { return SampleStdDev(s.values) } + +// Sum returns the sum of values at the time the snapshot was taken. +func (s *SampleSnapshot) Sum() int64 { return SampleSum(s.values) } + +// Update panics. +func (*SampleSnapshot) Update(int64) { + panic("Update called on a SampleSnapshot") +} + +// Values returns a copy of the values in the sample. +func (s *SampleSnapshot) Values() []int64 { + values := make([]int64, len(s.values)) + copy(values, s.values) + return values +} + +// Variance returns the variance of values at the time the snapshot was taken. +func (s *SampleSnapshot) Variance() float64 { return SampleVariance(s.values) } + +// SampleStdDev returns the standard deviation of the slice of int64. +func SampleStdDev(values []int64) float64 { + return math.Sqrt(SampleVariance(values)) +} + +// SampleSum returns the sum of the slice of int64. +func SampleSum(values []int64) int64 { + var sum int64 + for _, v := range values { + sum += v + } + return sum +} + +// SampleVariance returns the variance of the slice of int64. +func SampleVariance(values []int64) float64 { + if 0 == len(values) { + return 0.0 + } + m := SampleMean(values) + var sum float64 + for _, v := range values { + d := float64(v) - m + sum += d * d + } + return sum / float64(len(values)) +} + +// A uniform sample using Vitter's Algorithm R. +// +// +type UniformSample struct { + count int64 + mutex sync.Mutex + reservoirSize int + values []int64 +} + +// NewUniformSample constructs a new uniform sample with the given reservoir +// size. +func NewUniformSample(reservoirSize int) Sample { + if UseNilMetrics { + return NilSample{} + } + return &UniformSample{ + reservoirSize: reservoirSize, + values: make([]int64, 0, reservoirSize), + } +} + +// Clear clears all samples. +func (s *UniformSample) Clear() { + s.mutex.Lock() + defer s.mutex.Unlock() + s.count = 0 + s.values = make([]int64, 0, s.reservoirSize) +} + +// Count returns the number of samples recorded, which may exceed the +// reservoir size. +func (s *UniformSample) Count() int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return s.count +} + +// Max returns the maximum value in the sample, which may not be the maximum +// value ever to be part of the sample. +func (s *UniformSample) Max() int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SampleMax(s.values) +} + +// Mean returns the mean of the values in the sample. +func (s *UniformSample) Mean() float64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SampleMean(s.values) +} + +// Min returns the minimum value in the sample, which may not be the minimum +// value ever to be part of the sample. +func (s *UniformSample) Min() int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SampleMin(s.values) +} + +// Percentile returns an arbitrary percentile of values in the sample. +func (s *UniformSample) Percentile(p float64) float64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SamplePercentile(s.values, p) +} + +// Percentiles returns a slice of arbitrary percentiles of values in the +// sample. +func (s *UniformSample) Percentiles(ps []float64) []float64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SamplePercentiles(s.values, ps) +} + +// Size returns the size of the sample, which is at most the reservoir size. +func (s *UniformSample) Size() int { + s.mutex.Lock() + defer s.mutex.Unlock() + return len(s.values) +} + +// Snapshot returns a read-only copy of the sample. +func (s *UniformSample) Snapshot() Sample { + s.mutex.Lock() + defer s.mutex.Unlock() + values := make([]int64, len(s.values)) + copy(values, s.values) + return &SampleSnapshot{ + count: s.count, + values: values, + } +} + +// StdDev returns the standard deviation of the values in the sample. +func (s *UniformSample) StdDev() float64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SampleStdDev(s.values) +} + +// Sum returns the sum of the values in the sample. +func (s *UniformSample) Sum() int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SampleSum(s.values) +} + +// Update samples a new value. +func (s *UniformSample) Update(v int64) { + s.mutex.Lock() + defer s.mutex.Unlock() + s.count++ + if len(s.values) < s.reservoirSize { + s.values = append(s.values, v) + } else { + r := rand.Int63n(s.count) + if r < int64(len(s.values)) { + s.values[int(r)] = v + } + } +} + +// Values returns a copy of the values in the sample. +func (s *UniformSample) Values() []int64 { + s.mutex.Lock() + defer s.mutex.Unlock() + values := make([]int64, len(s.values)) + copy(values, s.values) + return values +} + +// Variance returns the variance of the values in the sample. +func (s *UniformSample) Variance() float64 { + s.mutex.Lock() + defer s.mutex.Unlock() + return SampleVariance(s.values) +} + +// expDecaySample represents an individual sample in a heap. +type expDecaySample struct { + k float64 + v int64 +} + +func newExpDecaySampleHeap(reservoirSize int) *expDecaySampleHeap { + return &expDecaySampleHeap{make([]expDecaySample, 0, reservoirSize)} +} + +// expDecaySampleHeap is a min-heap of expDecaySamples. +// The internal implementation is copied from the standard library's container/heap +type expDecaySampleHeap struct { + s []expDecaySample +} + +func (h *expDecaySampleHeap) Clear() { + h.s = h.s[:0] +} + +func (h *expDecaySampleHeap) Push(s expDecaySample) { + n := len(h.s) + h.s = h.s[0 : n+1] + h.s[n] = s + h.up(n) +} + +func (h *expDecaySampleHeap) Pop() expDecaySample { + n := len(h.s) - 1 + h.s[0], h.s[n] = h.s[n], h.s[0] + h.down(0, n) + + n = len(h.s) + s := h.s[n-1] + h.s = h.s[0 : n-1] + return s +} + +func (h *expDecaySampleHeap) Size() int { + return len(h.s) +} + +func (h *expDecaySampleHeap) Values() []expDecaySample { + return h.s +} + +func (h *expDecaySampleHeap) up(j int) { + for { + i := (j - 1) / 2 // parent + if i == j || !(h.s[j].k < h.s[i].k) { + break + } + h.s[i], h.s[j] = h.s[j], h.s[i] + j = i + } +} + +func (h *expDecaySampleHeap) down(i, n int) { + for { + j1 := 2*i + 1 + if j1 >= n || j1 < 0 { // j1 < 0 after int overflow + break + } + j := j1 // left child + if j2 := j1 + 1; j2 < n && !(h.s[j1].k < h.s[j2].k) { + j = j2 // = 2*i + 2 // right child + } + if !(h.s[j].k < h.s[i].k) { + break + } + h.s[i], h.s[j] = h.s[j], h.s[i] + i = j + } +} + +type int64Slice []int64 + +func (p int64Slice) Len() int { return len(p) } +func (p int64Slice) Less(i, j int) bool { return p[i] < p[j] } +func (p int64Slice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } diff --git a/vendor/github.com/rcrowley/go-metrics/syslog.go b/vendor/github.com/rcrowley/go-metrics/syslog.go new file mode 100644 index 00000000000..693f190855c --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/syslog.go @@ -0,0 +1,78 @@ +// +build !windows + +package metrics + +import ( + "fmt" + "log/syslog" + "time" +) + +// Output each metric in the given registry to syslog periodically using +// the given syslogger. +func Syslog(r Registry, d time.Duration, w *syslog.Writer) { + for _ = range time.Tick(d) { + r.Each(func(name string, i interface{}) { + switch metric := i.(type) { + case Counter: + w.Info(fmt.Sprintf("counter %s: count: %d", name, metric.Count())) + case Gauge: + w.Info(fmt.Sprintf("gauge %s: value: %d", name, metric.Value())) + case GaugeFloat64: + w.Info(fmt.Sprintf("gauge %s: value: %f", name, metric.Value())) + case Healthcheck: + metric.Check() + w.Info(fmt.Sprintf("healthcheck %s: error: %v", name, metric.Error())) + case Histogram: + h := metric.Snapshot() + ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + w.Info(fmt.Sprintf( + "histogram %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f", + name, + h.Count(), + h.Min(), + h.Max(), + h.Mean(), + h.StdDev(), + ps[0], + ps[1], + ps[2], + ps[3], + ps[4], + )) + case Meter: + m := metric.Snapshot() + w.Info(fmt.Sprintf( + "meter %s: count: %d 1-min: %.2f 5-min: %.2f 15-min: %.2f mean: %.2f", + name, + m.Count(), + m.Rate1(), + m.Rate5(), + m.Rate15(), + m.RateMean(), + )) + case Timer: + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + w.Info(fmt.Sprintf( + "timer %s: count: %d min: %d max: %d mean: %.2f stddev: %.2f median: %.2f 75%%: %.2f 95%%: %.2f 99%%: %.2f 99.9%%: %.2f 1-min: %.2f 5-min: %.2f 15-min: %.2f mean-rate: %.2f", + name, + t.Count(), + t.Min(), + t.Max(), + t.Mean(), + t.StdDev(), + ps[0], + ps[1], + ps[2], + ps[3], + ps[4], + t.Rate1(), + t.Rate5(), + t.Rate15(), + t.RateMean(), + )) + } + }) + } +} diff --git a/vendor/github.com/rcrowley/go-metrics/timer.go b/vendor/github.com/rcrowley/go-metrics/timer.go new file mode 100644 index 00000000000..d6ec4c6260f --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/timer.go @@ -0,0 +1,329 @@ +package metrics + +import ( + "sync" + "time" +) + +// Timers capture the duration and rate of events. +type Timer interface { + Count() int64 + Max() int64 + Mean() float64 + Min() int64 + Percentile(float64) float64 + Percentiles([]float64) []float64 + Rate1() float64 + Rate5() float64 + Rate15() float64 + RateMean() float64 + Snapshot() Timer + StdDev() float64 + Stop() + Sum() int64 + Time(func()) + Update(time.Duration) + UpdateSince(time.Time) + Variance() float64 +} + +// GetOrRegisterTimer returns an existing Timer or constructs and registers a +// new StandardTimer. +// Be sure to unregister the meter from the registry once it is of no use to +// allow for garbage collection. +func GetOrRegisterTimer(name string, r Registry) Timer { + if nil == r { + r = DefaultRegistry + } + return r.GetOrRegister(name, NewTimer).(Timer) +} + +// NewCustomTimer constructs a new StandardTimer from a Histogram and a Meter. +// Be sure to call Stop() once the timer is of no use to allow for garbage collection. +func NewCustomTimer(h Histogram, m Meter) Timer { + if UseNilMetrics { + return NilTimer{} + } + return &StandardTimer{ + histogram: h, + meter: m, + } +} + +// NewRegisteredTimer constructs and registers a new StandardTimer. +// Be sure to unregister the meter from the registry once it is of no use to +// allow for garbage collection. +func NewRegisteredTimer(name string, r Registry) Timer { + c := NewTimer() + if nil == r { + r = DefaultRegistry + } + r.Register(name, c) + return c +} + +// NewTimer constructs a new StandardTimer using an exponentially-decaying +// sample with the same reservoir size and alpha as UNIX load averages. +// Be sure to call Stop() once the timer is of no use to allow for garbage collection. +func NewTimer() Timer { + if UseNilMetrics { + return NilTimer{} + } + return &StandardTimer{ + histogram: NewHistogram(NewExpDecaySample(1028, 0.015)), + meter: NewMeter(), + } +} + +// NilTimer is a no-op Timer. +type NilTimer struct { + h Histogram + m Meter +} + +// Count is a no-op. +func (NilTimer) Count() int64 { return 0 } + +// Max is a no-op. +func (NilTimer) Max() int64 { return 0 } + +// Mean is a no-op. +func (NilTimer) Mean() float64 { return 0.0 } + +// Min is a no-op. +func (NilTimer) Min() int64 { return 0 } + +// Percentile is a no-op. +func (NilTimer) Percentile(p float64) float64 { return 0.0 } + +// Percentiles is a no-op. +func (NilTimer) Percentiles(ps []float64) []float64 { + return make([]float64, len(ps)) +} + +// Rate1 is a no-op. +func (NilTimer) Rate1() float64 { return 0.0 } + +// Rate5 is a no-op. +func (NilTimer) Rate5() float64 { return 0.0 } + +// Rate15 is a no-op. +func (NilTimer) Rate15() float64 { return 0.0 } + +// RateMean is a no-op. +func (NilTimer) RateMean() float64 { return 0.0 } + +// Snapshot is a no-op. +func (NilTimer) Snapshot() Timer { return NilTimer{} } + +// StdDev is a no-op. +func (NilTimer) StdDev() float64 { return 0.0 } + +// Stop is a no-op. +func (NilTimer) Stop() {} + +// Sum is a no-op. +func (NilTimer) Sum() int64 { return 0 } + +// Time is a no-op. +func (NilTimer) Time(func()) {} + +// Update is a no-op. +func (NilTimer) Update(time.Duration) {} + +// UpdateSince is a no-op. +func (NilTimer) UpdateSince(time.Time) {} + +// Variance is a no-op. +func (NilTimer) Variance() float64 { return 0.0 } + +// StandardTimer is the standard implementation of a Timer and uses a Histogram +// and Meter. +type StandardTimer struct { + histogram Histogram + meter Meter + mutex sync.Mutex +} + +// Count returns the number of events recorded. +func (t *StandardTimer) Count() int64 { + return t.histogram.Count() +} + +// Max returns the maximum value in the sample. +func (t *StandardTimer) Max() int64 { + return t.histogram.Max() +} + +// Mean returns the mean of the values in the sample. +func (t *StandardTimer) Mean() float64 { + return t.histogram.Mean() +} + +// Min returns the minimum value in the sample. +func (t *StandardTimer) Min() int64 { + return t.histogram.Min() +} + +// Percentile returns an arbitrary percentile of the values in the sample. +func (t *StandardTimer) Percentile(p float64) float64 { + return t.histogram.Percentile(p) +} + +// Percentiles returns a slice of arbitrary percentiles of the values in the +// sample. +func (t *StandardTimer) Percentiles(ps []float64) []float64 { + return t.histogram.Percentiles(ps) +} + +// Rate1 returns the one-minute moving average rate of events per second. +func (t *StandardTimer) Rate1() float64 { + return t.meter.Rate1() +} + +// Rate5 returns the five-minute moving average rate of events per second. +func (t *StandardTimer) Rate5() float64 { + return t.meter.Rate5() +} + +// Rate15 returns the fifteen-minute moving average rate of events per second. +func (t *StandardTimer) Rate15() float64 { + return t.meter.Rate15() +} + +// RateMean returns the meter's mean rate of events per second. +func (t *StandardTimer) RateMean() float64 { + return t.meter.RateMean() +} + +// Snapshot returns a read-only copy of the timer. +func (t *StandardTimer) Snapshot() Timer { + t.mutex.Lock() + defer t.mutex.Unlock() + return &TimerSnapshot{ + histogram: t.histogram.Snapshot().(*HistogramSnapshot), + meter: t.meter.Snapshot().(*MeterSnapshot), + } +} + +// StdDev returns the standard deviation of the values in the sample. +func (t *StandardTimer) StdDev() float64 { + return t.histogram.StdDev() +} + +// Stop stops the meter. +func (t *StandardTimer) Stop() { + t.meter.Stop() +} + +// Sum returns the sum in the sample. +func (t *StandardTimer) Sum() int64 { + return t.histogram.Sum() +} + +// Record the duration of the execution of the given function. +func (t *StandardTimer) Time(f func()) { + ts := time.Now() + f() + t.Update(time.Since(ts)) +} + +// Record the duration of an event. +func (t *StandardTimer) Update(d time.Duration) { + t.mutex.Lock() + defer t.mutex.Unlock() + t.histogram.Update(int64(d)) + t.meter.Mark(1) +} + +// Record the duration of an event that started at a time and ends now. +func (t *StandardTimer) UpdateSince(ts time.Time) { + t.mutex.Lock() + defer t.mutex.Unlock() + t.histogram.Update(int64(time.Since(ts))) + t.meter.Mark(1) +} + +// Variance returns the variance of the values in the sample. +func (t *StandardTimer) Variance() float64 { + return t.histogram.Variance() +} + +// TimerSnapshot is a read-only copy of another Timer. +type TimerSnapshot struct { + histogram *HistogramSnapshot + meter *MeterSnapshot +} + +// Count returns the number of events recorded at the time the snapshot was +// taken. +func (t *TimerSnapshot) Count() int64 { return t.histogram.Count() } + +// Max returns the maximum value at the time the snapshot was taken. +func (t *TimerSnapshot) Max() int64 { return t.histogram.Max() } + +// Mean returns the mean value at the time the snapshot was taken. +func (t *TimerSnapshot) Mean() float64 { return t.histogram.Mean() } + +// Min returns the minimum value at the time the snapshot was taken. +func (t *TimerSnapshot) Min() int64 { return t.histogram.Min() } + +// Percentile returns an arbitrary percentile of sampled values at the time the +// snapshot was taken. +func (t *TimerSnapshot) Percentile(p float64) float64 { + return t.histogram.Percentile(p) +} + +// Percentiles returns a slice of arbitrary percentiles of sampled values at +// the time the snapshot was taken. +func (t *TimerSnapshot) Percentiles(ps []float64) []float64 { + return t.histogram.Percentiles(ps) +} + +// Rate1 returns the one-minute moving average rate of events per second at the +// time the snapshot was taken. +func (t *TimerSnapshot) Rate1() float64 { return t.meter.Rate1() } + +// Rate5 returns the five-minute moving average rate of events per second at +// the time the snapshot was taken. +func (t *TimerSnapshot) Rate5() float64 { return t.meter.Rate5() } + +// Rate15 returns the fifteen-minute moving average rate of events per second +// at the time the snapshot was taken. +func (t *TimerSnapshot) Rate15() float64 { return t.meter.Rate15() } + +// RateMean returns the meter's mean rate of events per second at the time the +// snapshot was taken. +func (t *TimerSnapshot) RateMean() float64 { return t.meter.RateMean() } + +// Snapshot returns the snapshot. +func (t *TimerSnapshot) Snapshot() Timer { return t } + +// StdDev returns the standard deviation of the values at the time the snapshot +// was taken. +func (t *TimerSnapshot) StdDev() float64 { return t.histogram.StdDev() } + +// Stop is a no-op. +func (t *TimerSnapshot) Stop() {} + +// Sum returns the sum at the time the snapshot was taken. +func (t *TimerSnapshot) Sum() int64 { return t.histogram.Sum() } + +// Time panics. +func (*TimerSnapshot) Time(func()) { + panic("Time called on a TimerSnapshot") +} + +// Update panics. +func (*TimerSnapshot) Update(time.Duration) { + panic("Update called on a TimerSnapshot") +} + +// UpdateSince panics. +func (*TimerSnapshot) UpdateSince(time.Time) { + panic("UpdateSince called on a TimerSnapshot") +} + +// Variance returns the variance of the values at the time the snapshot was +// taken. +func (t *TimerSnapshot) Variance() float64 { return t.histogram.Variance() } diff --git a/vendor/github.com/rcrowley/go-metrics/writer.go b/vendor/github.com/rcrowley/go-metrics/writer.go new file mode 100644 index 00000000000..091e971d2e6 --- /dev/null +++ b/vendor/github.com/rcrowley/go-metrics/writer.go @@ -0,0 +1,100 @@ +package metrics + +import ( + "fmt" + "io" + "sort" + "time" +) + +// Write sorts writes each metric in the given registry periodically to the +// given io.Writer. +func Write(r Registry, d time.Duration, w io.Writer) { + for _ = range time.Tick(d) { + WriteOnce(r, w) + } +} + +// WriteOnce sorts and writes metrics in the given registry to the given +// io.Writer. +func WriteOnce(r Registry, w io.Writer) { + var namedMetrics namedMetricSlice + r.Each(func(name string, i interface{}) { + namedMetrics = append(namedMetrics, namedMetric{name, i}) + }) + + sort.Sort(namedMetrics) + for _, namedMetric := range namedMetrics { + switch metric := namedMetric.m.(type) { + case Counter: + fmt.Fprintf(w, "counter %s\n", namedMetric.name) + fmt.Fprintf(w, " count: %9d\n", metric.Count()) + case Gauge: + fmt.Fprintf(w, "gauge %s\n", namedMetric.name) + fmt.Fprintf(w, " value: %9d\n", metric.Value()) + case GaugeFloat64: + fmt.Fprintf(w, "gauge %s\n", namedMetric.name) + fmt.Fprintf(w, " value: %f\n", metric.Value()) + case Healthcheck: + metric.Check() + fmt.Fprintf(w, "healthcheck %s\n", namedMetric.name) + fmt.Fprintf(w, " error: %v\n", metric.Error()) + case Histogram: + h := metric.Snapshot() + ps := h.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + fmt.Fprintf(w, "histogram %s\n", namedMetric.name) + fmt.Fprintf(w, " count: %9d\n", h.Count()) + fmt.Fprintf(w, " min: %9d\n", h.Min()) + fmt.Fprintf(w, " max: %9d\n", h.Max()) + fmt.Fprintf(w, " mean: %12.2f\n", h.Mean()) + fmt.Fprintf(w, " stddev: %12.2f\n", h.StdDev()) + fmt.Fprintf(w, " median: %12.2f\n", ps[0]) + fmt.Fprintf(w, " 75%%: %12.2f\n", ps[1]) + fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2]) + fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3]) + fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4]) + case Meter: + m := metric.Snapshot() + fmt.Fprintf(w, "meter %s\n", namedMetric.name) + fmt.Fprintf(w, " count: %9d\n", m.Count()) + fmt.Fprintf(w, " 1-min rate: %12.2f\n", m.Rate1()) + fmt.Fprintf(w, " 5-min rate: %12.2f\n", m.Rate5()) + fmt.Fprintf(w, " 15-min rate: %12.2f\n", m.Rate15()) + fmt.Fprintf(w, " mean rate: %12.2f\n", m.RateMean()) + case Timer: + t := metric.Snapshot() + ps := t.Percentiles([]float64{0.5, 0.75, 0.95, 0.99, 0.999}) + fmt.Fprintf(w, "timer %s\n", namedMetric.name) + fmt.Fprintf(w, " count: %9d\n", t.Count()) + fmt.Fprintf(w, " min: %9d\n", t.Min()) + fmt.Fprintf(w, " max: %9d\n", t.Max()) + fmt.Fprintf(w, " mean: %12.2f\n", t.Mean()) + fmt.Fprintf(w, " stddev: %12.2f\n", t.StdDev()) + fmt.Fprintf(w, " median: %12.2f\n", ps[0]) + fmt.Fprintf(w, " 75%%: %12.2f\n", ps[1]) + fmt.Fprintf(w, " 95%%: %12.2f\n", ps[2]) + fmt.Fprintf(w, " 99%%: %12.2f\n", ps[3]) + fmt.Fprintf(w, " 99.9%%: %12.2f\n", ps[4]) + fmt.Fprintf(w, " 1-min rate: %12.2f\n", t.Rate1()) + fmt.Fprintf(w, " 5-min rate: %12.2f\n", t.Rate5()) + fmt.Fprintf(w, " 15-min rate: %12.2f\n", t.Rate15()) + fmt.Fprintf(w, " mean rate: %12.2f\n", t.RateMean()) + } + } +} + +type namedMetric struct { + name string + m interface{} +} + +// namedMetricSlice is a slice of namedMetrics that implements sort.Interface. +type namedMetricSlice []namedMetric + +func (nms namedMetricSlice) Len() int { return len(nms) } + +func (nms namedMetricSlice) Swap(i, j int) { nms[i], nms[j] = nms[j], nms[i] } + +func (nms namedMetricSlice) Less(i, j int) bool { + return nms[i].name < nms[j].name +} From 870182bf426774607b8447e90dc1ac17e268bb17 Mon Sep 17 00:00:00 2001 From: Galo Navarro Date: Fri, 9 Nov 2018 01:29:44 +0100 Subject: [PATCH 33/54] Add tests for pkg/apis/eventing/v1alpha1/ (#597) Signed-off-by: Galo Navarro --- ...ubscribable_channelable_validation_test.go | 177 ++++++++++++++++++ 1 file changed, 177 insertions(+) create mode 100644 pkg/apis/eventing/v1alpha1/subscribable_channelable_validation_test.go diff --git a/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation_test.go b/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation_test.go new file mode 100644 index 00000000000..bf76f614cc7 --- /dev/null +++ b/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation_test.go @@ -0,0 +1,177 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "github.com/google/go-cmp/cmp" + "github.com/knative/pkg/apis" + corev1 "k8s.io/api/core/v1" + "testing" +) + +var validationTests = []struct { + name string + ref corev1.ObjectReference + want *apis.FieldError +}{ + { + name: "invalid kind", + ref: corev1.ObjectReference{ + Name: "boaty-mcboatface", + APIVersion: "eventing.knative.dev/v1alpha1", + Kind: "Strait", + }, + want: &apis.FieldError{ + Message: "invalid value \"Strait\"", + Paths: []string{"kind"}, + Details: "only 'Channel' kind is allowed", + }, + }, + { + name: "invalid api version", + ref: corev1.ObjectReference{ + Name: "boaty-mcboatface", + APIVersion: "eventing.knative.dev/v1alpha2", + Kind: "Channel", + }, + want: &apis.FieldError{ + Message: `invalid value "eventing.knative.dev/v1alpha2"`, + Paths: []string{"apiVersion"}, + Details: "only eventing.knative.dev/v1alpha1 " + + "is allowed for apiVersion", + }, + }, + { + name: "valid channel", + ref: corev1.ObjectReference{ + Name: "boaty-mcboatface", + APIVersion: "eventing.knative.dev/v1alpha1", + Kind: "Channel", + }, + want: nil, + }, +} + +func TestIsChannelEmpty(t *testing.T) { + name := "non empty" + t.Run(name, func(t *testing.T) { + r := corev1.ObjectReference{ + Name: "boaty-mcboatface", + APIVersion: "eventing.knative.dev/v1alpha1", + Kind: "Channel", + } + if isChannelEmpty(r) { + t.Errorf("%s: isChannelEmpty(%s) should be false", name, r) + } + }) + + name = "empty" + t.Run(name, func(t *testing.T) { + r := corev1.ObjectReference{} + if !isChannelEmpty(r) { + t.Errorf("%s: isChannelEmpty(%s) should be true", name, r) + } + }) +} + +func TestIsValidChannel(t *testing.T) { + for _, test := range validationTests { + t.Run(test.name, func(t *testing.T) { + got := isValidChannel(test.ref) + if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { + t.Errorf("%s: validation (-want, +got) = %v", test.name, diff) + } + }) + } +} +func TestIsValidSubscribable(t *testing.T) { + for _, test := range validationTests { + t.Run(test.name, func(t *testing.T) { + got := isValidSubscribable(test.ref) + if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { + t.Errorf("%s: validation (-want, +got) = %v", test.name, diff) + } + }) + } +} + +func TestIsValidObjectReference(t *testing.T) { + tests := []struct { + name string + ref corev1.ObjectReference + want []*apis.FieldError + }{ + { + name: "missing api version and kind", + ref: corev1.ObjectReference{ + Name: "boaty-mcboatface", + APIVersion: "", + Kind: "", + }, + want: []*apis.FieldError{ + apis.ErrMissingField("apiVersion"), + apis.ErrMissingField("kind"), + }, + }, + { + name: "missing name", + ref: corev1.ObjectReference{ + Name: "", + APIVersion: "eventing.knative.dev/v1alpha1", + Kind: "Strait", + }, + want: []*apis.FieldError{ + apis.ErrMissingField("name"), + }, + }, + { + name: "missing all", + ref: corev1.ObjectReference{ + Name: "", + APIVersion: "", + Kind: "", + }, + want: []*apis.FieldError{ + apis.ErrMissingField("name"), + apis.ErrMissingField("apiVersion"), + apis.ErrMissingField("kind"), + }, + }, + { + name: "missing none", + ref: corev1.ObjectReference{ + Name: "kind", + APIVersion: "eventing.knative.dev/v1alpha1", + Kind: "Channel", + }, + want: []*apis.FieldError{}, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + allWanted := &apis.FieldError{} + for _, fe := range test.want { + allWanted = allWanted.Also(fe) + } + got := isValidObjectReference(test.ref) + if diff := cmp.Diff(allWanted.Error(), got.Error()); diff != "" { + t.Errorf("%s: validation (-want, +got) = %v", test.name, diff) + } + }) + } +} From 7dc7624e34a4da510648d5d58235638502dd06a9 Mon Sep 17 00:00:00 2001 From: Matt Moore Date: Thu, 8 Nov 2018 17:21:44 -0800 Subject: [PATCH 34/54] Pull in support for non-terminal conditions (#598) This pulls in the support for non-terminal conditions, which includes a new Severity field (as discussed in the API WG meeting). --- Gopkg.lock | 5 +- Gopkg.toml | 4 +- .../eventing/v1alpha1/channel_types_test.go | 9 +- .../v1alpha1/subscription_types_test.go | 4 +- .../reconcile_test.go | 11 ++- .../kafka/controller/reconcile_test.go | 5 +- .../pkg/apis/duck/v1alpha1/condition_set.go | 86 +++++++++++++------ .../apis/duck/v1alpha1/conditions_types.go | 21 ++++- .../knative/pkg/changeset/commit.go | 64 ++++++++++++++ .../github.com/knative/pkg/changeset/doc.go | 23 +++++ .../github.com/knative/pkg/logging/config.go | 16 +++- .../knative/pkg/logging/logkey/constants.go | 4 + 12 files changed, 204 insertions(+), 48 deletions(-) create mode 100644 vendor/github.com/knative/pkg/changeset/commit.go create mode 100644 vendor/github.com/knative/pkg/changeset/doc.go diff --git a/Gopkg.lock b/Gopkg.lock index a221505d40a..da20476a89c 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -272,7 +272,7 @@ revision = "aa113a015beb91c94a8e173c8f0ac18512a83954" [[projects]] - digest = "1:7777b78ab28a8f10918bdcdb043f4150d27da3181fafd69a1b15935197bf9d4c" + digest = "1:760be297dcd17acd32d2b4a7b9494be96e66451bfce53fc6fbaccad4ab2a5fa8" name = "github.com/knative/pkg" packages = [ "apis", @@ -283,6 +283,7 @@ "apis/istio/authentication/v1alpha1", "apis/istio/common/v1alpha1", "apis/istio/v1alpha3", + "changeset", "client/clientset/versioned", "client/clientset/versioned/scheme", "client/clientset/versioned/typed/authentication/v1alpha1", @@ -307,7 +308,7 @@ "webhook", ] pruneopts = "NUT" - revision = "a8160c7d728d26da67e596c5b1975877082a26e6" + revision = "4b704fa7948ad9ae8ec90d1cd5b4a34516b252ea" [[projects]] digest = "1:63f3974f3afe3dc5b6a115c0d53b0897cd01be6880c4bf5d014fc69a95db6ed1" diff --git a/Gopkg.toml b/Gopkg.toml index 9ea4d08a427..a89b7028dae 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -72,8 +72,8 @@ required = [ [[override]] name = "github.com/knative/pkg" - # HEAD as of 2018-11-02 - revision = "a8160c7d728d26da67e596c5b1975877082a26e6" + # HEAD as of 2018-11-08 + revision = "4b704fa7948ad9ae8ec90d1cd5b4a34516b252ea" [[constraint]] name = "github.com/knative/serving" diff --git a/pkg/apis/eventing/v1alpha1/channel_types_test.go b/pkg/apis/eventing/v1alpha1/channel_types_test.go index d96bb62e38f..2cbabce3696 100644 --- a/pkg/apis/eventing/v1alpha1/channel_types_test.go +++ b/pkg/apis/eventing/v1alpha1/channel_types_test.go @@ -35,7 +35,9 @@ var condUnprovisioned = duckv1alpha1.Condition{ Status: corev1.ConditionFalse, } -var ignoreTransitionTimeMessageAndReason = cmpopts.IgnoreFields(duckv1alpha1.Condition{}, "LastTransitionTime", "Message", "Reason") +var ignoreAllButTypeAndStatus = cmpopts.IgnoreFields( + duckv1alpha1.Condition{}, + "LastTransitionTime", "Message", "Reason", "Severity") func TestChannelGetCondition(t *testing.T) { tests := []struct { @@ -149,8 +151,7 @@ func TestChannelInitializeConditions(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.cs.InitializeConditions() - ignore := cmpopts.IgnoreFields(duckv1alpha1.Condition{}, "LastTransitionTime") - if diff := cmp.Diff(test.want, test.cs, ignore); diff != "" { + if diff := cmp.Diff(test.want, test.cs, ignoreAllButTypeAndStatus); diff != "" { t.Errorf("unexpected conditions (-want, +got) = %v", diff) } }) @@ -233,7 +234,7 @@ func TestChannelStatus_SetAddressable(t *testing.T) { t.Run(n, func(t *testing.T) { cs := &ChannelStatus{} cs.SetAddress(tc.domainInternal) - if diff := cmp.Diff(tc.want, cs, ignoreTransitionTimeMessageAndReason); diff != "" { + if diff := cmp.Diff(tc.want, cs, ignoreAllButTypeAndStatus); diff != "" { t.Errorf("unexpected conditions (-want, +got) = %v", diff) } }) diff --git a/pkg/apis/eventing/v1alpha1/subscription_types_test.go b/pkg/apis/eventing/v1alpha1/subscription_types_test.go index e159457d21f..c2fb93a74dc 100644 --- a/pkg/apis/eventing/v1alpha1/subscription_types_test.go +++ b/pkg/apis/eventing/v1alpha1/subscription_types_test.go @@ -20,7 +20,6 @@ import ( "testing" "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" corev1 "k8s.io/api/core/v1" ) @@ -162,8 +161,7 @@ func TestSubscriptionInitializeConditions(t *testing.T) { for _, test := range tests { t.Run(test.name, func(t *testing.T) { test.ss.InitializeConditions() - ignore := cmpopts.IgnoreFields(duckv1alpha1.Condition{}, "LastTransitionTime") - if diff := cmp.Diff(test.want, test.ss, ignore); diff != "" { + if diff := cmp.Diff(test.want, test.ss, ignoreAllButTypeAndStatus); diff != "" { t.Errorf("unexpected conditions (-want, +got) = %v", diff) } }) diff --git a/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go index 49f0f709c8f..030e8dd2025 100644 --- a/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go +++ b/pkg/controller/eventing/inmemory/clusterchannelprovisioner/reconcile_test.go @@ -257,12 +257,11 @@ func makeClusterChannelProvisioner() *eventingv1alpha1.ClusterChannelProvisioner func makeReadyClusterChannelProvisioner() *eventingv1alpha1.ClusterChannelProvisioner { ccp := makeClusterChannelProvisioner() - ccp.Status.Conditions = []duckv1alpha1.Condition{ - { - Type: duckv1alpha1.ConditionReady, - Status: corev1.ConditionTrue, - }, - } + ccp.Status.Conditions = []duckv1alpha1.Condition{{ + Type: duckv1alpha1.ConditionReady, + Status: corev1.ConditionTrue, + Severity: duckv1alpha1.ConditionSeverityError, + }} return ccp } diff --git a/pkg/provisioners/kafka/controller/reconcile_test.go b/pkg/provisioners/kafka/controller/reconcile_test.go index e7ab3eea676..ebe66418b1d 100644 --- a/pkg/provisioners/kafka/controller/reconcile_test.go +++ b/pkg/provisioners/kafka/controller/reconcile_test.go @@ -45,8 +45,9 @@ func init() { } var ClusterChannelProvisionerConditionReady = duckv1alpha1.Condition{ - Type: eventingv1alpha1.ClusterChannelProvisionerConditionReady, - Status: corev1.ConditionTrue, + Type: eventingv1alpha1.ClusterChannelProvisionerConditionReady, + Status: corev1.ConditionTrue, + Severity: duckv1alpha1.ConditionSeverityError, } var mockFetchError = controllertesting.Mocks{ diff --git a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/condition_set.go b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/condition_set.go index 30b76a5f667..e330970bd0b 100644 --- a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/condition_set.go +++ b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/condition_set.go @@ -62,7 +62,7 @@ type ConditionManager interface { SetCondition(new Condition) // MarkTrue sets the status of t to true, and then marks the happy condition to - // true if all other dependents are also true. + // true if all dependents are true. MarkTrue(t ConditionType) // MarkUnknown sets the status of t to Unknown and also sets the happy condition @@ -82,12 +82,14 @@ type ConditionManager interface { // NewLivingConditionSet returns a ConditionSet to hold the conditions for the // living resource. ConditionReady is used as the happy condition. +// The set of condition types provided are those of the terminal subconditions. func NewLivingConditionSet(d ...ConditionType) ConditionSet { return newConditionSet(ConditionReady, d...) } // NewBatchConditionSet returns a ConditionSet to hold the conditions for the // batch resource. ConditionSucceeded is used as the happy condition. +// The set of condition types provided are those of the terminal subconditions. func NewBatchConditionSet(d ...ConditionType) ConditionSet { return newConditionSet(ConditionSucceeded, d...) } @@ -209,13 +211,30 @@ func (r conditionsImpl) SetCondition(new Condition) { r.accessor.SetConditions(conditions) } +func (r conditionsImpl) isTerminal(t ConditionType) bool { + for _, cond := range append(r.dependents, r.happy) { + if cond == t { + return true + } + } + return false +} + +func (r conditionsImpl) severity(t ConditionType) ConditionSeverity { + if r.isTerminal(t) { + return ConditionSeverityError + } + return ConditionSeverityInfo +} + // MarkTrue sets the status of t to true, and then marks the happy condition to // true if all other dependents are also true. func (r conditionsImpl) MarkTrue(t ConditionType) { // set the specified condition r.SetCondition(Condition{ - Type: t, - Status: corev1.ConditionTrue, + Type: t, + Status: corev1.ConditionTrue, + Severity: r.severity(t), }) // check the dependents. @@ -229,8 +248,9 @@ func (r conditionsImpl) MarkTrue(t ConditionType) { // set the happy condition r.SetCondition(Condition{ - Type: r.happy, - Status: corev1.ConditionTrue, + Type: r.happy, + Status: corev1.ConditionTrue, + Severity: r.severity(r.happy), }) } @@ -239,13 +259,15 @@ func (r conditionsImpl) MarkTrue(t ConditionType) { func (r conditionsImpl) MarkUnknown(t ConditionType, reason, messageFormat string, messageA ...interface{}) { // set the specified condition r.SetCondition(Condition{ - Type: t, - Status: corev1.ConditionUnknown, - Reason: reason, - Message: fmt.Sprintf(messageFormat, messageA...), + Type: t, + Status: corev1.ConditionUnknown, + Reason: reason, + Message: fmt.Sprintf(messageFormat, messageA...), + Severity: r.severity(t), }) // check the dependents. + isDependent := false for _, cond := range r.dependents { c := r.GetCondition(cond) // Failed conditions trump Unknown conditions @@ -257,28 +279,39 @@ func (r conditionsImpl) MarkUnknown(t ConditionType, reason, messageFormat strin } return } + if cond == t { + isDependent = true + } } - // set the happy condition - r.SetCondition(Condition{ - Type: r.happy, - Status: corev1.ConditionUnknown, - Reason: reason, - Message: fmt.Sprintf(messageFormat, messageA...), - }) + if isDependent { + // set the happy condition, if it is one of our dependent subconditions. + r.SetCondition(Condition{ + Type: r.happy, + Status: corev1.ConditionUnknown, + Reason: reason, + Message: fmt.Sprintf(messageFormat, messageA...), + Severity: r.severity(r.happy), + }) + } } // MarkFalse sets the status of t and the happy condition to False. func (r conditionsImpl) MarkFalse(t ConditionType, reason, messageFormat string, messageA ...interface{}) { - for _, t := range []ConditionType{ - t, - r.happy, - } { + types := []ConditionType{t} + for _, cond := range r.dependents { + if cond == t { + types = append(types, r.happy) + } + } + + for _, t := range types { r.SetCondition(Condition{ - Type: t, - Status: corev1.ConditionFalse, - Reason: reason, - Message: fmt.Sprintf(messageFormat, messageA...), + Type: t, + Status: corev1.ConditionFalse, + Reason: reason, + Message: fmt.Sprintf(messageFormat, messageA...), + Severity: r.severity(t), }) } } @@ -295,8 +328,9 @@ func (r conditionsImpl) InitializeConditions() { func (r conditionsImpl) InitializeCondition(t ConditionType) { if c := r.GetCondition(t); c == nil { r.SetCondition(Condition{ - Type: t, - Status: corev1.ConditionUnknown, + Type: t, + Status: corev1.ConditionUnknown, + Severity: r.severity(t), }) } } diff --git a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/conditions_types.go b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/conditions_types.go index 44b99921051..0dbad3397e0 100644 --- a/vendor/github.com/knative/pkg/apis/duck/v1alpha1/conditions_types.go +++ b/vendor/github.com/knative/pkg/apis/duck/v1alpha1/conditions_types.go @@ -42,6 +42,21 @@ const ( ConditionSucceeded ConditionType = "Succeeded" ) +// ConditionSeverity expresses the severity of a Condition Type failing. +type ConditionSeverity string + +const ( + // ConditionSeverityError specifies that a failure of a condition type + // should be viewed as an error. + ConditionSeverityError ConditionSeverity = "Error" + // ConditionSeverityWarning specifies that a failure of a condition type + // should be viewed as a warning, but that things could still work. + ConditionSeverityWarning ConditionSeverity = "Warning" + // ConditionSeverityInfo specifies that a failure of a condition type + // should be viewed as purely informational, and that things could still work. + ConditionSeverityInfo ConditionSeverity = "Info" +) + // Conditions defines a readiness condition for a Knative resource. // See: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#typical-status-properties // +k8s:deepcopy-gen=true @@ -54,6 +69,11 @@ type Condition struct { // +required Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` + // Severity with which to treat failures of this type of condition. + // When this is not specified, it defaults to Error. + // +optional + Severity ConditionSeverity `json:"severity,omitempty" description:"how to interpret failures of this condition, one of Error, Warning, Info"` + // LastTransitionTime is the last time the condition transitioned from one status to another. // We use VolatileTime in place of metav1.Time to exclude this from creating equality.Semantic // differences (all other things held constant). @@ -93,7 +113,6 @@ func (c *Condition) IsUnknown() bool { return c.Status == corev1.ConditionUnknown } - // Conditions is an Implementable "duck type". var _ duck.Implementable = (*Conditions)(nil) diff --git a/vendor/github.com/knative/pkg/changeset/commit.go b/vendor/github.com/knative/pkg/changeset/commit.go new file mode 100644 index 00000000000..77ed7093211 --- /dev/null +++ b/vendor/github.com/knative/pkg/changeset/commit.go @@ -0,0 +1,64 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package changeset + +import ( + "fmt" + "io/ioutil" + "os" + "path/filepath" + "regexp" + "strings" +) + +const ( + commitIDFile = "HEAD" + koDataPathEnvName = "KO_DATA_PATH" +) + +var ( + commitIDRE = regexp.MustCompile(`^[a-f0-9]{40}$`) +) + +// Get tries to fetch the first 7 digitals of GitHub commit ID from HEAD file in +// KO_DATA_PATH. If it fails, it returns the error it gets. +func Get() (string, error) { + data, err := readFileFromKoData(commitIDFile) + if err != nil { + return "", err + } + commitID := strings.TrimSpace(string(data)) + if !commitIDRE.MatchString(commitID) { + err := fmt.Errorf("%q is not a valid GitHub commit ID", commitID) + return "", err + } + return string(commitID[0:7]), nil +} + +// readFileFromKoData tries to read data as string from the file with given name +// under KO_DATA_PATH then returns the content as string. The file is expected +// to be wrapped into the container from /kodata by ko. If it fails, returns +// the error it gets. +func readFileFromKoData(filename string) ([]byte, error) { + koDataPath := os.Getenv(koDataPathEnvName) + if koDataPath == "" { + err := fmt.Errorf("%q does not exist or is empty", koDataPathEnvName) + return nil, err + } + fullFilename := filepath.Join(koDataPath, filename) + return ioutil.ReadFile(fullFilename) +} diff --git a/vendor/github.com/knative/pkg/changeset/doc.go b/vendor/github.com/knative/pkg/changeset/doc.go new file mode 100644 index 00000000000..c56f7ebfb4d --- /dev/null +++ b/vendor/github.com/knative/pkg/changeset/doc.go @@ -0,0 +1,23 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package changeset provides Knative utilities for fetching GitHub Commit ID +// from kodata directory. It requires GitHub HEAD file to be linked into +// Knative component source code via the following command: +// ln -s -r .git/HEAD ./cmd//kodata/ +// Then ko will build this file into $KO_DATA_PATH when building the container +// for a Knative component. +package changeset diff --git a/vendor/github.com/knative/pkg/logging/config.go b/vendor/github.com/knative/pkg/logging/config.go index 8c0dfe50d94..c03c193148b 100644 --- a/vendor/github.com/knative/pkg/logging/config.go +++ b/vendor/github.com/knative/pkg/logging/config.go @@ -25,6 +25,7 @@ import ( "go.uber.org/zap/zapcore" corev1 "k8s.io/api/core/v1" + "github.com/knative/pkg/changeset" "github.com/knative/pkg/logging/logkey" ) @@ -37,7 +38,7 @@ import ( func NewLogger(configJSON string, levelOverride string, opts ...zap.Option) (*zap.SugaredLogger, zap.AtomicLevel) { logger, atomicLevel, err := newLoggerFromConfig(configJSON, levelOverride, opts) if err == nil { - return logger.Sugar(), atomicLevel + return enrichLoggerWithCommitID(logger.Sugar()), atomicLevel } loggingCfg := zap.NewProductionConfig() @@ -51,7 +52,18 @@ func NewLogger(configJSON string, levelOverride string, opts ...zap.Option) (*za if err2 != nil { panic(err2) } - return logger.Named("fallback-logger").Sugar(), loggingCfg.Level + return enrichLoggerWithCommitID(logger.Named("fallback-logger").Sugar()), loggingCfg.Level +} + +func enrichLoggerWithCommitID(logger *zap.SugaredLogger) *zap.SugaredLogger { + commmitID, err := changeset.Get() + if err == nil { + // Enrich logs with GitHub commit ID. + return logger.With(zap.String(logkey.GitHubCommitID, commmitID)) + } + + logger.Warnf("Fetch GitHub commit ID from kodata failed: %v", err) + return logger } // NewLoggerFromConfig creates a logger using the provided Config diff --git a/vendor/github.com/knative/pkg/logging/logkey/constants.go b/vendor/github.com/knative/pkg/logging/logkey/constants.go index 809b1012865..e4c62ee0daf 100644 --- a/vendor/github.com/knative/pkg/logging/logkey/constants.go +++ b/vendor/github.com/knative/pkg/logging/logkey/constants.go @@ -55,4 +55,8 @@ const ( // KubernetesService is the key used to represent a Kubernetes service name in logs KubernetesService = "knative.dev/k8sservice" + + // GitHubCommitID is the key used to represent the GitHub Commit ID where the + // Knative component was built from in logs + GitHubCommitID = "commit" ) From 9f0de0432c71092d9ea3b007ef4d3e44408ed168 Mon Sep 17 00:00:00 2001 From: Nicola Ferraro Date: Fri, 9 Nov 2018 18:35:44 +0100 Subject: [PATCH 35/54] Fix in-memory channel role (#599) --- .../provisioners/in-memory-channel/in-memory-channel.yaml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/config/provisioners/in-memory-channel/in-memory-channel.yaml b/config/provisioners/in-memory-channel/in-memory-channel.yaml index f0a5de2c42f..941da4fd30e 100644 --- a/config/provisioners/in-memory-channel/in-memory-channel.yaml +++ b/config/provisioners/in-memory-channel/in-memory-channel.yaml @@ -43,6 +43,12 @@ rules: - list - watch - update + - apiGroups: + - eventing.knative.dev + resources: + - channels/finalizers + verbs: + - update - apiGroups: - "" # Core API group. resources: From 6618c253959e5f061ca8b0ba67d989a69ccfa0e7 Mon Sep 17 00:00:00 2001 From: Yanwei Guo Date: Fri, 9 Nov 2018 14:30:44 -0800 Subject: [PATCH 36/54] link .git/HEAD to kodata (#603) --- cmd/controller/kodata/HEAD | 1 + cmd/fanoutsidecar/kodata/HEAD | 1 + cmd/sendevent/kodata/HEAD | 1 + cmd/webhook/kodata/HEAD | 1 + 4 files changed, 4 insertions(+) create mode 120000 cmd/controller/kodata/HEAD create mode 120000 cmd/fanoutsidecar/kodata/HEAD create mode 120000 cmd/sendevent/kodata/HEAD create mode 120000 cmd/webhook/kodata/HEAD diff --git a/cmd/controller/kodata/HEAD b/cmd/controller/kodata/HEAD new file mode 120000 index 00000000000..8f63681d362 --- /dev/null +++ b/cmd/controller/kodata/HEAD @@ -0,0 +1 @@ +../../../.git/HEAD \ No newline at end of file diff --git a/cmd/fanoutsidecar/kodata/HEAD b/cmd/fanoutsidecar/kodata/HEAD new file mode 120000 index 00000000000..8f63681d362 --- /dev/null +++ b/cmd/fanoutsidecar/kodata/HEAD @@ -0,0 +1 @@ +../../../.git/HEAD \ No newline at end of file diff --git a/cmd/sendevent/kodata/HEAD b/cmd/sendevent/kodata/HEAD new file mode 120000 index 00000000000..8f63681d362 --- /dev/null +++ b/cmd/sendevent/kodata/HEAD @@ -0,0 +1 @@ +../../../.git/HEAD \ No newline at end of file diff --git a/cmd/webhook/kodata/HEAD b/cmd/webhook/kodata/HEAD new file mode 120000 index 00000000000..8f63681d362 --- /dev/null +++ b/cmd/webhook/kodata/HEAD @@ -0,0 +1 @@ +../../../.git/HEAD \ No newline at end of file From 1182e06b1c86b0619d98381a1173e8ec1a4777ba Mon Sep 17 00:00:00 2001 From: Matthias Wessendorf Date: Sat, 10 Nov 2018 01:03:44 +0100 Subject: [PATCH 37/54] use listed notation (#601) --- config/provisioners/kafka/kafka-provisioner.yaml | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/config/provisioners/kafka/kafka-provisioner.yaml b/config/provisioners/kafka/kafka-provisioner.yaml index 746e39ab341..527a9c7fcb7 100644 --- a/config/provisioners/kafka/kafka-provisioner.yaml +++ b/config/provisioners/kafka/kafka-provisioner.yaml @@ -31,9 +31,16 @@ apiVersion: rbac.authorization.k8s.io/v1 metadata: name: kafka-channel-controller rules: -- apiGroups: ["eventing.knative.dev"] - resources: ["clusterchannelprovisioners", "channels"] - verbs: ["get", "watch", "list", "update"] + - apiGroups: + - eventing.knative.dev + resources: + - channels + - clusterchannelprovisioners + verbs: + - get + - list + - watch + - update --- apiVersion: rbac.authorization.k8s.io/v1beta1 From 8fcf54f7147d51b0825fc1d14befd88e1355fec5 Mon Sep 17 00:00:00 2001 From: Galo Navarro Date: Sat, 10 Nov 2018 01:40:44 +0100 Subject: [PATCH 38/54] Remove obsoleted code for subscribable interface (#602) * Remove duplicate code Signed-off-by: Galo Navarro * fixup! Remove duplicate code --- .gitignore | 1 + .../subscribable_channelable_validation.go | 25 ------------------- ...ubscribable_channelable_validation_test.go | 10 -------- 3 files changed, 1 insertion(+), 35 deletions(-) diff --git a/.gitignore b/.gitignore index 31e2a94a206..5366a3c8d79 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ bazel-* .idea/ .vscode/ .DS_Store +*.swp diff --git a/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go b/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go index 804f8bdc885..b415b68a183 100644 --- a/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go +++ b/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation.go @@ -50,31 +50,6 @@ func isValidChannel(f corev1.ObjectReference) *apis.FieldError { return errs } -func isSubscribableEmpty(f corev1.ObjectReference) bool { - return equality.Semantic.DeepEqual(f, corev1.ObjectReference{}) -} - -// Valid from only contains the following fields: -// - Kind == 'Channel' -// - APIVersion == 'eventing.knative.dev/v1alpha1' -// - Name == not empty -func isValidSubscribable(f corev1.ObjectReference) *apis.FieldError { - errs := isValidObjectReference(f) - - if f.Kind != "Channel" { - fe := apis.ErrInvalidValue(f.Kind, "kind") - fe.Paths = []string{"kind"} - fe.Details = "only 'Channel' kind is allowed" - errs = errs.Also(fe) - } - if f.APIVersion != "eventing.knative.dev/v1alpha1" { - fe := apis.ErrInvalidValue(f.APIVersion, "apiVersion") - fe.Details = "only eventing.knative.dev/v1alpha1 is allowed for apiVersion" - errs = errs.Also(fe) - } - return errs -} - func isValidObjectReference(f corev1.ObjectReference) *apis.FieldError { return checkRequiredObjectReferenceFields(f). Also(checkDisallowedObjectReferenceFields(f)) diff --git a/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation_test.go b/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation_test.go index bf76f614cc7..5586d3b23c7 100644 --- a/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation_test.go +++ b/pkg/apis/eventing/v1alpha1/subscribable_channelable_validation_test.go @@ -98,16 +98,6 @@ func TestIsValidChannel(t *testing.T) { }) } } -func TestIsValidSubscribable(t *testing.T) { - for _, test := range validationTests { - t.Run(test.name, func(t *testing.T) { - got := isValidSubscribable(test.ref) - if diff := cmp.Diff(test.want.Error(), got.Error()); diff != "" { - t.Errorf("%s: validation (-want, +got) = %v", test.name, diff) - } - }) - } -} func TestIsValidObjectReference(t *testing.T) { tests := []struct { From 0ad6cac324ad4a564adc1c98b259f4c0b5d63b9b Mon Sep 17 00:00:00 2001 From: Sabari Kumar Murugesan Date: Mon, 12 Nov 2018 10:17:45 -0800 Subject: [PATCH 39/54] Control plane services for Kafka Channel Provisioner (#573) Co-authored-by: Matthias Wessendorf --- .../provisioners/kafka/kafka-provisioner.yaml | 18 ++++ .../kafka/controller/channel/provider.go | 20 ++++- .../kafka/controller/channel/reconcile.go | 83 +++++++++---------- .../controller/channel/reconcile_test.go | 54 ++++++++++++ pkg/provisioners/kafka/controller/provider.go | 12 ++- .../kafka/controller/reconcile.go | 23 ++++- pkg/provisioners/kafka/main.go | 15 ++-- 7 files changed, 169 insertions(+), 56 deletions(-) diff --git a/config/provisioners/kafka/kafka-provisioner.yaml b/config/provisioners/kafka/kafka-provisioner.yaml index 527a9c7fcb7..78039fc55fb 100644 --- a/config/provisioners/kafka/kafka-provisioner.yaml +++ b/config/provisioners/kafka/kafka-provisioner.yaml @@ -41,6 +41,24 @@ rules: - list - watch - update + - apiGroups: + - "" # Core API group. + resources: + - services + verbs: + - get + - list + - watch + - create + - apiGroups: + - networking.istio.io + resources: + - virtualservices + verbs: + - get + - list + - watch + - create --- apiVersion: rbac.authorization.k8s.io/v1beta1 diff --git a/pkg/provisioners/kafka/controller/channel/provider.go b/pkg/provisioners/kafka/controller/channel/provider.go index b386576ee9f..be24497f1cb 100644 --- a/pkg/provisioners/kafka/controller/channel/provider.go +++ b/pkg/provisioners/kafka/controller/channel/provider.go @@ -18,7 +18,9 @@ package channel import ( "github.com/Shopify/sarama" + istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -27,7 +29,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" - "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" common "github.com/knative/eventing/pkg/provisioners/kafka/controller" ) @@ -65,7 +67,21 @@ func ProvideController(mgr manager.Manager, config *common.KafkaProvisionerConfi } // Watch Channel events and enqueue Channel object key. - if err := c.Watch(&source.Kind{Type: &v1alpha1.Channel{}}, &handler.EnqueueRequestForObject{}); err != nil { + if err := c.Watch(&source.Kind{Type: &eventingv1alpha1.Channel{}}, &handler.EnqueueRequestForObject{}); err != nil { + return nil, err + } + + // Watch the K8s Services that are owned by Channels. + err = c.Watch(&source.Kind{Type: &corev1.Service{}}, &handler.EnqueueRequestForOwner{OwnerType: &eventingv1alpha1.Channel{}, IsController: true}) + if err != nil { + logger.Error("unable to watch K8s Services.", zap.Error(err)) + return nil, err + } + + // Watch the VirtualServices that are owned by Channels. + err = c.Watch(&source.Kind{Type: &istiov1alpha3.VirtualService{}}, &handler.EnqueueRequestForOwner{OwnerType: &eventingv1alpha1.Channel{}, IsController: true}) + if err != nil { + logger.Error("unable to watch VirtualServices.", zap.Error(err)) return nil, err } diff --git a/pkg/provisioners/kafka/controller/channel/reconcile.go b/pkg/provisioners/kafka/controller/channel/reconcile.go index dc5248dfcdf..2dd03cf53c5 100644 --- a/pkg/provisioners/kafka/controller/channel/reconcile.go +++ b/pkg/provisioners/kafka/controller/channel/reconcile.go @@ -22,15 +22,16 @@ import ( "fmt" "github.com/Shopify/sarama" - "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "go.uber.org/zap" - "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" - "k8s.io/apimachinery/pkg/util/sets" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/reconcile" + "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + eventingController "github.com/knative/eventing/pkg/controller" + util "github.com/knative/eventing/pkg/provisioners" "github.com/knative/eventing/pkg/provisioners/kafka/controller" ) @@ -41,7 +42,7 @@ const ( ) type channelArgs struct { - NumPartitions int32 `json:"NumPartitions,omitempty"` + NumPartitions int32 } // Reconcile compares the actual state with the desired, and attempts to @@ -88,13 +89,13 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err if clusterChannelProvisioner.Status.IsReady() { // Reconcile this copy of the Channel and then write back any status // updates regardless of whether the reconcile error out. - err = r.reconcile(newChannel) + err = r.reconcile(ctx, newChannel) } else { newChannel.Status.MarkNotProvisioned("NotProvisioned", "ClusterChannelProvisioner %s is not ready", clusterChannelProvisioner.Name) err = fmt.Errorf("ClusterChannelProvisioner %s is not ready", clusterChannelProvisioner.Name) } - if updateChannelErr := r.updateChannel(ctx, newChannel); updateChannelErr != nil { + if updateChannelErr := util.UpdateChannel(ctx, r.client, newChannel); updateChannelErr != nil { r.logger.Info("failed to update channel status", zap.Error(updateChannelErr)) return reconcile.Result{}, updateChannelErr } @@ -103,7 +104,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err return reconcile.Result{}, err } -func (r *reconciler) reconcile(channel *v1alpha1.Channel) error { +func (r *reconciler) reconcile(ctx context.Context, channel *v1alpha1.Channel) error { // We don't currently initialize r.kafkaClusterAdmin, hence we end up creating the cluster admin client every time. // This is because of an issue with Shopify/sarama. See https://github.com/Shopify/sarama/issues/1162. @@ -131,16 +132,44 @@ func (r *reconciler) reconcile(channel *v1alpha1.Channel) error { if err := r.deprovisionChannel(channel, kafkaClusterAdmin); err != nil { return err } - r.removeFinalizer(channel) + util.RemoveFinalizer(channel, finalizerName) return nil } - r.addFinalizer(channel) + util.AddFinalizer(channel, finalizerName) if err := r.provisionChannel(channel, kafkaClusterAdmin); err != nil { channel.Status.MarkNotProvisioned("NotProvisioned", "error while provisioning: %s", err) return err } + + svc, err := util.CreateK8sService(ctx, r.client, channel) + + if err != nil { + r.logger.Info("error creating the Channel's K8s Service", zap.Error(err)) + return err + } + + // Check if this Channel is the owner of the K8s service. + if !metav1.IsControlledBy(svc, channel) { + r.logger.Warn("Channel's K8s Service is not owned by the Channel", zap.Any("channel", channel), zap.Any("service", svc)) + } + + channel.Status.SetAddress(eventingController.ServiceHostName(svc.Name, svc.Namespace)) + + virtualService, err := util.CreateVirtualService(ctx, r.client, channel) + + if err != nil { + r.logger.Info("error creating the Virtual Service for the Channel", zap.Error(err)) + return err + } + + // If the Virtual Service is not controlled by this Channel, we should log a warning, but don't + // consider it an error. + if !metav1.IsControlledBy(virtualService, channel) { + r.logger.Warn("VirtualService not owned by Channel", zap.Any("channel", channel), zap.Any("virtualService", virtualService)) + } + channel.Status.MarkProvisioned() // close the connection @@ -207,42 +236,6 @@ func (r *reconciler) getClusterChannelProvisioner() (*v1alpha1.ClusterChannelPro return clusterChannelProvisioner, nil } -func (r *reconciler) updateChannel(ctx context.Context, u *v1alpha1.Channel) error { - channel := &v1alpha1.Channel{} - err := r.client.Get(ctx, client.ObjectKey{Namespace: u.Namespace, Name: u.Name}, channel) - if err != nil { - return err - } - - updated := false - if !equality.Semantic.DeepEqual(channel.Finalizers, u.Finalizers) { - channel.SetFinalizers(u.ObjectMeta.Finalizers) - updated = true - } - - if !equality.Semantic.DeepEqual(channel.Status, u.Status) { - channel.Status = u.Status - updated = true - } - - if updated == false { - return nil - } - return r.client.Update(ctx, channel) -} - -func (r *reconciler) addFinalizer(channel *v1alpha1.Channel) { - finalizers := sets.NewString(channel.Finalizers...) - finalizers.Insert(finalizerName) - channel.Finalizers = finalizers.List() -} - -func (r *reconciler) removeFinalizer(channel *v1alpha1.Channel) { - finalizers := sets.NewString(channel.Finalizers...) - finalizers.Delete(finalizerName) - channel.Finalizers = finalizers.List() -} - func createKafkaAdminClient(config *controller.KafkaProvisionerConfig) (sarama.ClusterAdmin, error) { saramaConf := sarama.NewConfig() saramaConf.Version = sarama.V1_1_0_0 diff --git a/pkg/provisioners/kafka/controller/channel/reconcile_test.go b/pkg/provisioners/kafka/controller/channel/reconcile_test.go index 6becf1e1cbe..fd2422f87ab 100644 --- a/pkg/provisioners/kafka/controller/channel/reconcile_test.go +++ b/pkg/provisioners/kafka/controller/channel/reconcile_test.go @@ -25,6 +25,7 @@ import ( "github.com/Shopify/sarama" "github.com/google/go-cmp/cmp" duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -36,6 +37,7 @@ import ( eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" controllertesting "github.com/knative/eventing/pkg/controller/testing" "github.com/knative/eventing/pkg/provisioners" + util "github.com/knative/eventing/pkg/provisioners" "github.com/knative/eventing/pkg/provisioners/kafka/controller" ) @@ -43,16 +45,20 @@ const ( channelName = "test-channel" clusterChannelProvisionerName = "kafka-channel" testNS = "test-namespace" + testUID = "test-uid" argumentNumPartitions = "NumPartitions" ) var ( + truePointer = true + deletedTs = metav1.Now().Rfc3339Copy() ) func init() { // Add types to scheme eventingv1alpha1.AddToScheme(scheme.Scheme) + istiov1alpha3.AddToScheme(scheme.Scheme) } var mockFetchError = controllertesting.Mocks{ @@ -124,6 +130,7 @@ var testCases = []controllertesting.TestCase{ InitialState: []runtime.Object{ getNewClusterChannelProvisioner(clusterChannelProvisionerName, true), getNewChannel(channelName, clusterChannelProvisionerName), + makeVirtualService(), }, ReconcileKey: fmt.Sprintf("%s/%s", testNS, channelName), WantResult: reconcile.Result{}, @@ -425,6 +432,7 @@ func getNewChannelWithArgs(name string, args map[string]interface{}) *eventingv1 func getNewChannelProvisionedStatus(name, provisioner string) *eventingv1alpha1.Channel { c := getNewChannel(name, provisioner) c.Status.InitializeConditions() + c.Status.SetAddress(fmt.Sprintf("%s-channel.%s.svc.cluster.local", c.Name, c.Namespace)) c.Status.MarkProvisioned() c.Finalizers = []string{finalizerName} return c @@ -478,6 +486,52 @@ func getNewClusterChannelProvisioner(name string, isReady bool) *eventingv1alpha return clusterChannelProvisioner } +func makeVirtualService() *istiov1alpha3.VirtualService { + return &istiov1alpha3.VirtualService{ + TypeMeta: metav1.TypeMeta{ + APIVersion: istiov1alpha3.SchemeGroupVersion.String(), + Kind: "VirtualService", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: fmt.Sprintf("%s-channel", testNS), + Namespace: testNS, + Labels: map[string]string{ + "channel": channelName, + "provisioner": clusterChannelProvisionerName, + }, + OwnerReferences: []metav1.OwnerReference{ + { + APIVersion: eventingv1alpha1.SchemeGroupVersion.String(), + Kind: "Channel", + Name: channelName, + UID: testUID, + Controller: &truePointer, + BlockOwnerDeletion: &truePointer, + }, + }, + }, + Spec: istiov1alpha3.VirtualServiceSpec{ + Hosts: []string{ + fmt.Sprintf("%s-channel.%s.svc.cluster.local", channelName, testNS), + fmt.Sprintf("%s.%s.channels.cluster.local", channelName, testNS), + }, + Http: []istiov1alpha3.HTTPRoute{{ + Rewrite: &istiov1alpha3.HTTPRewrite{ + Authority: fmt.Sprintf("%s.%s.channels.cluster.local", channelName, testNS), + }, + Route: []istiov1alpha3.DestinationWeight{{ + Destination: istiov1alpha3.Destination{ + Host: "kafka-provisioner.knative-eventing.svc.cluster.local", + Port: istiov1alpha3.PortSelector{ + Number: util.PortNumber, + }, + }}, + }}, + }, + }, + } +} + func om(namespace, name string) metav1.ObjectMeta { return metav1.ObjectMeta{ Namespace: namespace, diff --git a/pkg/provisioners/kafka/controller/provider.go b/pkg/provisioners/kafka/controller/provider.go index 59c0ec94697..24885ee585e 100644 --- a/pkg/provisioners/kafka/controller/provider.go +++ b/pkg/provisioners/kafka/controller/provider.go @@ -17,8 +17,8 @@ limitations under the License. package controller import ( - "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" "go.uber.org/zap" + corev1 "k8s.io/api/core/v1" "k8s.io/client-go/tools/record" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller" @@ -26,6 +26,9 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" "sigs.k8s.io/controller-runtime/pkg/reconcile" "sigs.k8s.io/controller-runtime/pkg/source" + + "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + eventingv1alpha1 "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" ) const ( @@ -63,6 +66,13 @@ func ProvideController(mgr manager.Manager, config *KafkaProvisionerConfig, logg return nil, err } + // Watch the K8s Services that are owned by ClusterChannelProvisioners. + err = c.Watch(&source.Kind{Type: &corev1.Service{}}, &handler.EnqueueRequestForOwner{OwnerType: &eventingv1alpha1.ClusterChannelProvisioner{}, IsController: true}) + if err != nil { + logger.Error("unable to watch K8s Services.", zap.Error(err)) + return nil, err + } + return c, nil } diff --git a/pkg/provisioners/kafka/controller/reconcile.go b/pkg/provisioners/kafka/controller/reconcile.go index dd5d84b47a0..ff7be5c9fda 100644 --- a/pkg/provisioners/kafka/controller/reconcile.go +++ b/pkg/provisioners/kafka/controller/reconcile.go @@ -23,6 +23,8 @@ import ( "go.uber.org/zap" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/controller-runtime/pkg/reconcile" "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" @@ -63,7 +65,12 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err // Reconcile this copy of the Provisioner and then write back any status // updates regardless of whether the reconcile error out. - err = r.reconcile(newProvisioner) + err = r.reconcile(ctx, newProvisioner) + if err != nil { + r.logger.Info("error reconciling ClusterProvisioner", zap.Error(err)) + // Note that we do not return the error here, because we want to update the Status + // regardless of the error. + } if updateStatusErr := util.UpdateClusterChannelProvisionerStatus(ctx, r.client, newProvisioner); updateStatusErr != nil { r.logger.Info("error updating ClusterChannelProvisioner Status", zap.Error(updateStatusErr)) return reconcile.Result{}, updateStatusErr @@ -73,7 +80,7 @@ func (r *reconciler) Reconcile(request reconcile.Request) (reconcile.Result, err return reconcile.Result{}, err } -func (r *reconciler) reconcile(provisioner *v1alpha1.ClusterChannelProvisioner) error { +func (r *reconciler) reconcile(ctx context.Context, provisioner *v1alpha1.ClusterChannelProvisioner) error { // See if the provisioner has been deleted accessor, err := meta.Accessor(provisioner) if err != nil { @@ -87,6 +94,18 @@ func (r *reconciler) reconcile(provisioner *v1alpha1.ClusterChannelProvisioner) } provisioner.Status.InitializeConditions() + + svc, err := util.CreateDispatcherService(ctx, r.client, provisioner) + if err != nil { + r.logger.Info("error creating the ClusterProvisioner's K8s Service", zap.Error(err)) + return err + } + + // Check if this ClusterChannelProvisioner is the owner of the K8s service. + if !metav1.IsControlledBy(svc, provisioner) { + r.logger.Warn("ClusterChannelProvisioner's K8s Service is not owned by the ClusterChannelProvisioner", zap.Any("clusterChannelProvisioner", provisioner), zap.Any("service", svc)) + } + // Update Status as Ready provisioner.Status.MarkReady() diff --git a/pkg/provisioners/kafka/main.go b/pkg/provisioners/kafka/main.go index 65b97003b68..eb779abf630 100644 --- a/pkg/provisioners/kafka/main.go +++ b/pkg/provisioners/kafka/main.go @@ -6,11 +6,7 @@ import ( "os" "strings" - "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" - "github.com/knative/eventing/pkg/provisioners" - provisionerController "github.com/knative/eventing/pkg/provisioners/kafka/controller" - "github.com/knative/eventing/pkg/provisioners/kafka/controller/channel" - "github.com/knative/pkg/configmap" + istiov1alpha3 "github.com/knative/pkg/apis/istio/v1alpha3" "go.uber.org/zap" "k8s.io/apimachinery/pkg/runtime" _ "k8s.io/client-go/plugin/pkg/client/auth/gcp" @@ -19,6 +15,12 @@ import ( "sigs.k8s.io/controller-runtime/pkg/manager" logf "sigs.k8s.io/controller-runtime/pkg/runtime/log" "sigs.k8s.io/controller-runtime/pkg/runtime/signals" + + eventingv1alpha "github.com/knative/eventing/pkg/apis/eventing/v1alpha1" + "github.com/knative/eventing/pkg/provisioners" + provisionerController "github.com/knative/eventing/pkg/provisioners/kafka/controller" + "github.com/knative/eventing/pkg/provisioners/kafka/controller/channel" + "github.com/knative/pkg/configmap" ) const ( @@ -47,7 +49,8 @@ func main() { // Add custom types to this array to get them into the manager's scheme. schemeFuncs := []SchemeFunc{ - v1alpha1.AddToScheme, + eventingv1alpha.AddToScheme, + istiov1alpha3.AddToScheme, } for _, schemeFunc := range schemeFuncs { schemeFunc(mgr.GetScheme()) From 746656b57839c3456b16d8515a11c3cbb506e82c Mon Sep 17 00:00:00 2001 From: Matthias Wessendorf Date: Mon, 12 Nov 2018 19:53:45 +0100 Subject: [PATCH 40/54] Bumping serving (#604) --- Gopkg.lock | 23 +- Gopkg.toml | 2 +- vendor/github.com/knative/build/AUTHORS | 1 + .../build/cmd/controller/kodata/LICENSE | 1 + .../cmd/controller/kodata/VENDOR-LICENSE | 1 + .../build/cmd/creds-init/kodata/LICENSE | 1 + .../cmd/creds-init/kodata/VENDOR-LICENSE | 1 + .../knative/build/cmd/git-init/kodata/LICENSE | 1 + .../build/cmd/git-init/kodata/VENDOR-LICENSE | 1 + .../knative/build/cmd/logs/kodata/LICENSE | 1 + .../build/cmd/logs/kodata/VENDOR-LICENSE | 1 + .../knative/build/cmd/nop/kodata/LICENSE | 1 + .../build/cmd/nop/kodata/VENDOR-LICENSE | 1 + .../knative/build/cmd/webhook/kodata/LICENSE | 1 + .../build/cmd/webhook/kodata/VENDOR-LICENSE | 1 + .../knative/build/config/300-imagecache.yaml | 1 + .../knative/build/pkg/apis/build/register.go | 1 + .../pkg/apis/build/v1alpha1/build_defaults.go | 42 + .../v1alpha1/build_template_interface.go} | 11 +- .../build/v1alpha1/build_template_types.go | 41 +- .../v1alpha1/build_template_validation.go | 86 ++ .../pkg/apis/build/v1alpha1/build_types.go | 192 +++-- .../apis/build/v1alpha1/build_validation.go | 94 +++ .../v1alpha1/cluster_build_template_types.go | 74 ++ .../cluster_build_template_validation.go} | 13 +- .../build/v1alpha1/metadata_validation.go | 47 ++ .../build/pkg/apis/build/v1alpha1/register.go | 8 +- .../build/v1alpha1/zz_generated.deepcopy.go | 114 ++- .../knative/build/test/panic/kodata/LICENSE | 1 + .../build/test/panic/kodata/VENDOR-LICENSE | 1 + .../build/test/workingdir/kodata/LICENSE | 1 + .../test/workingdir/kodata/VENDOR-LICENSE | 1 + .../github.com/knative/pkg/kmeta/accessor.go | 94 +++ vendor/github.com/knative/pkg/kmeta/doc.go | 19 + vendor/github.com/knative/pkg/kmeta/labels.go | 114 +++ .../knative/pkg/kmeta/owner_references.go | 38 + vendor/github.com/knative/serving/AUTHORS | 3 +- .../serving/cmd/activator/kodata/LICENSE | 1 + .../cmd/activator/kodata/VENDOR-LICENSE | 1 + .../serving/cmd/autoscaler/kodata/LICENSE | 1 + .../cmd/autoscaler/kodata/VENDOR-LICENSE | 1 + .../serving/cmd/controller/kodata/LICENSE | 1 + .../cmd/controller/kodata/VENDOR-LICENSE | 1 + .../knative/serving/cmd/queue/kodata/LICENSE | 1 + .../serving/cmd/queue/kodata/VENDOR-LICENSE | 1 + .../serving/cmd/webhook/kodata/LICENSE | 1 + .../serving/cmd/webhook/kodata/VENDOR-LICENSE | 1 + .../serving/config/300-imagecache.yaml | 1 + .../serving/pkg/apis/autoscaling/register.go | 36 + .../autoscaling/v1alpha1/doc.go} | 7 +- .../apis/autoscaling/v1alpha1/kpa_defaults.go | 33 + .../apis/autoscaling/v1alpha1/kpa_types.go | 191 +++++ .../autoscaling/v1alpha1/kpa_validation.go | 79 ++ .../v1alpha1}/register.go | 15 +- .../v1alpha1/zz_generated.deepcopy.go | 129 +++ .../pkg/apis/istio/v1alpha3/gateway_types.go | 318 ------- .../istio/v1alpha3/virtualservice_types.go | 783 ------------------ .../istio/v1alpha3/zz_generated.deepcopy.go | 701 ---------------- .../serving/pkg/apis/networking/register.go | 40 + .../v1alpha1/clusteringress_defaults.go | 85 ++ .../v1alpha1/clusteringress_types.go | 346 ++++++++ .../v1alpha1/clusteringress_validation.go | 168 ++++ .../pkg/apis/networking/v1alpha1/doc.go | 24 + .../pkg/apis/networking/v1alpha1/register.go | 53 ++ .../v1alpha1/zz_generated.deepcopy.go | 368 ++++++++ .../serving/pkg/apis/serving/register.go | 10 + .../pkg/apis/serving/v1alpha1/build_compat.go | 101 +++ .../v1alpha1/configuration_defaults.go | 3 +- .../serving/v1alpha1/configuration_types.go | 191 ++--- .../v1alpha1/configuration_validation.go | 32 +- .../pkg/apis/serving/v1alpha1/field_error.go | 106 --- .../serving/v1alpha1/metadata_validation.go | 90 ++ .../serving/v1alpha1/revision_defaults.go | 15 +- .../apis/serving/v1alpha1/revision_types.go | 428 +++++----- .../serving/v1alpha1/revision_validation.go | 133 ++- .../apis/serving/v1alpha1/route_defaults.go | 3 +- .../pkg/apis/serving/v1alpha1/route_types.go | 244 ++---- .../apis/serving/v1alpha1/route_validation.go | 55 +- .../apis/serving/v1alpha1/service_defaults.go | 5 +- .../apis/serving/v1alpha1/service_types.go | 257 +++--- .../serving/v1alpha1/service_validation.go | 105 ++- .../serving/v1alpha1/zz_generated.deepcopy.go | 198 +++-- .../client/clientset/versioned/clientset.go | 50 +- .../clientset/versioned/scheme/register.go | 6 +- .../v1alpha1/autoscaling_client.go | 87 ++ .../v1alpha3 => autoscaling/v1alpha1}/doc.go | 2 +- .../v1alpha1/generated_expansion.go} | 7 +- .../autoscaling/v1alpha1/podautoscaler.go | 171 ++++ .../versioned/typed/istio/v1alpha3/gateway.go | 154 ---- .../typed/istio/v1alpha3/virtualservice.go | 154 ---- .../networking/v1alpha1/clusteringress.go | 160 ++++ .../typed/networking/v1alpha1/doc.go | 17 + .../v1alpha1/generated_expansion.go} | 6 +- .../v1alpha1/networking_client.go} | 41 +- .../config/testdata/config-controller.yaml | 1 - .../config/testdata/config-network.yaml | 1 - .../config/testdata/config-observability.yaml | 1 - .../route/config/testdata/config-domain.yaml | 1 - .../serving/pkg/gc/testdata/config-gc.yaml | 1 + .../config/testdata/config-gc.yaml | 1 + .../config/testdata/config-autoscaler.yaml | 1 + .../config/testdata/config-controller.yaml | 1 + .../config/testdata/config-logging.yaml | 1 + .../config/testdata/config-network.yaml | 1 + .../config/testdata/config-observability.yaml | 1 + .../route/config/testdata/config-domain.yaml | 1 + .../route/config/testdata/config-gc.yaml | 1 + .../elasticsearch}/LICENSE | 0 .../prometheus}/istio/LICENSE | 0 .../prometheus/kubernetes}/LICENSE | 0 .../prometheus}/prometheus-operator/LICENSE | 0 .../prometheus}/prometheus-operator/NOTICE | 0 112 files changed, 4039 insertions(+), 3226 deletions(-) create mode 120000 vendor/github.com/knative/build/cmd/controller/kodata/LICENSE create mode 120000 vendor/github.com/knative/build/cmd/controller/kodata/VENDOR-LICENSE create mode 120000 vendor/github.com/knative/build/cmd/creds-init/kodata/LICENSE create mode 120000 vendor/github.com/knative/build/cmd/creds-init/kodata/VENDOR-LICENSE create mode 120000 vendor/github.com/knative/build/cmd/git-init/kodata/LICENSE create mode 120000 vendor/github.com/knative/build/cmd/git-init/kodata/VENDOR-LICENSE create mode 120000 vendor/github.com/knative/build/cmd/logs/kodata/LICENSE create mode 120000 vendor/github.com/knative/build/cmd/logs/kodata/VENDOR-LICENSE create mode 120000 vendor/github.com/knative/build/cmd/nop/kodata/LICENSE create mode 120000 vendor/github.com/knative/build/cmd/nop/kodata/VENDOR-LICENSE create mode 120000 vendor/github.com/knative/build/cmd/webhook/kodata/LICENSE create mode 120000 vendor/github.com/knative/build/cmd/webhook/kodata/VENDOR-LICENSE create mode 120000 vendor/github.com/knative/build/config/300-imagecache.yaml create mode 100644 vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_defaults.go rename vendor/github.com/knative/{serving/pkg/apis/serving/v1alpha1/defaults.go => build/pkg/apis/build/v1alpha1/build_template_interface.go} (71%) create mode 100644 vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_template_validation.go create mode 100644 vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_validation.go create mode 100644 vendor/github.com/knative/build/pkg/apis/build/v1alpha1/cluster_build_template_types.go rename vendor/github.com/knative/{serving/pkg/apis/istio/v1alpha3/doc.go => build/pkg/apis/build/v1alpha1/cluster_build_template_validation.go} (68%) create mode 100644 vendor/github.com/knative/build/pkg/apis/build/v1alpha1/metadata_validation.go create mode 120000 vendor/github.com/knative/build/test/panic/kodata/LICENSE create mode 120000 vendor/github.com/knative/build/test/panic/kodata/VENDOR-LICENSE create mode 120000 vendor/github.com/knative/build/test/workingdir/kodata/LICENSE create mode 120000 vendor/github.com/knative/build/test/workingdir/kodata/VENDOR-LICENSE create mode 100644 vendor/github.com/knative/pkg/kmeta/accessor.go create mode 100644 vendor/github.com/knative/pkg/kmeta/doc.go create mode 100644 vendor/github.com/knative/pkg/kmeta/labels.go create mode 100644 vendor/github.com/knative/pkg/kmeta/owner_references.go create mode 120000 vendor/github.com/knative/serving/cmd/activator/kodata/LICENSE create mode 120000 vendor/github.com/knative/serving/cmd/activator/kodata/VENDOR-LICENSE create mode 120000 vendor/github.com/knative/serving/cmd/autoscaler/kodata/LICENSE create mode 120000 vendor/github.com/knative/serving/cmd/autoscaler/kodata/VENDOR-LICENSE create mode 120000 vendor/github.com/knative/serving/cmd/controller/kodata/LICENSE create mode 120000 vendor/github.com/knative/serving/cmd/controller/kodata/VENDOR-LICENSE create mode 120000 vendor/github.com/knative/serving/cmd/queue/kodata/LICENSE create mode 120000 vendor/github.com/knative/serving/cmd/queue/kodata/VENDOR-LICENSE create mode 120000 vendor/github.com/knative/serving/cmd/webhook/kodata/LICENSE create mode 120000 vendor/github.com/knative/serving/cmd/webhook/kodata/VENDOR-LICENSE create mode 120000 vendor/github.com/knative/serving/config/300-imagecache.yaml create mode 100644 vendor/github.com/knative/serving/pkg/apis/autoscaling/register.go rename vendor/github.com/knative/serving/pkg/{client/clientset/versioned/typed/istio/v1alpha3/generated_expansion.go => apis/autoscaling/v1alpha1/doc.go} (85%) create mode 100644 vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/kpa_defaults.go create mode 100644 vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/kpa_types.go create mode 100644 vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/kpa_validation.go rename vendor/github.com/knative/serving/pkg/apis/{istio/v1alpha3 => autoscaling/v1alpha1}/register.go (84%) create mode 100644 vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/zz_generated.deepcopy.go delete mode 100644 vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/gateway_types.go delete mode 100644 vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/virtualservice_types.go delete mode 100644 vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/zz_generated.deepcopy.go create mode 100644 vendor/github.com/knative/serving/pkg/apis/networking/register.go create mode 100644 vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/clusteringress_defaults.go create mode 100644 vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/clusteringress_types.go create mode 100644 vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/clusteringress_validation.go create mode 100644 vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/doc.go create mode 100644 vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/register.go create mode 100644 vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go create mode 100644 vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/build_compat.go delete mode 100644 vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/field_error.go create mode 100644 vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/metadata_validation.go create mode 100644 vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/autoscaling_client.go rename vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/{istio/v1alpha3 => autoscaling/v1alpha1}/doc.go (97%) rename vendor/github.com/knative/serving/pkg/{apis/istio/register.go => client/clientset/versioned/typed/autoscaling/v1alpha1/generated_expansion.go} (90%) create mode 100644 vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/podautoscaler.go delete mode 100644 vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/gateway.go delete mode 100644 vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/virtualservice.go create mode 100644 vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/clusteringress.go create mode 100644 vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/doc.go rename vendor/github.com/knative/serving/pkg/{apis/serving/v1alpha1/immutable.go => client/clientset/versioned/typed/networking/v1alpha1/generated_expansion.go} (87%) rename vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/{istio/v1alpha3/istio_client.go => networking/v1alpha1/networking_client.go} (59%) delete mode 120000 vendor/github.com/knative/serving/pkg/controller/revision/config/testdata/config-controller.yaml delete mode 120000 vendor/github.com/knative/serving/pkg/controller/revision/config/testdata/config-network.yaml delete mode 120000 vendor/github.com/knative/serving/pkg/controller/revision/config/testdata/config-observability.yaml delete mode 120000 vendor/github.com/knative/serving/pkg/controller/route/config/testdata/config-domain.yaml create mode 120000 vendor/github.com/knative/serving/pkg/gc/testdata/config-gc.yaml create mode 120000 vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/configuration/config/testdata/config-gc.yaml create mode 120000 vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-autoscaler.yaml create mode 120000 vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-controller.yaml create mode 120000 vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-logging.yaml create mode 120000 vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-network.yaml create mode 120000 vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-observability.yaml create mode 120000 vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/route/config/testdata/config-domain.yaml create mode 120000 vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/route/config/testdata/config-gc.yaml rename vendor/github.com/knative/serving/third_party/config/monitoring/{common/kubernetes => logging/elasticsearch}/LICENSE (100%) rename vendor/github.com/knative/serving/third_party/config/monitoring/{common => metrics/prometheus}/istio/LICENSE (100%) rename vendor/github.com/knative/serving/third_party/config/monitoring/{elasticsearch => metrics/prometheus/kubernetes}/LICENSE (100%) rename vendor/github.com/knative/serving/third_party/config/monitoring/{common => metrics/prometheus}/prometheus-operator/LICENSE (100%) rename vendor/github.com/knative/serving/third_party/config/monitoring/{common => metrics/prometheus}/prometheus-operator/NOTICE (100%) diff --git a/Gopkg.lock b/Gopkg.lock index da20476a89c..2832e370dd1 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -249,14 +249,15 @@ revision = "f2b4162afba35581b6d4a50d3b8f34e33c144682" [[projects]] - digest = "1:3b8adce8bbe5865d7155ddfadbd063915265f04a22ab47a3f5cddd7b32333d1e" + digest = "1:345bbba667abadd6263391c915251ede8d9fa6f6852839c60bb6738b6122b89c" name = "github.com/knative/build" packages = [ "pkg/apis/build", "pkg/apis/build/v1alpha1", ] pruneopts = "NUT" - revision = "5c1d8c8469d1ed34b2aecf4c2305b3a57fff2ee3" + revision = "94859753e2c6724df2be86f6a254f810895fa3eb" + version = "v0.2.0" [[projects]] branch = "master" @@ -272,7 +273,7 @@ revision = "aa113a015beb91c94a8e173c8f0ac18512a83954" [[projects]] - digest = "1:760be297dcd17acd32d2b4a7b9494be96e66451bfce53fc6fbaccad4ab2a5fa8" + digest = "1:9870368d47209c3a52b9000fd00420c51832604c00a65aa0a56b6e60a2ff3664" name = "github.com/knative/pkg" packages = [ "apis", @@ -298,6 +299,7 @@ "client/listers/istio/v1alpha3", "cloudevents", "configmap", + "kmeta", "logging", "logging/logkey", "signals", @@ -311,21 +313,24 @@ revision = "4b704fa7948ad9ae8ec90d1cd5b4a34516b252ea" [[projects]] - digest = "1:63f3974f3afe3dc5b6a115c0d53b0897cd01be6880c4bf5d014fc69a95db6ed1" + digest = "1:cf62a9e49c586f87503a2ca683f636afae817333b6056c7529f2643ec285ed7e" name = "github.com/knative/serving" packages = [ - "pkg/apis/istio", - "pkg/apis/istio/v1alpha3", + "pkg/apis/autoscaling", + "pkg/apis/autoscaling/v1alpha1", + "pkg/apis/networking", + "pkg/apis/networking/v1alpha1", "pkg/apis/serving", "pkg/apis/serving/v1alpha1", "pkg/client/clientset/versioned", "pkg/client/clientset/versioned/scheme", - "pkg/client/clientset/versioned/typed/istio/v1alpha3", + "pkg/client/clientset/versioned/typed/autoscaling/v1alpha1", + "pkg/client/clientset/versioned/typed/networking/v1alpha1", "pkg/client/clientset/versioned/typed/serving/v1alpha1", ] pruneopts = "NUT" - revision = "37c640555a3241ed707962ba6b29d6c7fe68f937" - version = "v0.1.1" + revision = "1036f34badadfa711d88378a1c09a3527bb1584b" + version = "v0.2.1" [[projects]] branch = "master" diff --git a/Gopkg.toml b/Gopkg.toml index a89b7028dae..12934b0b2ce 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -77,7 +77,7 @@ required = [ [[constraint]] name = "github.com/knative/serving" - version = "v0.1.1" + version = "v0.2.1" [[override]] name = "github.com/Shopify/sarama" diff --git a/vendor/github.com/knative/build/AUTHORS b/vendor/github.com/knative/build/AUTHORS index ab85282788a..9c2b57e2ca4 100644 --- a/vendor/github.com/knative/build/AUTHORS +++ b/vendor/github.com/knative/build/AUTHORS @@ -5,3 +5,4 @@ # of contributors, see the revision history in source control. Google LLC Pivotal Software, Inc. +Red Hat, Inc. \ No newline at end of file diff --git a/vendor/github.com/knative/build/cmd/controller/kodata/LICENSE b/vendor/github.com/knative/build/cmd/controller/kodata/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/vendor/github.com/knative/build/cmd/controller/kodata/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/cmd/controller/kodata/VENDOR-LICENSE b/vendor/github.com/knative/build/cmd/controller/kodata/VENDOR-LICENSE new file mode 120000 index 00000000000..3cc89764519 --- /dev/null +++ b/vendor/github.com/knative/build/cmd/controller/kodata/VENDOR-LICENSE @@ -0,0 +1 @@ +../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/cmd/creds-init/kodata/LICENSE b/vendor/github.com/knative/build/cmd/creds-init/kodata/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/vendor/github.com/knative/build/cmd/creds-init/kodata/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/cmd/creds-init/kodata/VENDOR-LICENSE b/vendor/github.com/knative/build/cmd/creds-init/kodata/VENDOR-LICENSE new file mode 120000 index 00000000000..3cc89764519 --- /dev/null +++ b/vendor/github.com/knative/build/cmd/creds-init/kodata/VENDOR-LICENSE @@ -0,0 +1 @@ +../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/cmd/git-init/kodata/LICENSE b/vendor/github.com/knative/build/cmd/git-init/kodata/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/vendor/github.com/knative/build/cmd/git-init/kodata/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/cmd/git-init/kodata/VENDOR-LICENSE b/vendor/github.com/knative/build/cmd/git-init/kodata/VENDOR-LICENSE new file mode 120000 index 00000000000..3cc89764519 --- /dev/null +++ b/vendor/github.com/knative/build/cmd/git-init/kodata/VENDOR-LICENSE @@ -0,0 +1 @@ +../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/cmd/logs/kodata/LICENSE b/vendor/github.com/knative/build/cmd/logs/kodata/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/vendor/github.com/knative/build/cmd/logs/kodata/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/cmd/logs/kodata/VENDOR-LICENSE b/vendor/github.com/knative/build/cmd/logs/kodata/VENDOR-LICENSE new file mode 120000 index 00000000000..3cc89764519 --- /dev/null +++ b/vendor/github.com/knative/build/cmd/logs/kodata/VENDOR-LICENSE @@ -0,0 +1 @@ +../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/cmd/nop/kodata/LICENSE b/vendor/github.com/knative/build/cmd/nop/kodata/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/vendor/github.com/knative/build/cmd/nop/kodata/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/cmd/nop/kodata/VENDOR-LICENSE b/vendor/github.com/knative/build/cmd/nop/kodata/VENDOR-LICENSE new file mode 120000 index 00000000000..3cc89764519 --- /dev/null +++ b/vendor/github.com/knative/build/cmd/nop/kodata/VENDOR-LICENSE @@ -0,0 +1 @@ +../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/cmd/webhook/kodata/LICENSE b/vendor/github.com/knative/build/cmd/webhook/kodata/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/vendor/github.com/knative/build/cmd/webhook/kodata/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/cmd/webhook/kodata/VENDOR-LICENSE b/vendor/github.com/knative/build/cmd/webhook/kodata/VENDOR-LICENSE new file mode 120000 index 00000000000..3cc89764519 --- /dev/null +++ b/vendor/github.com/knative/build/cmd/webhook/kodata/VENDOR-LICENSE @@ -0,0 +1 @@ +../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/config/300-imagecache.yaml b/vendor/github.com/knative/build/config/300-imagecache.yaml new file mode 120000 index 00000000000..f10d6dacf6a --- /dev/null +++ b/vendor/github.com/knative/build/config/300-imagecache.yaml @@ -0,0 +1 @@ +../vendor/github.com/knative/caching/config/image.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/build/pkg/apis/build/register.go b/vendor/github.com/knative/build/pkg/apis/build/register.go index ec8299fe8c2..379817b13ca 100644 --- a/vendor/github.com/knative/build/pkg/apis/build/register.go +++ b/vendor/github.com/knative/build/pkg/apis/build/register.go @@ -16,4 +16,5 @@ limitations under the License. package build +// GroupName is the Kubernetes resource group name for Build types. const GroupName = "build.knative.dev" diff --git a/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_defaults.go b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_defaults.go new file mode 100644 index 00000000000..fafe21989ae --- /dev/null +++ b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_defaults.go @@ -0,0 +1,42 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +// DefaultTimeout is 10min +const DefaultTimeout = 10 * time.Minute + +// SetDefaults for build +func (b *Build) SetDefaults() { + if b == nil { + return + } + if b.Spec.ServiceAccountName == "" { + b.Spec.ServiceAccountName = "default" + } + if b.Spec.Timeout == nil { + b.Spec.Timeout = &metav1.Duration{Duration: DefaultTimeout} + } + if b.Spec.Template != nil && b.Spec.Template.Kind == "" { + b.Spec.Template.Kind = BuildTemplateKind + } +} diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/defaults.go b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_template_interface.go similarity index 71% rename from vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/defaults.go rename to vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_template_interface.go index f458c0650f7..a14dba3b734 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/defaults.go +++ b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_template_interface.go @@ -1,5 +1,6 @@ /* -Copyright 2017 The Knative Authors +Copyright 2018 The Knative Authors + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -15,8 +16,8 @@ limitations under the License. package v1alpha1 -// Defaultable defines an interface for setting the defaults for the -// uninitialized fields of this instance. -type Defaultable interface { - SetDefaults() +// BuildTemplateInterface is implemented by BuildTemplate and ClusterBuildTemplate +type BuildTemplateInterface interface { + TemplateSpec() BuildTemplateSpec + Copy() BuildTemplateInterface } diff --git a/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_template_types.go b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_template_types.go index 7d63ea4b722..20d78c03077 100644 --- a/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_template_types.go +++ b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_template_types.go @@ -17,12 +17,20 @@ limitations under the License. package v1alpha1 import ( - "encoding/json" - + "github.com/knative/pkg/apis" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/knative/pkg/kmeta" ) +// Template is an interface for accessing the BuildTemplateSpec +// from various forms of template (namespace-/cluster-scoped). +type Template interface { + TemplateSpec() BuildTemplateSpec +} + // +genclient // +genclient:noStatus // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -35,6 +43,15 @@ type BuildTemplate struct { Spec BuildTemplateSpec `json:"spec"` } +// Check that our resource implements several interfaces. +var _ kmeta.OwnerRefable = (*BuildTemplate)(nil) +var _ Template = (*BuildTemplate)(nil) +var _ BuildTemplateInterface = (*BuildTemplate)(nil) + +// Check that BuildTemplate may be validated and defaulted. +var _ apis.Validatable = (*BuildTemplate)(nil) +var _ apis.Defaultable = (*BuildTemplate)(nil) + // BuildTemplateSpec is the spec for a BuildTemplate. type BuildTemplateSpec struct { // TODO: Generation does not work correctly with CRD. They are scrubbed @@ -80,6 +97,20 @@ type BuildTemplateList struct { Items []BuildTemplate `json:"items"` } -func (bt *BuildTemplate) GetGeneration() int64 { return bt.Spec.Generation } -func (bt *BuildTemplate) SetGeneration(generation int64) { bt.Spec.Generation = generation } -func (bt *BuildTemplate) GetSpecJSON() ([]byte, error) { return json.Marshal(bt.Spec) } +// TemplateSpec returnes the Spec used by the template +func (bt *BuildTemplate) TemplateSpec() BuildTemplateSpec { + return bt.Spec +} + +// Copy performes a deep copy +func (bt *BuildTemplate) Copy() BuildTemplateInterface { + return bt.DeepCopy() +} + +// GetGroupVersionKind gives kind +func (bt *BuildTemplate) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("BuildTemplate") +} + +// SetDefaults for build template +func (bt *BuildTemplate) SetDefaults() {} diff --git a/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_template_validation.go b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_template_validation.go new file mode 100644 index 00000000000..cd3464c1fbe --- /dev/null +++ b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_template_validation.go @@ -0,0 +1,86 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "github.com/knative/pkg/apis" + corev1 "k8s.io/api/core/v1" +) + +// Validate build template +func (b *BuildTemplate) Validate() *apis.FieldError { + return validateObjectMetadata(b.GetObjectMeta()).ViaField("metadata").Also(b.Spec.Validate().ViaField("spec")) +} + +// Validate Build Template +func (b *BuildTemplateSpec) Validate() *apis.FieldError { + if err := validateSteps(b.Steps); err != nil { + return err + } + if err := ValidateVolumes(b.Volumes); err != nil { + return err + } + if err := validateParameters(b.Parameters); err != nil { + return err + } + return nil +} + +//ValidateVolumes validates collection of volumes that are available to mount into the +// steps of the build ot build template. +func ValidateVolumes(volumes []corev1.Volume) *apis.FieldError { + // Build must not duplicate volume names. + vols := map[string]struct{}{} + for _, v := range volumes { + if _, ok := vols[v.Name]; ok { + return apis.ErrMultipleOneOf("volumeName") + } + vols[v.Name] = struct{}{} + } + return nil +} + +func validateSteps(steps []corev1.Container) *apis.FieldError { + // Build must not duplicate step names. + names := map[string]struct{}{} + for _, s := range steps { + if s.Image == "" { + return apis.ErrMissingField("Image") + } + + if s.Name == "" { + continue + } + if _, ok := names[s.Name]; ok { + return apis.ErrMultipleOneOf("stepName") + } + names[s.Name] = struct{}{} + } + return nil +} + +func validateParameters(params []ParameterSpec) *apis.FieldError { + // Template must not duplicate parameter names. + seen := map[string]struct{}{} + for _, p := range params { + if _, ok := seen[p.Name]; ok { + return apis.ErrInvalidKeyName("ParamName", "b.spec.params") + } + seen[p.Name] = struct{}{} + } + return nil +} diff --git a/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_types.go b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_types.go index 9a2640db1bf..2d484b877ef 100644 --- a/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_types.go +++ b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_types.go @@ -17,10 +17,13 @@ limitations under the License. package v1alpha1 import ( - "encoding/json" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/knative/pkg/apis" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "github.com/knative/pkg/kmeta" ) // +genclient @@ -38,6 +41,13 @@ type Build struct { Status BuildStatus `json:"status"` } +// Check that our resource implements several interfaces. +var _ kmeta.OwnerRefable = (*Build)(nil) + +// Check that Build may be validated and defaulted. +var _ apis.Validatable = (*Build)(nil) +var _ apis.Defaultable = (*Build)(nil) + // BuildSpec is the spec for a Build resource. type BuildSpec struct { // TODO: Generation does not work correctly with CRD. They are scrubbed @@ -63,10 +73,36 @@ type BuildSpec struct { // Template, if specified, references a BuildTemplate resource to use to // populate fields in the build, and optional Arguments to pass to the - // template. + // template. The default Kind of template is BuildTemplate Template *TemplateInstantiationSpec `json:"template,omitempty"` + + // NodeSelector is a selector which must be true for the pod to fit on a node. + // Selector which must match a node's labels for the pod to be scheduled on that node. + // More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/ + // +optional + NodeSelector map[string]string `json:"nodeSelector,omitempty"` + + // Time after which the build times out. Defaults to 10 minutes. + // Specified build timeout should be less than 24h. + // Refer Go's ParseDuration documentation for expected format: https://golang.org/pkg/time/#ParseDuration + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // If specified, the pod's scheduling constraints + // +optional + Affinity *corev1.Affinity `json:"affinity,omitempty"` } +// TemplateKind defines the type of BuildTemplate used by the build. +type TemplateKind string + +const ( + // BuildTemplateKind indicates that the template type has a namepace scope. + BuildTemplateKind TemplateKind = "BuildTemplate" + // ClusterBuildTemplateKind indicates that template type has a cluster scope. + ClusterBuildTemplateKind TemplateKind = "ClusterBuildTemplate" +) + // TemplateInstantiationSpec specifies how a BuildTemplate is instantiated into // a Build. type TemplateInstantiationSpec struct { @@ -75,6 +111,10 @@ type TemplateInstantiationSpec struct { // The template is assumed to exist in the Build's namespace. Name string `json:"name"` + // The Kind of the template to be used, possible values are BuildTemplate + // or ClusterBuildTemplate. If nothing is specified, the default if is BuildTemplate + Kind TemplateKind `json:"kind,omitempty"` + // Arguments, if specified, lists values that should be applied to the // parameters specified by the template. Arguments []ArgumentSpec `json:"arguments,omitempty"` @@ -87,16 +127,30 @@ type TemplateInstantiationSpec struct { // ArgumentSpec defines the actual values to use to populate a template's // parameters. type ArgumentSpec struct { - Name string `json:"name"` + // Name is the name of the argument. + Name string `json:"name"` + // Value is the value of the argument. Value string `json:"value"` // TODO(jasonhall): ValueFrom? } // SourceSpec defines the input to the Build type SourceSpec struct { - Git *GitSourceSpec `json:"git,omitempty"` - GCS *GCSSourceSpec `json:"gcs,omitempty"` + // Git represents source in a Git repository. + Git *GitSourceSpec `json:"git,omitempty"` + + // GCS represents source in Google Cloud Storage. + GCS *GCSSourceSpec `json:"gcs,omitempty"` + + // Custom indicates that source should be retrieved using a custom + // process defined in a container invocation. Custom *corev1.Container `json:"custom,omitempty"` + + // SubPath specifies a path within the fetched source which should be + // built. This option makes parent directories *inaccessible* to the + // build steps. (The specific source type may, in fact, not even fetch + // files not in the SubPath.) + SubPath string `json:"subPath,omitempty"` } // GitSourceSpec describes a Git repo source input to the Build. @@ -113,21 +167,30 @@ type GitSourceSpec struct { // GCSSourceSpec describes source input to the Build in the form of an archive, // or a source manifest describing files to fetch. type GCSSourceSpec struct { - Type GCSSourceType `json:"type,omitempty"` - Location string `json:"location,omitempty"` + // Type declares the style of source to fetch. + Type GCSSourceType `json:"type,omitempty"` + + // Location specifies the location of the source archive or manifest file. + Location string `json:"location,omitempty"` } +// GCSSourceType defines a type of GCS source fetch. type GCSSourceType string const ( - GCSArchive GCSSourceType = "Archive" + // GCSArchive indicates that source should be fetched from a typical archive file. + GCSArchive GCSSourceType = "Archive" + + // GCSManifest indicates that source should be fetched using a + // manifest-based protocol which enables incremental source upload. GCSManifest GCSSourceType = "Manifest" ) +// BuildProvider defines a build execution implementation. type BuildProvider string const ( - // GoogleBuildProvider indicates that this build was performed with Google Container Builder. + // GoogleBuildProvider indicates that this build was performed with Google Cloud Build. GoogleBuildProvider BuildProvider = "Google" // ClusterBuildProvider indicates that this build was performed on-cluster. ClusterBuildProvider BuildProvider = "Cluster" @@ -137,51 +200,51 @@ const ( type BuildStatus struct { Builder BuildProvider `json:"builder,omitempty"` - // Additional information based on the Builder executing this build. + // Cluster provides additional information if the builder is Cluster. Cluster *ClusterSpec `json:"cluster,omitempty"` - Google *GoogleSpec `json:"google,omitempty"` + // Google provides additional information if the builder is Google. + Google *GoogleSpec `json:"google,omitempty"` - // Information about the execution of the build. - StartTime metav1.Time `json:"startTime,omitEmpty"` + // StartTime is the time the build is actually started. + StartTime metav1.Time `json:"startTime,omitEmpty"` + // CompletionTime is the time the build completed. CompletionTime metav1.Time `json:"completionTime,omitEmpty"` - // Parallel list to spec.Containers + // StepStates describes the state of each build step container. StepStates []corev1.ContainerState `json:"stepStates,omitEmpty"` - Conditions []BuildCondition `json:"conditions,omitempty"` + + // StepsCompleted lists the name of build steps completed. + StepsCompleted []string `json:"stepsCompleted"` + + // Conditions describes the set of conditions of this build. + Conditions duckv1alpha1.Conditions `json:"conditions,omitempty"` } +// Check that BuildStatus may have its conditions managed. +var _ duckv1alpha1.ConditionsAccessor = (*BuildStatus)(nil) + +// ClusterSpec provides information about the on-cluster build, if applicable. type ClusterSpec struct { + // Namespace is the namespace in which the pod is running. Namespace string `json:"namespace"` - PodName string `json:"podName"` + // PodName is the name of the pod responsible for executing this build's steps. + PodName string `json:"podName"` } +// GoogleSpec provides information about the GCB build, if applicable. type GoogleSpec struct { + // Operation is the unique name of the GCB API Operation for the build. Operation string `json:"operation"` } -type BuildConditionType string - -const ( - // BuildSucceeded is set when the build is running, and becomes True - // when the build finishes successfully. - // - // If the build is ongoing, its status will be Unknown. If it fails, - // its status will be False. - BuildSucceeded BuildConditionType = "Succeeded" -) - -// BuildCondition defines a readiness condition for a Build. -// See: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#typical-status-properties -type BuildCondition struct { - Type BuildConditionType `json:"state"` +// BuildSucceeded is set when the build is running, and becomes True when the +// build finishes successfully. +// +// If the build is ongoing, its status will be Unknown. If it fails, its status +// will be False. +const BuildSucceeded = duckv1alpha1.ConditionSucceeded - Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` - - // +optional - Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"` - // +optional - Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"` -} +var buildCondSet = duckv1alpha1.NewBatchConditionSet() // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -190,44 +253,35 @@ type BuildList struct { metav1.TypeMeta `json:",inline"` metav1.ListMeta `json:"metadata"` + // Items is the list of Build items in this list. Items []Build `json:"items"` } -func (bs *BuildStatus) GetCondition(t BuildConditionType) *BuildCondition { - for _, cond := range bs.Conditions { - if cond.Type == t { - return &cond - } - } - return nil +// GetCondition returns the Condition matching the given type. +func (bs *BuildStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { + return buildCondSet.Manage(bs).GetCondition(t) } -func (b *BuildStatus) SetCondition(newCond *BuildCondition) { - if newCond == nil { - return +// SetCondition sets the condition, unsetting previous conditions with the same +// type as necessary. +func (bs *BuildStatus) SetCondition(newCond *duckv1alpha1.Condition) { + if newCond != nil { + buildCondSet.Manage(bs).SetCondition(*newCond) } +} - t := newCond.Type - var conditions []BuildCondition - for _, cond := range b.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - conditions = append(conditions, *newCond) - b.Conditions = conditions +// GetConditions returns the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (bs *BuildStatus) GetConditions() duckv1alpha1.Conditions { + return bs.Conditions } -func (b *BuildStatus) RemoveCondition(t BuildConditionType) { - var conditions []BuildCondition - for _, cond := range b.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - b.Conditions = conditions +// SetConditions sets the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (bs *BuildStatus) SetConditions(conditions duckv1alpha1.Conditions) { + bs.Conditions = conditions } -func (b *Build) GetGeneration() int64 { return b.Spec.Generation } -func (b *Build) SetGeneration(generation int64) { b.Spec.Generation = generation } -func (b *Build) GetSpecJSON() ([]byte, error) { return json.Marshal(b.Spec) } +func (b *Build) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("Build") +} diff --git a/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_validation.go b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_validation.go new file mode 100644 index 00000000000..04792a42f6d --- /dev/null +++ b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/build_validation.go @@ -0,0 +1,94 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "fmt" + "time" + + "github.com/knative/pkg/apis" +) + +// Validate Build +func (b *Build) Validate() *apis.FieldError { + return validateObjectMetadata(b.GetObjectMeta()).ViaField("metadata").Also(b.Spec.Validate().ViaField("spec")) +} + +// Validate for build spec +func (bs *BuildSpec) Validate() *apis.FieldError { + if bs.Template == nil && len(bs.Steps) == 0 { + return apis.ErrMissingField("b.spec.template").Also(apis.ErrMissingField("b.spec.steps")) + } + if bs.Template != nil && len(bs.Steps) > 0 { + return apis.ErrMissingField("b.spec.template").Also(apis.ErrMissingField("b.spec.steps")) + } + + if bs.Template != nil && bs.Template.Name == "" { + apis.ErrMissingField("build.spec.template.name") + } + + // If a build specifies a template, all the template's parameters without + // defaults must be satisfied by the build's parameters. + if bs.Template != nil { + return bs.Template.Validate() + } + if err := ValidateVolumes(bs.Volumes); err != nil { + return err + } + if err := bs.validateTimeout(); err != nil { + return err + } + + if err := validateSteps(bs.Steps); err != nil { + return err + } + return nil +} + +// Validate templateKind +func (b *TemplateInstantiationSpec) Validate() *apis.FieldError { + if b == nil { + return nil + } + if b.Name == "" { + return apis.ErrMissingField("build.spec.template.name") + } + if b.Kind != "" { + switch b.Kind { + case ClusterBuildTemplateKind, + BuildTemplateKind: + return nil + default: + return apis.ErrInvalidValue(string(b.Kind), apis.CurrentField) + } + } + return nil +} + +func (bt *BuildSpec) validateTimeout() *apis.FieldError { + if bt.Timeout == nil { + return nil + } + maxTimeout := time.Duration(24 * time.Hour) + + if bt.Timeout.Duration > maxTimeout { + return apis.ErrInvalidValue(fmt.Sprintf("%s should be < 24h", bt.Timeout), "b.spec.timeout") + } else if bt.Timeout.Duration < 0 { + return apis.ErrInvalidValue(fmt.Sprintf("%s should be > 0", bt.Timeout), "b.spec.timeout") + } + return nil +} diff --git a/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/cluster_build_template_types.go b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/cluster_build_template_types.go new file mode 100644 index 00000000000..604b13371de --- /dev/null +++ b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/cluster_build_template_types.go @@ -0,0 +1,74 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/knative/pkg/apis" + "github.com/knative/pkg/kmeta" +) + +// +genclient +// +genclient:noStatus +// +genclient:nonNamespaced +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterBuildTemplate is a template that can used to easily create Builds. +type ClusterBuildTemplate struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec BuildTemplateSpec `json:"spec"` +} + +// Check that our resource implements several interfaces. +var _ kmeta.OwnerRefable = (*ClusterBuildTemplate)(nil) +var _ Template = (*ClusterBuildTemplate)(nil) +var _ BuildTemplateInterface = (*ClusterBuildTemplate)(nil) + +// Check that ClusterBuildTemplate may be validated and defaulted. +var _ apis.Validatable = (*ClusterBuildTemplate)(nil) +var _ apis.Defaultable = (*ClusterBuildTemplate)(nil) + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterBuildTemplateList is a list of BuildTemplate resources. +type ClusterBuildTemplateList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []ClusterBuildTemplate `json:"items"` +} + +// TemplateSpec returnes the Spec used by the template +func (bt *ClusterBuildTemplate) TemplateSpec() BuildTemplateSpec { + return bt.Spec +} + +// Copy performes a deep copy +func (bt *ClusterBuildTemplate) Copy() BuildTemplateInterface { + return bt.DeepCopy() +} + +func (bt *ClusterBuildTemplate) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("ClusterBuildTemplate") +} + +// SetDefaults +func (b *ClusterBuildTemplate) SetDefaults() {} diff --git a/vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/doc.go b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/cluster_build_template_validation.go similarity index 68% rename from vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/doc.go rename to vendor/github.com/knative/build/pkg/apis/build/v1alpha1/cluster_build_template_validation.go index 47ec83daedf..111c8d5cc68 100644 --- a/vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/doc.go +++ b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/cluster_build_template_validation.go @@ -14,10 +14,11 @@ See the License for the specific language governing permissions and limitations under the License. */ -// Api versions allow the api contract for a resource to be changed while keeping -// backward compatibility by support multiple concurrent versions -// of the same resource +package v1alpha1 -// +k8s:deepcopy-gen=package -// +groupName=networking.istio.io -package v1alpha3 +import "github.com/knative/pkg/apis" + +// Validate ClusterBuildTemplate +func (b *ClusterBuildTemplate) Validate() *apis.FieldError { + return validateObjectMetadata(b.GetObjectMeta()).ViaField("metadata").Also(b.Spec.Validate().ViaField("spec")) +} diff --git a/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/metadata_validation.go b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/metadata_validation.go new file mode 100644 index 00000000000..8801c3fca6f --- /dev/null +++ b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/metadata_validation.go @@ -0,0 +1,47 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "strings" + + "github.com/knative/pkg/apis" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + maxLength = 63 +) + +func validateObjectMetadata(meta metav1.Object) *apis.FieldError { + name := meta.GetName() + + if strings.Contains(name, ".") { + return &apis.FieldError{ + Message: "Invalid resource name: special character . must not be present", + Paths: []string{"name"}, + } + } + + if len(name) > maxLength { + return &apis.FieldError{ + Message: "Invalid resource name: length must be no more than 63 characters", + Paths: []string{"name"}, + } + } + return nil +} diff --git a/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/register.go b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/register.go index 95c900c26ee..c2b2c6512c7 100644 --- a/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/register.go +++ b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/register.go @@ -38,8 +38,10 @@ func Resource(resource string) schema.GroupResource { } var ( - SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) - AddToScheme = SchemeBuilder.AddToScheme + schemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + + // AddToScheme adds Build types to the scheme. + AddToScheme = schemeBuilder.AddToScheme ) // Adds the list of known types to Scheme. @@ -49,6 +51,8 @@ func addKnownTypes(scheme *runtime.Scheme) error { &BuildList{}, &BuildTemplate{}, &BuildTemplateList{}, + &ClusterBuildTemplate{}, + &ClusterBuildTemplateList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/zz_generated.deepcopy.go index a73da3e46ac..0880074fb1b 100644 --- a/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/zz_generated.deepcopy.go +++ b/vendor/github.com/knative/build/pkg/apis/build/v1alpha1/zz_generated.deepcopy.go @@ -21,7 +21,9 @@ limitations under the License. package v1alpha1 import ( + duck_v1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" v1 "k8s.io/api/core/v1" + meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -69,22 +71,6 @@ func (in *Build) DeepCopyObject() runtime.Object { return nil } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *BuildCondition) DeepCopyInto(out *BuildCondition) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new BuildCondition. -func (in *BuildCondition) DeepCopy() *BuildCondition { - if in == nil { - return nil - } - out := new(BuildCondition) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *BuildList) DeepCopyInto(out *BuildList) { *out = *in @@ -153,6 +139,31 @@ func (in *BuildSpec) DeepCopyInto(out *BuildSpec) { (*in).DeepCopyInto(*out) } } + if in.NodeSelector != nil { + in, out := &in.NodeSelector, &out.NodeSelector + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + if *in == nil { + *out = nil + } else { + *out = new(meta_v1.Duration) + **out = **in + } + } + if in.Affinity != nil { + in, out := &in.Affinity, &out.Affinity + if *in == nil { + *out = nil + } else { + *out = new(v1.Affinity) + (*in).DeepCopyInto(*out) + } + } return } @@ -196,10 +207,17 @@ func (in *BuildStatus) DeepCopyInto(out *BuildStatus) { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.StepsCompleted != nil { + in, out := &in.StepsCompleted, &out.StepsCompleted + *out = make([]string, len(*in)) + copy(*out, *in) + } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]BuildCondition, len(*in)) - copy(*out, *in) + *out = make(duck_v1alpha1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } } return } @@ -311,6 +329,66 @@ func (in *BuildTemplateSpec) DeepCopy() *BuildTemplateSpec { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterBuildTemplate) DeepCopyInto(out *ClusterBuildTemplate) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterBuildTemplate. +func (in *ClusterBuildTemplate) DeepCopy() *ClusterBuildTemplate { + if in == nil { + return nil + } + out := new(ClusterBuildTemplate) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterBuildTemplate) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterBuildTemplateList) DeepCopyInto(out *ClusterBuildTemplateList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterBuildTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterBuildTemplateList. +func (in *ClusterBuildTemplateList) DeepCopy() *ClusterBuildTemplateList { + if in == nil { + return nil + } + out := new(ClusterBuildTemplateList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterBuildTemplateList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ClusterSpec) DeepCopyInto(out *ClusterSpec) { *out = *in diff --git a/vendor/github.com/knative/build/test/panic/kodata/LICENSE b/vendor/github.com/knative/build/test/panic/kodata/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/vendor/github.com/knative/build/test/panic/kodata/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/test/panic/kodata/VENDOR-LICENSE b/vendor/github.com/knative/build/test/panic/kodata/VENDOR-LICENSE new file mode 120000 index 00000000000..3cc89764519 --- /dev/null +++ b/vendor/github.com/knative/build/test/panic/kodata/VENDOR-LICENSE @@ -0,0 +1 @@ +../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/test/workingdir/kodata/LICENSE b/vendor/github.com/knative/build/test/workingdir/kodata/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/vendor/github.com/knative/build/test/workingdir/kodata/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/build/test/workingdir/kodata/VENDOR-LICENSE b/vendor/github.com/knative/build/test/workingdir/kodata/VENDOR-LICENSE new file mode 120000 index 00000000000..3cc89764519 --- /dev/null +++ b/vendor/github.com/knative/build/test/workingdir/kodata/VENDOR-LICENSE @@ -0,0 +1 @@ +../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/pkg/kmeta/accessor.go b/vendor/github.com/knative/pkg/kmeta/accessor.go new file mode 100644 index 00000000000..07c69bedaa6 --- /dev/null +++ b/vendor/github.com/knative/pkg/kmeta/accessor.go @@ -0,0 +1,94 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kmeta + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/tools/cache" +) + +// Accessor is a collection of interfaces from metav1.TypeMeta, +// runtime.Object and metav1.Object that Kubernetes API types +// registered with runtime.Scheme must support. +type Accessor interface { + // Interfaces for metav1.TypeMeta + GroupVersionKind() schema.GroupVersionKind + SetGroupVersionKind(gvk schema.GroupVersionKind) + + // Interfaces for runtime.Object + GetObjectKind() schema.ObjectKind + DeepCopyObject() runtime.Object + + // Interfaces for metav1.Object + GetNamespace() string + SetNamespace(namespace string) + GetName() string + SetName(name string) + GetGenerateName() string + SetGenerateName(name string) + GetUID() types.UID + SetUID(uid types.UID) + GetResourceVersion() string + SetResourceVersion(version string) + GetGeneration() int64 + SetGeneration(generation int64) + GetSelfLink() string + SetSelfLink(selfLink string) + GetCreationTimestamp() metav1.Time + SetCreationTimestamp(timestamp metav1.Time) + GetDeletionTimestamp() *metav1.Time + SetDeletionTimestamp(timestamp *metav1.Time) + GetDeletionGracePeriodSeconds() *int64 + SetDeletionGracePeriodSeconds(*int64) + GetLabels() map[string]string + SetLabels(labels map[string]string) + GetAnnotations() map[string]string + SetAnnotations(annotations map[string]string) + GetInitializers() *metav1.Initializers + SetInitializers(initializers *metav1.Initializers) + GetFinalizers() []string + SetFinalizers(finalizers []string) + GetOwnerReferences() []metav1.OwnerReference + SetOwnerReferences([]metav1.OwnerReference) + GetClusterName() string + SetClusterName(clusterName string) +} + +// DeletionHandlingAccessor tries to convert given interface into Accessor first; +// and to handle deletion, it try to fetch info from DeletedFinalStateUnknown on failure. +// The name is a reference to cache.DeletionHandlingMetaNamespaceKeyFunc +func DeletionHandlingAccessor(obj interface{}) (Accessor, error) { + accessor, ok := obj.(Accessor) + if !ok { + // To handle obj deletion, try to fetch info from DeletedFinalStateUnknown. + tombstone, ok := obj.(cache.DeletedFinalStateUnknown) + if !ok { + return nil, fmt.Errorf("Couldn't get Accessor from tombstone %#v", obj) + } + accessor, ok = tombstone.Obj.(Accessor) + if !ok { + return nil, fmt.Errorf("The object that Tombstone contained is not of kmeta.Accessor %#v", obj) + } + } + + return accessor, nil +} diff --git a/vendor/github.com/knative/pkg/kmeta/doc.go b/vendor/github.com/knative/pkg/kmeta/doc.go new file mode 100644 index 00000000000..53ff38d726b --- /dev/null +++ b/vendor/github.com/knative/pkg/kmeta/doc.go @@ -0,0 +1,19 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://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 kmeta provides Knative utilities for operating on Kubernetes +// resources' ObjectMeta. +package kmeta diff --git a/vendor/github.com/knative/pkg/kmeta/labels.go b/vendor/github.com/knative/pkg/kmeta/labels.go new file mode 100644 index 00000000000..f9a72d8bf37 --- /dev/null +++ b/vendor/github.com/knative/pkg/kmeta/labels.go @@ -0,0 +1,114 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kmeta + +import ( + "fmt" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/apimachinery/pkg/selection" +) + +// The methods in this file are used for managing subresources in cases where +// a controller instantiates different resources for each version of itself. +// There are two sets of methods available here: +// * `*VersionLabel*`: these methods act on `metadata.resourceVersion` and +// create new labels for EVERY change to the resource (incl. `/status`). +// * `*GenerationLabel*`: these methods act on `metadata.generation` and +// create new labels for changes to the resource's "spec" (typically, but +// some K8s resources change `metadata.generation` for annotations as well +// e.g. Deployment). +// +// For example, if an A might instantiate N B's at version 1 and M B's at +// version 2 then it can use MakeVersionLabels to decorate each subresource +// with the appropriate labels for the version at which it was instantiated. +// +// During reconciliation, MakeVersionLabelSelector can be used with the +// informer listers to access the appropriate subresources for the current +// version of the parent resource. +// +// Likewise during reconciliation, MakeOldVersionLabelSelector can be used +// with the API client's DeleteCollection method to clean up subresources +// for older versions of the resource. + +// MakeVersionLabels constructs a set of labels to apply to subresources +// instantiated at this version of the parent resource, so that we can +// efficiently select them. +func MakeVersionLabels(om metav1.ObjectMetaAccessor) labels.Set { + return map[string]string{ + "controller": string(om.GetObjectMeta().GetUID()), + "version": om.GetObjectMeta().GetResourceVersion(), + } +} + +// MakeVersionLabelSelector constructs a selector for subresources +// instantiated at this version of the parent resource. This keys +// off of the labels populated by MakeVersionLabels. +func MakeVersionLabelSelector(om metav1.ObjectMetaAccessor) labels.Selector { + return labels.SelectorFromSet(MakeVersionLabels(om)) +} + +// MakeOldVersionLabelSelector constructs a selector for subresources +// instantiated at an older version of the parent resource. This keys +// off of the labels populated by MakeVersionLabels. +func MakeOldVersionLabelSelector(om metav1.ObjectMetaAccessor) labels.Selector { + return labels.NewSelector().Add( + mustNewRequirement("controller", selection.Equals, []string{string(om.GetObjectMeta().GetUID())}), + mustNewRequirement("version", selection.NotEquals, []string{om.GetObjectMeta().GetResourceVersion()}), + ) +} + +// MakeGenerationLabels constructs a set of labels to apply to subresources +// instantiated at this version of the parent resource, so that we can +// efficiently select them. +func MakeGenerationLabels(om metav1.ObjectMetaAccessor) labels.Set { + return map[string]string{ + "controller": string(om.GetObjectMeta().GetUID()), + "generation": genStr(om), + } +} + +// MakeGenerationLabelSelector constructs a selector for subresources +// instantiated at this version of the parent resource. This keys +// off of the labels populated by MakeGenerationLabels. +func MakeGenerationLabelSelector(om metav1.ObjectMetaAccessor) labels.Selector { + return labels.SelectorFromSet(MakeGenerationLabels(om)) +} + +// MakeOldGenerationLabelSelector constructs a selector for subresources +// instantiated at an older version of the parent resource. This keys +// off of the labels populated by MakeGenerationLabels. +func MakeOldGenerationLabelSelector(om metav1.ObjectMetaAccessor) labels.Selector { + return labels.NewSelector().Add( + mustNewRequirement("controller", selection.Equals, []string{string(om.GetObjectMeta().GetUID())}), + mustNewRequirement("generation", selection.NotEquals, []string{genStr(om)}), + ) +} + +func genStr(om metav1.ObjectMetaAccessor) string { + return fmt.Sprintf("%05d", om.GetObjectMeta().GetGeneration()) +} + +// mustNewRequirement panics if there are any errors constructing our selectors. +func mustNewRequirement(key string, op selection.Operator, vals []string) labels.Requirement { + r, err := labels.NewRequirement(key, op, vals) + if err != nil { + panic(fmt.Sprintf("mustNewRequirement(%v, %v, %v) = %v", key, op, vals, err)) + } + return *r +} diff --git a/vendor/github.com/knative/pkg/kmeta/owner_references.go b/vendor/github.com/knative/pkg/kmeta/owner_references.go new file mode 100644 index 00000000000..2e9a1289f93 --- /dev/null +++ b/vendor/github.com/knative/pkg/kmeta/owner_references.go @@ -0,0 +1,38 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package kmeta + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// OwnerRefable indicates that a particular type has sufficient +// information to produce a metav1.OwnerReference to an object. +type OwnerRefable interface { + metav1.ObjectMetaAccessor + + // GetGroupVersionKind returns a GroupVersionKind. The name is chosen + // to avoid collision with TypeMeta's GroupVersionKind() method. + // See: https://issues.k8s.io/3030 + GetGroupVersionKind() schema.GroupVersionKind +} + +// NewControllerRef creates an OwnerReference pointing to the given controller. +func NewControllerRef(obj OwnerRefable) *metav1.OwnerReference { + return metav1.NewControllerRef(obj.GetObjectMeta(), obj.GetGroupVersionKind()) +} diff --git a/vendor/github.com/knative/serving/AUTHORS b/vendor/github.com/knative/serving/AUTHORS index 4e068958755..5ab90824ca2 100644 --- a/vendor/github.com/knative/serving/AUTHORS +++ b/vendor/github.com/knative/serving/AUTHORS @@ -6,4 +6,5 @@ Google LLC Pivotal Software, Inc. IBM Corp -Red Hat, Inc. \ No newline at end of file +Red Hat, Inc. +Cisco Systems, Inc. diff --git a/vendor/github.com/knative/serving/cmd/activator/kodata/LICENSE b/vendor/github.com/knative/serving/cmd/activator/kodata/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/vendor/github.com/knative/serving/cmd/activator/kodata/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/serving/cmd/activator/kodata/VENDOR-LICENSE b/vendor/github.com/knative/serving/cmd/activator/kodata/VENDOR-LICENSE new file mode 120000 index 00000000000..3cc89764519 --- /dev/null +++ b/vendor/github.com/knative/serving/cmd/activator/kodata/VENDOR-LICENSE @@ -0,0 +1 @@ +../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/serving/cmd/autoscaler/kodata/LICENSE b/vendor/github.com/knative/serving/cmd/autoscaler/kodata/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/vendor/github.com/knative/serving/cmd/autoscaler/kodata/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/serving/cmd/autoscaler/kodata/VENDOR-LICENSE b/vendor/github.com/knative/serving/cmd/autoscaler/kodata/VENDOR-LICENSE new file mode 120000 index 00000000000..3cc89764519 --- /dev/null +++ b/vendor/github.com/knative/serving/cmd/autoscaler/kodata/VENDOR-LICENSE @@ -0,0 +1 @@ +../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/serving/cmd/controller/kodata/LICENSE b/vendor/github.com/knative/serving/cmd/controller/kodata/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/vendor/github.com/knative/serving/cmd/controller/kodata/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/serving/cmd/controller/kodata/VENDOR-LICENSE b/vendor/github.com/knative/serving/cmd/controller/kodata/VENDOR-LICENSE new file mode 120000 index 00000000000..3cc89764519 --- /dev/null +++ b/vendor/github.com/knative/serving/cmd/controller/kodata/VENDOR-LICENSE @@ -0,0 +1 @@ +../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/serving/cmd/queue/kodata/LICENSE b/vendor/github.com/knative/serving/cmd/queue/kodata/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/vendor/github.com/knative/serving/cmd/queue/kodata/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/serving/cmd/queue/kodata/VENDOR-LICENSE b/vendor/github.com/knative/serving/cmd/queue/kodata/VENDOR-LICENSE new file mode 120000 index 00000000000..3cc89764519 --- /dev/null +++ b/vendor/github.com/knative/serving/cmd/queue/kodata/VENDOR-LICENSE @@ -0,0 +1 @@ +../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/serving/cmd/webhook/kodata/LICENSE b/vendor/github.com/knative/serving/cmd/webhook/kodata/LICENSE new file mode 120000 index 00000000000..5853aaea53b --- /dev/null +++ b/vendor/github.com/knative/serving/cmd/webhook/kodata/LICENSE @@ -0,0 +1 @@ +../../../LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/serving/cmd/webhook/kodata/VENDOR-LICENSE b/vendor/github.com/knative/serving/cmd/webhook/kodata/VENDOR-LICENSE new file mode 120000 index 00000000000..3cc89764519 --- /dev/null +++ b/vendor/github.com/knative/serving/cmd/webhook/kodata/VENDOR-LICENSE @@ -0,0 +1 @@ +../../../third_party/VENDOR-LICENSE \ No newline at end of file diff --git a/vendor/github.com/knative/serving/config/300-imagecache.yaml b/vendor/github.com/knative/serving/config/300-imagecache.yaml new file mode 120000 index 00000000000..f10d6dacf6a --- /dev/null +++ b/vendor/github.com/knative/serving/config/300-imagecache.yaml @@ -0,0 +1 @@ +../vendor/github.com/knative/caching/config/image.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/pkg/apis/autoscaling/register.go b/vendor/github.com/knative/serving/pkg/apis/autoscaling/register.go new file mode 100644 index 00000000000..38626e0b85b --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/autoscaling/register.go @@ -0,0 +1,36 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package autoscaling + +const ( + InternalGroupName = "autoscaling.internal.knative.dev" + + GroupName = "autoscaling.knative.dev" + + // ClassAnnotationKey is the annotation for the explicit class of autoscaler + // that a particular resource has opted into. For example, + // autoscaling.knative.dev/class: foo + // This uses a different domain because unlike the resource, it is user-facing. + ClassAnnotationKey = GroupName + "/class" + + MinScaleAnnotationKey = GroupName + "/minScale" + MaxScaleAnnotationKey = GroupName + "/maxScale" + + // KPALabelKey is the label key attached to a K8s Service to hint to the KPA + // which services/endpoints should trigger reconciles. + KPALabelKey = GroupName + "/kpa" +) diff --git a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/generated_expansion.go b/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/doc.go similarity index 85% rename from vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/generated_expansion.go rename to vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/doc.go index c746b14b13e..bcdf80570a8 100644 --- a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/generated_expansion.go +++ b/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/doc.go @@ -13,8 +13,7 @@ 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 v1alpha3 -type GatewayExpansion interface{} - -type VirtualServiceExpansion interface{} +// +k8s:deepcopy-gen=package +// +groupName=autoscaling.internal.knative.dev +package v1alpha1 diff --git a/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/kpa_defaults.go b/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/kpa_defaults.go new file mode 100644 index 00000000000..6d168478d89 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/kpa_defaults.go @@ -0,0 +1,33 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + servingv1alpha1 "github.com/knative/serving/pkg/apis/serving/v1alpha1" +) + +func (r *PodAutoscaler) SetDefaults() { + r.Spec.SetDefaults() +} + +func (rs *PodAutoscalerSpec) SetDefaults() { + // When ConcurrencyModel is specified but ContainerConcurrency + // is not (0), use the ConcurrencyModel value. + if rs.ConcurrencyModel == servingv1alpha1.RevisionRequestConcurrencyModelSingle && rs.ContainerConcurrency == 0 { + rs.ContainerConcurrency = 1 + } +} diff --git a/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/kpa_types.go b/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/kpa_types.go new file mode 100644 index 00000000000..c5e73d28dd8 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/kpa_types.go @@ -0,0 +1,191 @@ +/* +Copyright 2018 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "strconv" + "time" + + autoscalingv1 "k8s.io/api/autoscaling/v1" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/knative/pkg/apis" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "github.com/knative/serving/pkg/apis/autoscaling" + servingv1alpha1 "github.com/knative/serving/pkg/apis/serving/v1alpha1" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PodAutoscaler is a Knative abstraction that encapsulates the interface by which Knative +// components instantiate autoscalers. This definition is an abstraction that may be backed +// by multiple definitions. For more information, see the Knative Pluggability presentation: +// https://docs.google.com/presentation/d/10KWynvAJYuOEWy69VBa6bHJVCqIsz1TNdEKosNvcpPY/edit +type PodAutoscaler struct { + metav1.TypeMeta `json:",inline"` + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec holds the desired state of the PodAutoscaler (from the client). + // +optional + Spec PodAutoscalerSpec `json:"spec,omitempty"` + + // Status communicates the observed state of the PodAutoscaler (from the controller). + // +optional + Status PodAutoscalerStatus `json:"status,omitempty"` +} + +// Check that PodAutoscaler can be validated, can be defaulted, and has immutable fields. +var _ apis.Validatable = (*PodAutoscaler)(nil) +var _ apis.Defaultable = (*PodAutoscaler)(nil) +var _ apis.Immutable = (*PodAutoscaler)(nil) + +// Check that ConfigurationStatus may have its conditions managed. +var _ duckv1alpha1.ConditionsAccessor = (*PodAutoscalerStatus)(nil) + +// PodAutoscalerSpec holds the desired state of the PodAutoscaler (from the client). +type PodAutoscalerSpec struct { + // TODO: Generation does not work correctly with CRD. They are scrubbed + // by the APIserver (https://github.com/kubernetes/kubernetes/issues/58778) + // So, we add Generation here. Once that gets fixed, remove this and use + // ObjectMeta.Generation instead. + // +optional + Generation int64 `json:"generation,omitempty"` + + // ConcurrencyModel specifies the desired concurrency model + // (Single or Multi) for the scale target. Defaults to Multi. + // Deprecated in favor of ContainerConcurrency. + // +optional + ConcurrencyModel servingv1alpha1.RevisionRequestConcurrencyModelType `json:"concurrencyModel,omitempty"` + + // ContainerConcurrency specifies the maximum allowed + // in-flight (concurrent) requests per container of the Revision. + // Defaults to `0` which means unlimited concurrency. + // This field replaces ConcurrencyModel. A value of `1` + // is equivalent to `Single` and `0` is equivalent to `Multi`. + // +optional + ContainerConcurrency servingv1alpha1.RevisionContainerConcurrencyType `json:"containerConcurrency,omitempty"` + + // ScaleTargetRef defines the /scale-able resource that this PodAutoscaler + // is responsible for quickly right-sizing. + ScaleTargetRef autoscalingv1.CrossVersionObjectReference `json:"scaleTargetRef"` + + // ServiceName holds the name of a core Kubernetes Service resource that + // load balances over the pods referenced by the ScaleTargetRef. + ServiceName string `json:"serviceName"` +} + +const ( + // PodAutoscalerConditionReady is set when the revision is starting to materialize + // runtime resources, and becomes true when those resources are ready. + PodAutoscalerConditionReady = duckv1alpha1.ConditionReady + // PodAutoscalerConditionActive is set when the PodAutoscaler's ScaleTargetRef is receiving traffic. + PodAutoscalerConditionActive duckv1alpha1.ConditionType = "Active" +) + +var podCondSet = duckv1alpha1.NewLivingConditionSet(PodAutoscalerConditionActive) + +// PodAutoscalerStatus communicates the observed state of the PodAutoscaler (from the controller). +type PodAutoscalerStatus struct { + // Conditions communicates information about ongoing/complete + // reconciliation processes that bring the "spec" inline with the observed + // state of the world. + // +optional + Conditions duckv1alpha1.Conditions `json:"conditions,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// PodAutoscalerList is a list of PodAutoscaler resources +type PodAutoscalerList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata"` + + Items []PodAutoscaler `json:"items"` +} + +func (kpa *PodAutoscaler) scaleBoundInt32(key string) int32 { + if s, ok := kpa.Annotations[key]; ok { + // no error check: relying on validation + i, _ := strconv.ParseInt(s, 10, 32) + return int32(i) + } + return 0 +} + +// ScaleBounds returns scale bounds annotations values as a tuple: +// `(min, max int32)`. The value of 0 for any of min or max means the bound is +// not set +func (kpa *PodAutoscaler) ScaleBounds() (min, max int32) { + min = kpa.scaleBoundInt32(autoscaling.MinScaleAnnotationKey) + max = kpa.scaleBoundInt32(autoscaling.MaxScaleAnnotationKey) + return +} + +// IsReady looks at the conditions and if the Status has a condition +// PodAutoscalerConditionReady returns true if ConditionStatus is True +func (rs *PodAutoscalerStatus) IsReady() bool { + return podCondSet.Manage(rs).IsHappy() +} + +func (rs *PodAutoscalerStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { + return podCondSet.Manage(rs).GetCondition(t) +} + +func (rs *PodAutoscalerStatus) InitializeConditions() { + podCondSet.Manage(rs).InitializeConditions() +} + +func (rs *PodAutoscalerStatus) MarkActive() { + podCondSet.Manage(rs).MarkTrue(PodAutoscalerConditionActive) +} + +func (rs *PodAutoscalerStatus) MarkActivating(reason, message string) { + podCondSet.Manage(rs).MarkUnknown(PodAutoscalerConditionActive, reason, message) +} + +func (rs *PodAutoscalerStatus) MarkInactive(reason, message string) { + podCondSet.Manage(rs).MarkFalse(PodAutoscalerConditionActive, reason, message) +} + +// CanScaleToZero checks whether the pod autoscaler has been in an inactive state +// for at least the specified grace period. +func (rs *PodAutoscalerStatus) CanScaleToZero(gracePeriod time.Duration) bool { + if cond := rs.GetCondition(PodAutoscalerConditionActive); cond != nil { + switch cond.Status { + case corev1.ConditionFalse: + // Check that this PodAutoscaler has been inactive for + // at least the grace period. + return time.Now().After(cond.LastTransitionTime.Inner.Add(gracePeriod)) + } + } + return false +} + +// GetConditions returns the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (rs *PodAutoscalerStatus) GetConditions() duckv1alpha1.Conditions { + return rs.Conditions +} + +// SetConditions sets the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (rs *PodAutoscalerStatus) SetConditions(conditions duckv1alpha1.Conditions) { + rs.Conditions = conditions +} diff --git a/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/kpa_validation.go b/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/kpa_validation.go new file mode 100644 index 00000000000..97bd744955d --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/kpa_validation.go @@ -0,0 +1,79 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "github.com/google/go-cmp/cmp" + autoscalingv1 "k8s.io/api/autoscaling/v1" + "k8s.io/apimachinery/pkg/api/equality" + + "github.com/knative/pkg/apis" + servingv1alpha1 "github.com/knative/serving/pkg/apis/serving/v1alpha1" +) + +func (rt *PodAutoscaler) Validate() *apis.FieldError { + return servingv1alpha1.ValidateObjectMetadata(rt.GetObjectMeta()).ViaField("metadata").Also(rt.Spec.Validate().ViaField("spec")) +} + +func (rs *PodAutoscalerSpec) Validate() *apis.FieldError { + if equality.Semantic.DeepEqual(rs, &PodAutoscalerSpec{}) { + return apis.ErrMissingField(apis.CurrentField) + } + errs := validateReference(rs.ScaleTargetRef).ViaField("scaleTargetRef") + if rs.ServiceName == "" { + errs = errs.Also(apis.ErrMissingField("serviceName")) + } + if err := rs.ConcurrencyModel.Validate(); err != nil { + errs = errs.Also(err.ViaField("concurrencyModel")) + } else if err := servingv1alpha1.ValidateContainerConcurrency(rs.ContainerConcurrency, rs.ConcurrencyModel); err != nil { + errs = errs.Also(err) + } + return errs +} + +func validateReference(ref autoscalingv1.CrossVersionObjectReference) *apis.FieldError { + if equality.Semantic.DeepEqual(ref, autoscalingv1.CrossVersionObjectReference{}) { + return apis.ErrMissingField(apis.CurrentField) + } + var errs *apis.FieldError + if ref.Kind == "" { + errs = errs.Also(apis.ErrMissingField("kind")) + } + if ref.Name == "" { + errs = errs.Also(apis.ErrMissingField("name")) + } + if ref.APIVersion == "" { + errs = errs.Also(apis.ErrMissingField("apiVersion")) + } + return errs +} + +func (current *PodAutoscaler) CheckImmutableFields(og apis.Immutable) *apis.FieldError { + original, ok := og.(*PodAutoscaler) + if !ok { + return &apis.FieldError{Message: "The provided original was not a PodAutoscaler"} + } + + if diff := cmp.Diff(original.Spec, current.Spec); diff != "" { + return &apis.FieldError{ + Message: "Immutable fields changed (-old +new)", + Paths: []string{"spec"}, + Details: diff, + } + } + return nil +} diff --git a/vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/register.go b/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/register.go similarity index 84% rename from vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/register.go rename to vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/register.go index 2a70cb2340d..92d4fee7f36 100644 --- a/vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/register.go +++ b/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/register.go @@ -1,5 +1,5 @@ /* -Copyright 2018 The Knative Authors +Copyright 2018 The Knative Authors. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -14,17 +14,18 @@ See the License for the specific language governing permissions and limitations under the License. */ -package v1alpha3 +package v1alpha1 import ( - "github.com/knative/serving/pkg/apis/istio" + "github.com/knative/serving/pkg/apis/autoscaling" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" ) // SchemeGroupVersion is group version used to register these objects -var SchemeGroupVersion = schema.GroupVersion{Group: istio.GroupName, Version: "v1alpha3"} +var SchemeGroupVersion = schema.GroupVersion{Group: autoscaling.InternalGroupName, Version: "v1alpha1"} // Kind takes an unqualified kind and returns back a Group qualified GroupKind func Kind(kind string) schema.GroupKind { @@ -44,10 +45,8 @@ var ( // Adds the list of known types to Scheme. func addKnownTypes(scheme *runtime.Scheme) error { scheme.AddKnownTypes(SchemeGroupVersion, - &VirtualService{}, - &Gateway{}, - &VirtualServiceList{}, - &GatewayList{}, + &PodAutoscaler{}, + &PodAutoscalerList{}, ) metav1.AddToGroupVersion(scheme, SchemeGroupVersion) return nil diff --git a/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000000..466269da328 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/autoscaling/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,129 @@ +// +build !ignore_autogenerated + +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This file was autogenerated by deepcopy-gen. Do not edit it manually! + +package v1alpha1 + +import ( + duck_v1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodAutoscaler) DeepCopyInto(out *PodAutoscaler) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + out.Spec = in.Spec + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodAutoscaler. +func (in *PodAutoscaler) DeepCopy() *PodAutoscaler { + if in == nil { + return nil + } + out := new(PodAutoscaler) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PodAutoscaler) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } else { + return nil + } +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodAutoscalerList) DeepCopyInto(out *PodAutoscalerList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]PodAutoscaler, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodAutoscalerList. +func (in *PodAutoscalerList) DeepCopy() *PodAutoscalerList { + if in == nil { + return nil + } + out := new(PodAutoscalerList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *PodAutoscalerList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } else { + return nil + } +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodAutoscalerSpec) DeepCopyInto(out *PodAutoscalerSpec) { + *out = *in + out.ScaleTargetRef = in.ScaleTargetRef + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodAutoscalerSpec. +func (in *PodAutoscalerSpec) DeepCopy() *PodAutoscalerSpec { + if in == nil { + return nil + } + out := new(PodAutoscalerSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *PodAutoscalerStatus) DeepCopyInto(out *PodAutoscalerStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(duck_v1alpha1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PodAutoscalerStatus. +func (in *PodAutoscalerStatus) DeepCopy() *PodAutoscalerStatus { + if in == nil { + return nil + } + out := new(PodAutoscalerStatus) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/gateway_types.go b/vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/gateway_types.go deleted file mode 100644 index 0822a5e3b4e..00000000000 --- a/vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/gateway_types.go +++ /dev/null @@ -1,318 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha3 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// Gateway describes a load balancer operating at the edge of the mesh -// receiving incoming or outgoing HTTP/TCP connections. The specification -// describes a set of ports that should be exposed, the type of protocol to -// use, SNI configuration for the load balancer, etc. -// -// For example, the following gateway spec sets up a proxy to act as a load -// balancer exposing port 80 and 9080 (http), 443 (https), and port 2379 -// (TCP) for ingress. The gateway will be applied to the proxy running on -// a pod with labels "app: my-gateway-controller". While Istio will configure the -// proxy to listen on these ports, it is the responsibility of the user to -// ensure that external traffic to these ports are allowed into the mesh. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: Gateway -// metadata: -// name: my-gateway -// spec: -// selector: -// app: my-gatweway-controller -// servers: -// - port: -// number: 80 -// name: http -// protocol: HTTP -// hosts: -// - uk.bookinfo.com -// - eu.bookinfo.com -// tls: -// httpsRedirect: true # sends 302 redirect for http requests -// - port: -// number: 443 -// name: https -// protocol: HTTPS -// hosts: -// - uk.bookinfo.com -// - eu.bookinfo.com -// tls: -// mode: SIMPLE #enables HTTPS on this port -// serverCertificate: /etc/certs/servercert.pem -// privateKey: /etc/certs/privatekey.pem -// - port: -// number: 9080 -// name: http-wildcard -// protocol: HTTP -// # no hosts implies wildcard match -// - port: -// number: 2379 #to expose internal service via external port 2379 -// name: mongo -// protocol: MONGO -// -// The gateway specification above describes the L4-L6 properties of a load -// balancer. A VirtualService can then be bound to a gateway to control -// the forwarding of traffic arriving at a particular host or gateway port. -// -// For example, the following VirtualService splits traffic for -// https://uk.bookinfo.com/reviews, https://eu.bookinfo.com/reviews, -// http://uk.bookinfo.com:9080/reviews, http://eu.bookinfo.com:9080/reviews -// into two versions (prod and qa) of an internal reviews service on port -// 9080. In addition, requests containing the cookie user: dev-123 will be -// sent to special port 7777 in the qa version. The same rule is also -// applicable inside the mesh for requests to the reviews.prod -// service. This rule is applicable across ports 443, 9080. Note that -// http://uk.bookinfo.com gets redirected to https://uk.bookinfo.com -// (i.e. 80 redirects to 443). -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: bookinfo-rule -// spec: -// hosts: -// - reviews.prod -// - uk.bookinfo.com -// - eu.bookinfo.com -// gateways: -// - my-gateway -// - mesh # applies to all the sidecars in the mesh -// http: -// - match: -// - headers: -// cookie: -// user: dev-123 -// route: -// - destination: -// port: -// number: 7777 -// name: reviews.qa -// - match: -// uri: -// prefix: /reviews/ -// route: -// - destination: -// port: -// number: 9080 # can be omitted if its the only port for reviews -// name: reviews.prod -// weight: 80 -// - destination: -// name: reviews.qa -// weight: 20 -// -// The following VirtualService forwards traffic arriving at (external) port -// 2379 from 172.17.16.0/24 subnet to internal Mongo server on port 5555. This -// rule is not applicable internally in the mesh as the gateway list omits -// the reserved name "mesh". -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: bookinfo-Mongo -// spec: -// hosts: -// - mongosvr #name of Mongo service -// gateways: -// - my-gateway -// tcp: -// - match: -// - port: -// number: 2379 -// sourceSubnet: "172.17.16.0/24" -// route: -// - destination: -// name: mongo.prod -// -type Gateway struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec GatewaySpec `json:"spec"` -} - -type GatewaySpec struct { - // REQUIRED: A list of server specifications. - Servers []Server `json:"servers"` - - // One or more labels that indicate a specific set of pods/VMs - // on which this gateway configuration should be applied. - // If no selectors are provided, the gateway will be implemented by - // the default istio-ingress controller. - Selector map[string]string `json:"selector,omitempty"` -} - -// Server describes the properties of the proxy on a given load balancer port. -// For example, -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: Gateway -// metadata: -// name: my-ingress -// spec: -// selector: -// app: my-ingress-controller -// servers: -// - port: -// number: 80 -// name: http2 -// protocol: HTTP2 -// -// Another example -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: Gateway -// metadata: -// name: my-tcp-ingress -// spec: -// selector: -// app: my-tcp-ingress-controller -// servers: -// - port: -// number: 27018 -// name: mongo -// protocol: MONGO -// -// The following is an example of TLS configuration for port 443 -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: Gateway -// metadata: -// name: my-tls-ingress -// spec: -// selector: -// app: my-tls-ingress-controller -// servers: -// - port: -// number: 443 -// name: https -// protocol: HTTPS -// tls: -// mode: SIMPLE -// serverCertificate: /etc/certs/server.pem -// privateKey: /etc/certs/privatekey.pem -// -type Server struct { - // REQUIRED: The Port on which the proxy should listen for incoming - // connections - Port Port `json:"port"` - - // A list of hosts exposed by this gateway. While - // typically applicable to HTTP services, it can also be used for TCP - // services using TLS with SNI. Standard DNS wildcard prefix syntax - // is permitted. - // - // A VirtualService that is bound to a gateway must having a matching host - // in its default destination. Specifically one of the VirtualService - // destination hosts is a strict suffix of a gateway host or - // a gateway host is a suffix of one of the VirtualService hosts. - Hosts []string `json:"hosts,omitempty"` - - // Set of TLS related options that govern the server's behavior. Use - // these options to control if all http requests should be redirected to - // https, and the TLS modes to use. - TLS *TLSOptions `json:"tls,omitempty"` -} - -type TLSOptions struct { - // If set to true, the load balancer will send a 302 redirect for all - // http connections, asking the clients to use HTTPS. - HttpsRedirect bool `json:"httpsRedirect"` - - // Optional: Indicates whether connections to this port should be - // secured using TLS. The value of this field determines how TLS is - // enforced. - Mode TLSMode `json:"mode,omitempty"` - - // REQUIRED if mode is "SIMPLE" or "MUTUAL". The path to the file - // holding the server-side TLS certificate to use. - ServerCertificate string `json:"serverCertificate"` - - // REQUIRED if mode is "SIMPLE" or "MUTUAL". The path to the file - // holding the server's private key. - PrivateKey string `json:"privateKey"` - - // REQUIRED if mode is "MUTUAL". The path to a file containing - // certificate authority certificates to use in verifying a presented - // client side certificate. - CaCertificates string `json:"caCertificates"` - - // A list of alternate names to verify the subject identity in the - // certificate presented by the client. - SubjectAltNames []string `json:"subjectAltNames"` -} - -// TLS modes enforced by the proxy -type TLSMode string - -const ( - // If set to "PASSTHROUGH", the proxy will forward the connection - // to the upstream server selected based on the SNI string presented - // by the client. - TLSModePassThrough TLSMode = "PASSTHROUGH" - - // If set to "SIMPLE", the proxy will secure connections with - // standard TLS semantics. - TLSModeSimple TLSMode = "SIMPLE" - - // If set to "MUTUAL", the proxy will secure connections to the - // upstream using mutual TLS by presenting client certificates for - // authentication. - TLSModeMutual TLSMode = "MUTUAL" -) - -// Port describes the properties of a specific port of a service. -type Port struct { - // REQUIRED: A valid non-negative integer port number. - Number int `json:"number"` - - // REQUIRED: The protocol exposed on the port. - // MUST BE one of HTTP|HTTPS|GRPC|HTTP2|MONGO|TCP. - Protocol PortProtocol `json:"protocol"` - - // Label assigned to the port. - Name string `json:"name,omitempty"` -} - -type PortProtocol string - -const ( - ProtocolHTTP PortProtocol = "HTTP" - ProtocolHTTPS PortProtocol = "HTTPS" - ProtocolGRPC PortProtocol = "GRPC" - ProtocolHTTP2 PortProtocol = "HTTP2" - ProtocolMongo PortProtocol = "Mongo" - ProtocolTCP PortProtocol = "TCP" -) - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// GatewayList is a list of Gateway resources -type GatewayList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []Gateway `json:"items"` -} diff --git a/vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/virtualservice_types.go b/vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/virtualservice_types.go deleted file mode 100644 index 87e2f9815ef..00000000000 --- a/vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/virtualservice_types.go +++ /dev/null @@ -1,783 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha3 - -import ( - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" -) - -// +genclient -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// VirtualService -type VirtualService struct { - metav1.TypeMeta `json:",inline"` - metav1.ObjectMeta `json:"metadata,omitempty"` - - Spec VirtualServiceSpec `json:"spec"` -} - -// A VirtualService defines a set of traffic routing rules to apply when a host is -// addressed. Each routing rule defines matching criteria for traffic of a specific -// protocol. If the traffic is matched, then it is sent to a named destination service -// (or subset/version of it) defined in the registry. -// -// The source of traffic can also be matched in a routing rule. This allows routing -// to be customized for specific client contexts. -// -// The following example routes all HTTP traffic by default to -// pods of the reviews service with label "version: v1". In addition, -// HTTP requests containing /wpcatalog/, /consumercatalog/ url prefixes will -// be rewritten to /newcatalog and sent to pods with label "version: v2". The -// rules will be applied at the gateway named "bookinfo" as well as at all -// the sidecars in the mesh (indicated by the reserved gateway name -// "mesh"). -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: reviews-route -// spec: -// hosts: -// - reviews -// gateways: # if omitted, defaults to "mesh" -// - bookinfo -// - mesh -// http: -// - match: -// - uri: -// prefix: "/wpcatalog" -// - uri: -// prefix: "/consumercatalog" -// rewrite: -// uri: "/newcatalog" -// route: -// - destination: -// host: reviews -// subset: v2 -// - route: -// - destination: -// host: reviews -// subset: v1 -// -// A subset/version of a route destination is identified with a reference -// to a named service subset which must be declared in a corresponding -// DestinationRule. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: DestinationRule -// metadata: -// name: reviews-destination -// spec: -// host: reviews -// subsets: -// - name: v1 -// labels: -// version: v1 -// - name: v2 -// labels: -// version: v2 -// -// A host name can be defined by only one VirtualService. A single -// VirtualService can be used to describe traffic properties for multiple -// HTTP and TCP ports. -type VirtualServiceSpec struct { - // REQUIRED. The destination address for traffic captured by this virtual - // service. Could be a DNS name with wildcard prefix or a CIDR - // prefix. Depending on the platform, short-names can also be used - // instead of a FQDN (i.e. has no dots in the name). In such a scenario, - // the FQDN of the host would be derived based on the underlying - // platform. - // - // For example on Kubernetes, when hosts contains a short name, Istio will - // interpret the short name based on the namespace of the rule. Thus, when a - // client namespace applies a rule in the "default" namespace containing a name - // "reviews, Istio will setup routes to the "reviews.default.svc.cluster.local" - // service. However, if a different name such as "reviews.sales.svc.cluster.local" - // is used, it would be treated as a FQDN during virtual host matching. - // In Consul, a plain service name would be resolved to the FQDN - // "reviews.service.consul". - // - // Note that the hosts field applies to both HTTP and TCP - // services. Service inside the mesh, i.e., those found in the service - // registry, must always be referred to using their alphanumeric - // names. IP addresses or CIDR prefixes are allowed only for services - // defined via the Gateway. - Hosts []string `json:"hosts"` - - // The names of gateways and sidecars that should apply these routes. A - // single VirtualService is used for sidecars inside the mesh as well - // as for one or more gateways. The selection condition imposed by this field - // can be overridden using the source field in the match conditions of HTTP/TCP - // routes. The reserved word "mesh" is used to imply all the sidecars in - // the mesh. When this field is omitted, the default gateway ("mesh") - // will be used, which would apply the rule to all sidecars in the - // mesh. If a list of gateway names is provided, the rules will apply - // only to the gateways. To apply the rules to both gateways and sidecars, - // specify "mesh" as one of the gateway names. - Gateways []string `json:"gateways,omitempty"` - - // An ordered list of route rules for HTTP traffic. - // The first rule matching an incoming request is used. - Http []HTTPRoute `json:"http,omitempty"` - - // An ordered list of route rules for TCP traffic. - // The first rule matching an incoming request is used. - Tcp []TCPRoute `json:"tcp,omitempty"` -} - -// Describes match conditions and actions for routing HTTP/1.1, HTTP2, and -// gRPC traffic. See VirtualService for usage examples. -type HTTPRoute struct { - // Match conditions to be satisfied for the rule to be - // activated. All conditions inside a single match block have AND - // semantics, while the list of match blocks have OR semantics. The rule - // is matched if any one of the match blocks succeed. - Match []HTTPMatchRequest `json:"match,omitempty"` - - // A http rule can either redirect or forward (default) traffic. The - // forwarding target can be one of several versions of a service (see - // glossary in beginning of document). Weights associated with the - // service version determine the proportion of traffic it receives. - Route []DestinationWeight `json:"route,omitempty"` - - // A http rule can either redirect or forward (default) traffic. If - // traffic passthrough option is specified in the rule, - // route/redirect will be ignored. The redirect primitive can be used to - // send a HTTP 302 redirect to a different URI or Authority. - Redirect *HTTPRedirect `json:"redirect,omitempty"` - - // Rewrite HTTP URIs and Authority headers. Rewrite cannot be used with - // Redirect primitive. Rewrite will be performed before forwarding. - Rewrite *HTTPRewrite `json:"rewrite,omitempty"` - - // Indicates that a HTTP/1.1 client connection to this particular route - // should be allowed (and expected) to upgrade to a WebSocket connection. - // The default is false. Istio's reference sidecar implementation (Envoy) - // expects the first request to this route to contain the WebSocket - // upgrade headers. Otherwise, the request will be rejected. Note that - // Websocket allows secondary protocol negotiation which may then be - // subject to further routing rules based on the protocol selected. - WebsocketUpgrade bool `json:"websocketUpgrade,omitempty"` - - // Timeout for HTTP requests. - Timeout string `json:"timeout,omitempty"` - - // Retry policy for HTTP requests. - Retries *HTTPRetry `json:"retries,omitempty"` - - // Fault injection policy to apply on HTTP traffic. - Fault *HTTPFaultInjection `json:"fault,omitempty"` - - // Mirror HTTP traffic to a another destination in addition to forwarding - // the requests to the intended destination. Mirrored traffic is on a - // best effort basis where the sidecar/gateway will not wait for the - // mirrored cluster to respond before returning the response from the - // original destination. Statistics will be generated for the mirrored - // destination. - Mirror *Destination `json:"mirror,omitempty"` - - // Additional HTTP headers to add before forwarding a request to the - // destination service. - AppendHeaders map[string]string `json:"appendHeaders,omitempty"` - - // Http headers to remove before returning the response to the caller - RemoveResponseHeaders map[string]string `json:"removeResponseHeaders,omitempty"` -} - -// HttpMatchRequest specifies a set of criterion to be met in order for the -// rule to be applied to the HTTP request. For example, the following -// restricts the rule to match only requests where the URL path -// starts with /ratings/v2/ and the request contains a "cookie" with value -// "user=jason". -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: ratings-route -// spec: -// hosts: -// - ratings -// http: -// - match: -// - headers: -// cookie: -// regex: "^(.*?;)?(user=jason)(;.*)?" -// uri: -// prefix: "/ratings/v2/" -// route: -// - destination: -// host: ratings -// -// HTTPMatchRequest CANNOT be empty. -type HTTPMatchRequest struct { - // URI to match - // values are case-sensitive and formatted as follows: - // - // - `exact: "value"` for exact string match - // - // - `prefix: "value"` for prefix-based match - // - // - `regex: "value"` for ECMAscript style regex-based match - // - Uri *StringMatch `json:"uri,omitempty"` - - // URI Scheme - // values are case-sensitive and formatted as follows: - // - // - `exact: "value"` for exact string match - // - // - `prefix: "value"` for prefix-based match - // - // - `regex: "value"` for ECMAscript style regex-based match - // - Scheme *StringMatch `json:"scheme,omitempty"` - - // HTTP Method - // values are case-sensitive and formatted as follows: - // - // - `exact: "value"` for exact string match - // - // - `prefix: "value"` for prefix-based match - // - // - `regex: "value"` for ECMAscript style regex-based match - // - Method *StringMatch `json:"method,omitempty"` - - // HTTP Authority - // values are case-sensitive and formatted as follows: - // - // - `exact: "value"` for exact string match - // - // - `prefix: "value"` for prefix-based match - // - // - `regex: "value"` for ECMAscript style regex-based match - // - Authority *StringMatch `json:"authority,omitempty"` - - // The header keys must be lowercase and use hyphen as the separator, - // e.g. _x-request-id_. - // - // Header values are case-sensitive and formatted as follows: - // - // - `exact: "value"` for exact string match - // - // - `prefix: "value"` for prefix-based match - // - // - `regex: "value"` for ECMAscript style regex-based match - // - // **Note:** The keys `uri`, `scheme`, `method`, and `authority` will be ignored. - Headers map[string]StringMatch `json:"headers,omitempty"` -} - -// Describes how to match a given string in HTTP headers. Match is -// case-sensitive. -type StringMatch struct { - // Specified exactly one of the fields below. - - // exact string match - Exact string `json:"exact,omitempty"` - - // prefix-based match - Prefix string `json:"prefix,omitempty"` - - // ECMAscript style regex-based match - Regex string `json:"regex,omitempty"` -} - -type DestinationWeight struct { - // REQUIRED. Destination uniquely identifies the instances of a service - // to which the request/connection should be forwarded to. - Destination Destination `json:"destination"` - - // REQUIRED. The proportion of traffic to be forwarded to the service - // version. (0-100). Sum of weights across destinations SHOULD BE == 100. - // If there is only destination in a rule, the weight value is assumed to - // be 100. - Weight int `json:"weight"` -} - -// Destination indicates the network addressable service to which the -// request/connection will be sent after processing a routing rule. The -// destination.name should unambiguously refer to a service in the service -// registry. It can be a short name or a fully qualified domain name from -// the service registry, a resolvable DNS name, an IP address or a service -// name from the service registry and a subset name. The order of inference -// is as follows: -// -// 1. Service registry lookup. The entire name is looked up in the service -// registry. If the lookup succeeds, the search terminates. The requests -// will be routed to any instance of the service in the mesh. When the -// service name consists of a single word, the FQDN will be constructed in -// a platform specific manner. For example, in Kubernetes, the namespace -// associated with the routing rule will be used to identify the service as -// .. However, if the service name contains -// multiple words separated by a dot (e.g., reviews.prod), the name in its -// entirety would be looked up in the service registry. -// -// 2. Runtime DNS lookup by the proxy. If step 1 fails, and the name is not -// an IP address, it will be considered as a DNS name that is not in the -// service registry (e.g., wikipedia.org). The sidecar/gateway will resolve -// the DNS and load balance requests appropriately. See Envoy's strict_dns -// for details. -// -// The following example routes all traffic by default to pods of the -// reviews service with label "version: v1" (i.e., subset v1), and some -// to subset v2, in a kubernetes environment. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: reviews-route -// spec: -// hosts: -// - reviews # namespace is same as the client/caller's namespace -// http: -// - match: -// - uri: -// prefix: "/wpcatalog" -// - uri: -// prefix: "/consumercatalog" -// rewrite: -// uri: "/newcatalog" -// route: -// - destination: -// host: reviews -// subset: v2 -// - route: -// - destination: -// host: reviews -// subset: v1 -// -// And the associated DestinationRule -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: DestinationRule -// metadata: -// name: reviews-destination -// spec: -// host: reviews -// subsets: -// - name: v1 -// labels: -// version: v1 -// - name: v2 -// labels: -// version: v2 -// -// The following VirtualService sets a timeout of 5s for all calls to -// productpage.prod service. Notice that there are no subsets defined in -// this rule. Istio will fetch all instances of productpage.prod service -// from the service registry and populate the sidecar's load balancing -// pool. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: my-productpage-rule -// spec: -// hosts: -// - productpage.prod # in kubernetes, this applies only to prod namespace -// http: -// - timeout: 5s -// route: -// - destination: -// host: productpage.prod -// -// The following sets a timeout of 5s for all calls to the external -// service wikipedia.org, as there is no internal service of that name. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: my-wiki-rule -// spec: -// hosts: -// - wikipedia.org -// http: -// - timeout: 5s -// route: -// - destination: -// host: wikipedia.org -// -type Destination struct { - // REQUIRED. The name of a service from the service registry. Service - // names are looked up from the platform's service registry (e.g., - // Kubernetes services, Consul services, etc.) and from the hosts - // declared by [ServiceEntry](#ServiceEntry). Traffic forwarded to - // destinations that are not found in either of the two, will be dropped. - // - // *Note for Kubernetes users*: When short names are used (e.g. "reviews" - // instead of "reviews.default.svc.cluster.local"), Istio will interpret - // the short name based on the namespace of the rule, not the service. A - // rule in the "default" namespace containing a host "reviews will be - // interpreted as "reviews.default.svc.cluster.local", irrespective of - // the actual namespace associated with the reviews service. _To avoid - // potential misconfigurations, it is recommended to always use fully - // qualified domain names over short names._ - Host string `json:"host"` - - // The name of a subset within the service. Applicable only to services - // within the mesh. The subset must be defined in a corresponding - // DestinationRule. - Subset string `json:"subset,omitempty"` - - // Specifies the port on the host that is being addressed. If a service - // exposes only a single port it is not required to explicitly select the - // port. - Port PortSelector `json:"port,omitempty"` -} - -// PortSelector specifies the number of a port to be used for -// matching or selection for final routing. -type PortSelector struct { - // Choose one of the fields below. - - // Valid port number - Number uint32 `json:"number,omitempty"` - - // Valid port name - Name string `json:"name,omitempty"` -} - -// Describes match conditions and actions for routing TCP traffic. The -// following routing rule forwards traffic arriving at port 27017 for -// mongo.prod.svc.cluster.local from 172.17.16.* subnet to another Mongo -// server on port 5555. -// -// ```yaml -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: bookinfo-Mongo -// spec: -// hosts: -// - mongo.prod.svc.cluster.local -// tcp: -// - match: -// - port: 27017 -// sourceSubnet: "172.17.16.0/24" -// route: -// - destination: -// host: mongo.backup.svc.cluster.local -// port: -// number: 5555 -// ``` -type TCPRoute struct { - // Match conditions to be satisfied for the rule to be - // activated. All conditions inside a single match block have AND - // semantics, while the list of match blocks have OR semantics. The rule - // is matched if any one of the match blocks succeed. - Match []L4MatchAttributes `json:"match"` - - // The destination to which the connection should be forwarded to. - // Currently, only one destination is allowed for TCP services. When TCP - // weighted routing support is introduced in Envoy, multiple destinations - // with weights can be specified. - Route DestinationWeight `json:"route"` -} - -// L4 connection match attributes. Note that L4 connection matching support -// is incomplete. -type L4MatchAttributes struct { - // IPv4 or IPv6 ip address of destination with optional subnet. E.g., - // a.b.c.d/xx form or just a.b.c.d. This is only valid when the - // destination service has several IPs and the application explicitly - // specifies a particular IP. - DestinationSubnet string `json:"destinationSubnet,omitempty"` - - // Specifies the port on the host that is being addressed. Many services - // only expose a single port or label ports with the protocols they support, - // in these cases it is not required to explicitly select the port. - Port int `json:"port,omitempty"` - - // IPv4 or IPv6 ip address of source with optional subnet. E.g., a.b.c.d/xx - // form or just a.b.c.d - SourceSubnet string `json:"sourceSubnet,omitempty"` - - // One or more labels that constrain the applicability of a rule to - // workloads with the given labels. If the VirtualService has a list of - // gateways specified at the top, it should include the reserved gateway - // `mesh` in order for this field to be applicable. - SourceLabel map[string]string `json:"sourceLabel,omitempty"` - - // Names of gateways where the rule should be applied to. Gateway names - // at the top of the VirtualService (if any) are overridden. The gateway match is - // independent of sourceLabels. - Gateways []string `json:"gateways,omitempty"` -} - -// HTTPRedirect can be used to send a 302 redirect response to the caller, -// where the Authority/Host and the URI in the response can be swapped with -// the specified values. For example, the following rule redirects -// requests for /v1/getProductRatings API on the ratings service to -// /v1/bookRatings provided by the bookratings service. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: ratings-route -// spec: -// hosts: -// - ratings -// http: -// - match: -// - uri: -// exact: /v1/getProductRatings -// redirect: -// uri: /v1/bookRatings -// authority: bookratings.default.svc.cluster.local -// ... -// -type HTTPRedirect struct { - // On a redirect, overwrite the Path portion of the URL with this - // value. Note that the entire path will be replaced, irrespective of the - // request URI being matched as an exact path or prefix. - Uri string `json:"uri,omitempty"` - - // On a redirect, overwrite the Authority/Host portion of the URL with - // this value. - Authority string `json:"authority,omitempty"` -} - -// HTTPRewrite can be used to rewrite specific parts of a HTTP request -// before forwarding the request to the destination. Rewrite primitive can -// be used only with the DestinationWeights. The following example -// demonstrates how to rewrite the URL prefix for api call (/ratings) to -// ratings service before making the actual API call. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: ratings-route -// spec: -// hosts: -// - ratings -// http: -// - match: -// - uri: -// prefix: /ratings -// rewrite: -// uri: /v1/bookRatings -// route: -// - destination: -// host: ratings -// subset: v1 -// -type HTTPRewrite struct { - // rewrite the path (or the prefix) portion of the URI with this - // value. If the original URI was matched based on prefix, the value - // provided in this field will replace the corresponding matched prefix. - Uri string `json:"uri,omitempty"` - - // rewrite the Authority/Host header with this value. - Authority string `json:"authority,omitempty"` -} - -// Describes the retry policy to use when a HTTP request fails. For -// example, the following rule sets the maximum number of retries to 3 when -// calling ratings:v1 service, with a 2s timeout per retry attempt. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: ratings-route -// spec: -// hosts: -// - ratings -// http: -// - route: -// - destination: -// host: ratings -// subset: v1 -// retries: -// attempts: 3 -// perTryTimeout: 2s -// -type HTTPRetry struct { - // REQUIRED. Number of retries for a given request. The interval - // between retries will be determined automatically (25ms+). Actual - // number of retries attempted depends on the httpReqTimeout. - Attempts int `json:"attempts"` - - // Timeout per retry attempt for a given request. format: 1h/1m/1s/1ms. MUST BE >=1ms. - PerTryTimeout string `json:"perTryTimeout"` -} - -// Describes the Cross-Origin Resource Sharing (CORS) policy, for a given -// service. Refer to -// https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS -// for further details about cross origin resource sharing. For example, -// the following rule restricts cross origin requests to those originating -// from example.com domain using HTTP POST/GET, and sets the -// Access-Control-Allow-Credentials header to false. In addition, it only -// exposes X-Foo-bar header and sets an expiry period of 1 day. -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: ratings-route -// spec: -// hosts: -// - ratings -// http: -// - route: -// - destination: -// host: ratings -// subset: v1 -// corsPolicy: -// allowOrigin: -// - example.com -// allowMethods: -// - POST -// - GET -// allowCredentials: false -// allowHeaders: -// - X-Foo-Bar -// maxAge: "1d" -// -type CorsPolicy struct { - // The list of origins that are allowed to perform CORS requests. The - // content will be serialized into the Access-Control-Allow-Origin - // header. Wildcard * will allow all origins. - AllowOrigin []string `json:"allowOrigin,omitempty"` - - // List of HTTP methods allowed to access the resource. The content will - // be serialized into the Access-Control-Allow-Methods header. - AllowMethods []string `json:"allowMethods,omitempty"` - - // List of HTTP headers that can be used when requesting the - // resource. Serialized to Access-Control-Allow-Methods header. - AllowHeaders []string `json:"allowHeaders,omitempty"` - - // A white list of HTTP headers that the browsers are allowed to - // access. Serialized into Access-Control-Expose-Headers header. - ExposeHeaders []string `json:"exposeHeaders,omitempty"` - - // Specifies how long the the results of a preflight request can be - // cached. Translates to the Access-Control-Max-Age header. - MaxAge string `json:"maxAge,omitempty"` - - // Indicates whether the caller is allowed to send the actual request - // (not the preflight) using credentials. Translates to - // Access-Control-Allow-Credentials header. - AllowCredentials bool `json:"allowCredentials,omitempty"` -} - -// HTTPFaultInjection can be used to specify one or more faults to inject -// while forwarding http requests to the destination specified in a route. -// Fault specification is part of a VirtualService rule. Faults include -// aborting the Http request from downstream service, and/or delaying -// proxying of requests. A fault rule MUST HAVE delay or abort or both. -// -// *Note:* Delay and abort faults are independent of one another, even if -// both are specified simultaneously. -type HTTPFaultInjection struct { - // Delay requests before forwarding, emulating various failures such as - // network issues, overloaded upstream service, etc. - Delay *InjectDelay `json:"delay,omitempty"` - - // Abort Http request attempts and return error codes back to downstream - // service, giving the impression that the upstream service is faulty. - Abort *InjectAbort `json:"abort,omitempty"` -} - -// Delay specification is used to inject latency into the request -// forwarding path. The following example will introduce a 5 second delay -// in 10% of the requests to the "v1" version of the "reviews" -// service from all pods with label env: prod -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: reviews-route -// spec: -// hosts: -// - reviews -// http: -// - match: -// - sourceLabels: -// env: prod -// route: -// - destination: -// host: reviews -// subset: v1 -// fault: -// delay: -// percent: 10 -// fixedDelay: 5s -// -// The _fixedDelay_ field is used to indicate the amount of delay in -// seconds. An optional _percent_ field, a value between 0 and 100, can -// be used to only delay a certain percentage of requests. If left -// unspecified, all request will be delayed. -type InjectDelay struct { - // Percentage of requests on which the delay will be injected (0-100). - Percent int `json:"percent,omitempty"` - - // REQUIRED. Add a fixed delay before forwarding the request. Format: - // 1h/1m/1s/1ms. MUST be >=1ms. - FixedDelay string `json:"fixedDelay"` - - // (-- Add a delay (based on an exponential function) before forwarding - // the request. mean delay needed to derive the exponential delay - // values --) - ExponentialDelay string `json:"exponentialDelay,omitempty"` -} - -// Abort specification is used to prematurely abort a request with a -// pre-specified error code. The following example will return an HTTP -// 400 error code for 10% of the requests to the "ratings" service "v1". -// -// apiVersion: networking.istio.io/v1alpha3 -// kind: VirtualService -// metadata: -// name: ratings-route -// spec: -// hosts: -// - ratings -// http: -// - route: -// - destination: -// host: ratings -// subset: v1 -// fault: -// abort: -// percent: 10 -// httpStatus: 400 -// -// The _httpStatus_ field is used to indicate the HTTP status code to -// return to the caller. The optional _percent_ field, a value between 0 -// and 100, is used to only abort a certain percentage of requests. If -// not specified, all requests are aborted. -type InjectAbort struct { - // Percentage of requests to be aborted with the error code provided (0-100). - Perecent int `json:"percent,omitempty"` - - // REQUIRED. HTTP status code to use to abort the Http request. - HttpStatus int `json:"httpStatus"` -} - -// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object - -// VirtualServiceList is a list of VirtualService resources -type VirtualServiceList struct { - metav1.TypeMeta `json:",inline"` - metav1.ListMeta `json:"metadata"` - - Items []VirtualService `json:"items"` -} diff --git a/vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/zz_generated.deepcopy.go b/vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/zz_generated.deepcopy.go deleted file mode 100644 index 5857c8585ae..00000000000 --- a/vendor/github.com/knative/serving/pkg/apis/istio/v1alpha3/zz_generated.deepcopy.go +++ /dev/null @@ -1,701 +0,0 @@ -// +build !ignore_autogenerated - -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -// This file was autogenerated by deepcopy-gen. Do not edit it manually! - -package v1alpha3 - -import ( - runtime "k8s.io/apimachinery/pkg/runtime" -) - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *CorsPolicy) DeepCopyInto(out *CorsPolicy) { - *out = *in - if in.AllowOrigin != nil { - in, out := &in.AllowOrigin, &out.AllowOrigin - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.AllowMethods != nil { - in, out := &in.AllowMethods, &out.AllowMethods - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.AllowHeaders != nil { - in, out := &in.AllowHeaders, &out.AllowHeaders - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.ExposeHeaders != nil { - in, out := &in.ExposeHeaders, &out.ExposeHeaders - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CorsPolicy. -func (in *CorsPolicy) DeepCopy() *CorsPolicy { - if in == nil { - return nil - } - out := new(CorsPolicy) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Destination) DeepCopyInto(out *Destination) { - *out = *in - out.Port = in.Port - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Destination. -func (in *Destination) DeepCopy() *Destination { - if in == nil { - return nil - } - out := new(Destination) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *DestinationWeight) DeepCopyInto(out *DestinationWeight) { - *out = *in - out.Destination = in.Destination - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DestinationWeight. -func (in *DestinationWeight) DeepCopy() *DestinationWeight { - if in == nil { - return nil - } - out := new(DestinationWeight) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Gateway) DeepCopyInto(out *Gateway) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Gateway. -func (in *Gateway) DeepCopy() *Gateway { - if in == nil { - return nil - } - out := new(Gateway) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *Gateway) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } else { - return nil - } -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GatewayList) DeepCopyInto(out *GatewayList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]Gateway, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewayList. -func (in *GatewayList) DeepCopy() *GatewayList { - if in == nil { - return nil - } - out := new(GatewayList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *GatewayList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } else { - return nil - } -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *GatewaySpec) DeepCopyInto(out *GatewaySpec) { - *out = *in - if in.Servers != nil { - in, out := &in.Servers, &out.Servers - *out = make([]Server, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Selector != nil { - in, out := &in.Selector, &out.Selector - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GatewaySpec. -func (in *GatewaySpec) DeepCopy() *GatewaySpec { - if in == nil { - return nil - } - out := new(GatewaySpec) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPFaultInjection) DeepCopyInto(out *HTTPFaultInjection) { - *out = *in - if in.Delay != nil { - in, out := &in.Delay, &out.Delay - if *in == nil { - *out = nil - } else { - *out = new(InjectDelay) - **out = **in - } - } - if in.Abort != nil { - in, out := &in.Abort, &out.Abort - if *in == nil { - *out = nil - } else { - *out = new(InjectAbort) - **out = **in - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPFaultInjection. -func (in *HTTPFaultInjection) DeepCopy() *HTTPFaultInjection { - if in == nil { - return nil - } - out := new(HTTPFaultInjection) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPMatchRequest) DeepCopyInto(out *HTTPMatchRequest) { - *out = *in - if in.Uri != nil { - in, out := &in.Uri, &out.Uri - if *in == nil { - *out = nil - } else { - *out = new(StringMatch) - **out = **in - } - } - if in.Scheme != nil { - in, out := &in.Scheme, &out.Scheme - if *in == nil { - *out = nil - } else { - *out = new(StringMatch) - **out = **in - } - } - if in.Method != nil { - in, out := &in.Method, &out.Method - if *in == nil { - *out = nil - } else { - *out = new(StringMatch) - **out = **in - } - } - if in.Authority != nil { - in, out := &in.Authority, &out.Authority - if *in == nil { - *out = nil - } else { - *out = new(StringMatch) - **out = **in - } - } - if in.Headers != nil { - in, out := &in.Headers, &out.Headers - *out = make(map[string]StringMatch, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPMatchRequest. -func (in *HTTPMatchRequest) DeepCopy() *HTTPMatchRequest { - if in == nil { - return nil - } - out := new(HTTPMatchRequest) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPRedirect) DeepCopyInto(out *HTTPRedirect) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRedirect. -func (in *HTTPRedirect) DeepCopy() *HTTPRedirect { - if in == nil { - return nil - } - out := new(HTTPRedirect) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPRetry) DeepCopyInto(out *HTTPRetry) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRetry. -func (in *HTTPRetry) DeepCopy() *HTTPRetry { - if in == nil { - return nil - } - out := new(HTTPRetry) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPRewrite) DeepCopyInto(out *HTTPRewrite) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRewrite. -func (in *HTTPRewrite) DeepCopy() *HTTPRewrite { - if in == nil { - return nil - } - out := new(HTTPRewrite) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *HTTPRoute) DeepCopyInto(out *HTTPRoute) { - *out = *in - if in.Match != nil { - in, out := &in.Match, &out.Match - *out = make([]HTTPMatchRequest, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Route != nil { - in, out := &in.Route, &out.Route - *out = make([]DestinationWeight, len(*in)) - copy(*out, *in) - } - if in.Redirect != nil { - in, out := &in.Redirect, &out.Redirect - if *in == nil { - *out = nil - } else { - *out = new(HTTPRedirect) - **out = **in - } - } - if in.Rewrite != nil { - in, out := &in.Rewrite, &out.Rewrite - if *in == nil { - *out = nil - } else { - *out = new(HTTPRewrite) - **out = **in - } - } - if in.Retries != nil { - in, out := &in.Retries, &out.Retries - if *in == nil { - *out = nil - } else { - *out = new(HTTPRetry) - **out = **in - } - } - if in.Fault != nil { - in, out := &in.Fault, &out.Fault - if *in == nil { - *out = nil - } else { - *out = new(HTTPFaultInjection) - (*in).DeepCopyInto(*out) - } - } - if in.Mirror != nil { - in, out := &in.Mirror, &out.Mirror - if *in == nil { - *out = nil - } else { - *out = new(Destination) - **out = **in - } - } - if in.AppendHeaders != nil { - in, out := &in.AppendHeaders, &out.AppendHeaders - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.RemoveResponseHeaders != nil { - in, out := &in.RemoveResponseHeaders, &out.RemoveResponseHeaders - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRoute. -func (in *HTTPRoute) DeepCopy() *HTTPRoute { - if in == nil { - return nil - } - out := new(HTTPRoute) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InjectAbort) DeepCopyInto(out *InjectAbort) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InjectAbort. -func (in *InjectAbort) DeepCopy() *InjectAbort { - if in == nil { - return nil - } - out := new(InjectAbort) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *InjectDelay) DeepCopyInto(out *InjectDelay) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new InjectDelay. -func (in *InjectDelay) DeepCopy() *InjectDelay { - if in == nil { - return nil - } - out := new(InjectDelay) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *L4MatchAttributes) DeepCopyInto(out *L4MatchAttributes) { - *out = *in - if in.SourceLabel != nil { - in, out := &in.SourceLabel, &out.SourceLabel - *out = make(map[string]string, len(*in)) - for key, val := range *in { - (*out)[key] = val - } - } - if in.Gateways != nil { - in, out := &in.Gateways, &out.Gateways - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new L4MatchAttributes. -func (in *L4MatchAttributes) DeepCopy() *L4MatchAttributes { - if in == nil { - return nil - } - out := new(L4MatchAttributes) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Port) DeepCopyInto(out *Port) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Port. -func (in *Port) DeepCopy() *Port { - if in == nil { - return nil - } - out := new(Port) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *PortSelector) DeepCopyInto(out *PortSelector) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new PortSelector. -func (in *PortSelector) DeepCopy() *PortSelector { - if in == nil { - return nil - } - out := new(PortSelector) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *Server) DeepCopyInto(out *Server) { - *out = *in - out.Port = in.Port - if in.Hosts != nil { - in, out := &in.Hosts, &out.Hosts - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.TLS != nil { - in, out := &in.TLS, &out.TLS - if *in == nil { - *out = nil - } else { - *out = new(TLSOptions) - (*in).DeepCopyInto(*out) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Server. -func (in *Server) DeepCopy() *Server { - if in == nil { - return nil - } - out := new(Server) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *StringMatch) DeepCopyInto(out *StringMatch) { - *out = *in - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StringMatch. -func (in *StringMatch) DeepCopy() *StringMatch { - if in == nil { - return nil - } - out := new(StringMatch) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TCPRoute) DeepCopyInto(out *TCPRoute) { - *out = *in - if in.Match != nil { - in, out := &in.Match, &out.Match - *out = make([]L4MatchAttributes, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - out.Route = in.Route - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TCPRoute. -func (in *TCPRoute) DeepCopy() *TCPRoute { - if in == nil { - return nil - } - out := new(TCPRoute) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *TLSOptions) DeepCopyInto(out *TLSOptions) { - *out = *in - if in.SubjectAltNames != nil { - in, out := &in.SubjectAltNames, &out.SubjectAltNames - *out = make([]string, len(*in)) - copy(*out, *in) - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new TLSOptions. -func (in *TLSOptions) DeepCopy() *TLSOptions { - if in == nil { - return nil - } - out := new(TLSOptions) - in.DeepCopyInto(out) - return out -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VirtualService) DeepCopyInto(out *VirtualService) { - *out = *in - out.TypeMeta = in.TypeMeta - in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - in.Spec.DeepCopyInto(&out.Spec) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualService. -func (in *VirtualService) DeepCopy() *VirtualService { - if in == nil { - return nil - } - out := new(VirtualService) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VirtualService) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } else { - return nil - } -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VirtualServiceList) DeepCopyInto(out *VirtualServiceList) { - *out = *in - out.TypeMeta = in.TypeMeta - out.ListMeta = in.ListMeta - if in.Items != nil { - in, out := &in.Items, &out.Items - *out = make([]VirtualService, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServiceList. -func (in *VirtualServiceList) DeepCopy() *VirtualServiceList { - if in == nil { - return nil - } - out := new(VirtualServiceList) - in.DeepCopyInto(out) - return out -} - -// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. -func (in *VirtualServiceList) DeepCopyObject() runtime.Object { - if c := in.DeepCopy(); c != nil { - return c - } else { - return nil - } -} - -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *VirtualServiceSpec) DeepCopyInto(out *VirtualServiceSpec) { - *out = *in - if in.Hosts != nil { - in, out := &in.Hosts, &out.Hosts - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Gateways != nil { - in, out := &in.Gateways, &out.Gateways - *out = make([]string, len(*in)) - copy(*out, *in) - } - if in.Http != nil { - in, out := &in.Http, &out.Http - *out = make([]HTTPRoute, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - if in.Tcp != nil { - in, out := &in.Tcp, &out.Tcp - *out = make([]TCPRoute, len(*in)) - for i := range *in { - (*in)[i].DeepCopyInto(&(*out)[i]) - } - } - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VirtualServiceSpec. -func (in *VirtualServiceSpec) DeepCopy() *VirtualServiceSpec { - if in == nil { - return nil - } - out := new(VirtualServiceSpec) - in.DeepCopyInto(out) - return out -} diff --git a/vendor/github.com/knative/serving/pkg/apis/networking/register.go b/vendor/github.com/knative/serving/pkg/apis/networking/register.go new file mode 100644 index 00000000000..e47c0cfa043 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/networking/register.go @@ -0,0 +1,40 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package networking + +const ( + GroupName = "networking.internal.knative.dev" + + // IngressClassAnnotationKey is the annotation for the + // explicit class of ClusterIngress that a particular resource has + // opted into. For example, + // + // networking.knative.dev/ingress.class: some-network-impl + // + // This uses a different domain because unlike the resource, it is + // user-facing. + // + // The parent resource may use its own annotations to choose the + // annotation value for the ClusterIngress it uses. Based on such + // value a different reconcilation logic may be used (for examples, + // Istio-based ClusterIngress will reconcile into a VirtualService). + IngressClassAnnotationKey = "networking.knative.dev/ingress.class" + + // IngressLabelKey is the label key attached to underlying network programming + // resources to indicate which ClusterIngress triggered their creation. + IngressLabelKey = GroupName + "/clusteringress" +) diff --git a/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/clusteringress_defaults.go b/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/clusteringress_defaults.go new file mode 100644 index 00000000000..7a6df88b3b1 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/clusteringress_defaults.go @@ -0,0 +1,85 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "time" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + // DefaultTimeout will be set if timeout not specified. + DefaultTimeout = 60 * time.Second + // DefaultRetryCount will be set if Attempts not specified. + DefaultRetryCount = 3 +) + +func (c *ClusterIngress) SetDefaults() { + c.Spec.SetDefaults() +} + +func (c *IngressSpec) SetDefaults() { + for i := range c.TLS { + c.TLS[i].SetDefaults() + } + for i := range c.Rules { + c.Rules[i].SetDefaults() + } +} + +func (t *ClusterIngressTLS) SetDefaults() { + // Default Secret key for ServerCertificate is `tls.cert`. + if t.ServerCertificate == "" { + t.ServerCertificate = "tls.cert" + } + // Default Secret key for PrivateKey is `tls.key`. + if t.PrivateKey == "" { + t.PrivateKey = "tls.key" + } +} + +func (r *ClusterIngressRule) SetDefaults() { + r.HTTP.SetDefaults() +} + +func (r *HTTPClusterIngressRuleValue) SetDefaults() { + for i := range r.Paths { + r.Paths[i].SetDefaults() + } +} + +func (p *HTTPClusterIngressPath) SetDefaults() { + // If only one split is specified, we default to 100. + if len(p.Splits) == 1 && p.Splits[0].Percent == 0 { + p.Splits[0].Percent = 100 + } + + if p.Timeout == nil { + p.Timeout = &metav1.Duration{Duration: DefaultTimeout} + } + + if p.Retries == nil { + p.Retries = &HTTPRetry{ + PerTryTimeout: &metav1.Duration{Duration: DefaultTimeout}, + Attempts: DefaultRetryCount, + } + } + if p.Retries.PerTryTimeout == nil { + p.Retries.PerTryTimeout = &metav1.Duration{Duration: DefaultTimeout} + } +} diff --git a/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/clusteringress_types.go b/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/clusteringress_types.go new file mode 100644 index 00000000000..7d19a45b79d --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/clusteringress_types.go @@ -0,0 +1,346 @@ +/* +Copyright 2018 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "github.com/knative/pkg/apis" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/util/intstr" +) + +// +genclient +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object +// +genclient:nonNamespaced + +// ClusterIngress is a collection of rules that allow inbound connections to reach the +// endpoints defined by a backend. An ClusterIngress can be configured to give services +// externally-reachable urls, load balance traffic offer name based virtual hosting etc. +// +// This is heavily based on K8s Ingress https://godoc.org/k8s.io/api/extensions/v1beta1#Ingress +// which some highlighted modifications. +type ClusterIngress struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // +optional + metav1.ObjectMeta `json:"metadata,omitempty"` + + // Spec is the desired state of the ClusterIngress. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status + // +optional + Spec IngressSpec `json:"spec,omitempty"` + + // Status is the current state of the ClusterIngress. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#spec-and-status + // +optional + Status IngressStatus `json:"status,omitempty"` +} + +// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object + +// ClusterIngressList is a collection of ClusterIngress. +type ClusterIngressList struct { + metav1.TypeMeta `json:",inline"` + // Standard object's metadata. + // More info: https://git.k8s.io/community/contributors/devel/api-conventions.md#metadata + // +optional + metav1.ListMeta `json:"metadata,omitempty"` + + // Items is the list of ClusterIngress. + Items []ClusterIngress `json:"items"` +} + +// IngressSpec describes the ClusterIngress the user wishes to exist. +// +// In general this follow the same shape as K8s Ingress. Some notable differences: +// - Backends now can have namespace: +// - Traffic can be split across multiple backends. +// - Timeout & Retry can be configured. +// - Headers can be appended. +type IngressSpec struct { + // TODO: Generation does not work correctly with CRD. They are scrubbed + // by the APIserver (https://github.com/kubernetes/kubernetes/issues/58778) + // So, we add Generation here. Once that gets fixed, remove this and use + // ObjectMeta.Generation instead. + // +optional + Generation int64 `json:"generation,omitempty"` + + // TLS configuration. Currently the ClusterIngress only supports a single TLS + // port, 443. If multiple members of this list specify different hosts, they + // will be multiplexed on the same port according to the hostname specified + // through the SNI TLS extension, if the ingress controller fulfilling the + // ingress supports SNI. + // +optional + TLS []ClusterIngressTLS `json:"tls,omitempty"` + + // A list of host rules used to configure the ClusterIngress. + // +optional + Rules []ClusterIngressRule `json:"rules,omitempty"` + + // TODO: We need to consider a way to specify if the ClusterIngress address + // should be exposed to the Internet, or only exposed privately (cluster local, + // VPC, RFC1918). An example use case is for + // https://github.com/knative/serving/issues/2127. +} + +// ClusterIngressTLS describes the transport layer security associated with an ClusterIngress. +type ClusterIngressTLS struct { + // Hosts are a list of hosts included in the TLS certificate. The values in + // this list must match the name/s used in the tlsSecret. Defaults to the + // wildcard host setting for the loadbalancer controller fulfilling this + // ClusterIngress, if left unspecified. + // +optional + Hosts []string `json:"hosts,omitempty"` + + // SecretName is the name of the secret used to terminate SSL traffic. + SecretName string `json:"secretName,omitempty"` + + // SecretNamespace is the namespace of the secret used to terminate SSL traffic. + SecretNamespace string `json:"secretNamespace,omitempty"` + + // ServerCertificate identifies the certificate filename in the secret. + // Defaults to `tls.cert`. + // +optional + ServerCertificate string `json:"serverCertificate,omitempty"` + + // PrivateKey identifies the private key filename in the secret. + // Defaults to `tls.key`. + // +optional + PrivateKey string `json:"privateKey,omitempty"` +} + +// ClusterIngressRule represents the rules mapping the paths under a specified host to +// the related backend services. Incoming requests are first evaluated for a host +// match, then routed to the backend associated with the matching ClusterIngressRuleValue. +type ClusterIngressRule struct { + // Host is the fully qualified domain name of a network host, as defined + // by RFC 3986. Note the following deviations from the "host" part of the + // URI as defined in the RFC: + // 1. IPs are not allowed. Currently a rule value can only apply to the + // IP in the Spec of the parent ClusterIngress. + // 2. The `:` delimiter is not respected because ports are not allowed. + // Currently the port of an ClusterIngress is implicitly :80 for http and + // :443 for https. + // Both these may change in the future. + // If the host is unspecified, the ClusterIngress routes all traffic based on the + // specified ClusterIngressRuleValue. + // If multiple matching Hosts were provided, the first rule will take precedent. + // +optional + Hosts []string `json:"hosts,omitempty"` + + // HTTP represents a rule to apply against incoming requests. If the + // rule is satisfied, the request is routed to the specified backend. + HTTP *HTTPClusterIngressRuleValue `json:"http,omitempty"` +} + +// HTTPClusterIngressRuleValue is a list of http selectors pointing to backends. +// In the example: http:///? -> backend where +// where parts of the url correspond to RFC 3986, this resource will be used +// to match against everything after the last '/' and before the first '?' +// or '#'. +type HTTPClusterIngressRuleValue struct { + // A collection of paths that map requests to backends. + // + // If they are multiple matching paths, the first match takes precendent. + Paths []HTTPClusterIngressPath `json:"paths"` + + // TODO: Consider adding fields for ingress-type specific global + // options usable by a loadbalancer, like http keep-alive. +} + +// HTTPClusterIngressPath associates a path regex with a backend. Incoming urls matching +// the path are forwarded to the backend. +type HTTPClusterIngressPath struct { + // Path is an extended POSIX regex as defined by IEEE Std 1003.1, + // (i.e this follows the egrep/unix syntax, not the perl syntax) + // matched against the path of an incoming request. Currently it can + // contain characters disallowed from the conventional "path" + // part of a URL as defined by RFC 3986. Paths must begin with + // a '/'. If unspecified, the path defaults to a catch all sending + // traffic to the backend. + // +optional + Path string `json:"path,omitempty"` + + // Splits defines the referenced service endpoints to which the traffic + // will be forwarded to. + Splits []ClusterIngressBackendSplit `json:"splits"` + + // AppendHeaders allow specifying additional HTTP headers to add + // before forwarding a request to the destination service. + // + // NOTE: This differs from K8s Ingress which doesn't allow header appending. + // +optional + AppendHeaders map[string]string `json:"appendHeaders,omitempty"` + + // Timeout for HTTP requests. + // + // NOTE: This differs from K8s Ingress which doesn't allow setting timeouts. + // +optional + Timeout *metav1.Duration `json:"timeout,omitempty"` + + // Retry policy for HTTP requests. + // + // NOTE: This differs from K8s Ingress which doesn't allow retry settings. + // +optional + Retries *HTTPRetry `json:"retries,omitempty"` +} + +// ClusterIngressBackend describes all endpoints for a given service and port. +type ClusterIngressBackendSplit struct { + // Specifies the backend receiving the traffic split. + ClusterIngressBackend `json:",inline"` + + // Specifies the split percentage, a number between 0 and 100. If + // only one split is specified, we default to 100. + // + // NOTE: This differs from K8s Ingress to allow percentage split. + Percent int `json:"percent,omitempty"` +} + +// ClusterIngressBackend describes all endpoints for a given service and port. +type ClusterIngressBackend struct { + // Specifies the namespace of the referenced service. + // + // NOTE: This differs from K8s Ingress to allow routing to different namespaces. + ServiceNamespace string `json:"serviceNamespace"` + + // Specifies the name of the referenced service. + ServiceName string `json:"serviceName"` + + // Specifies the port of the referenced service. + ServicePort intstr.IntOrString `json:"servicePort"` +} + +// HTTPRetry describes the retry policy to use when a HTTP request fails. +type HTTPRetry struct { + // Number of retries for a given request. + Attempts int `json:"attempts"` + + // Timeout per retry attempt for a given request. format: 1h/1m/1s/1ms. MUST BE >=1ms. + PerTryTimeout *metav1.Duration `json:"perTryTimeout"` +} + +// IngressStatus describe the current state of the ClusterIngress. +type IngressStatus struct { + // +optional + Conditions duckv1alpha1.Conditions `json:"conditions,omitempty"` + // LoadBalancer contains the current status of the load-balancer. + // +optional + LoadBalancer *LoadBalancerStatus `json:"loadBalancer,omitempty"` +} + +// LoadBalancerStatus represents the status of a load-balancer. +type LoadBalancerStatus struct { + // Ingress is a list containing ingress points for the load-balancer. + // Traffic intended for the service should be sent to these ingress points. + // +optional + Ingress []LoadBalancerIngressStatus `json:"ingress,omitempty"` +} + +// LoadBalancerIngress represents the status of a load-balancer ingress point: +// traffic intended for the service should be sent to an ingress point. +type LoadBalancerIngressStatus struct { + // IP is set for load-balancer ingress points that are IP based + // (typically GCE or OpenStack load-balancers) + // +optional + IP string `json:"ip,omitempty"` + + // Domain is set for load-balancer ingress points that are DNS based + // (typically AWS load-balancers) + // +optional + Domain string `json:"domain,omitempty"` + + // DomainInternal is set if there is a cluster-local DNS name to access the Ingress. + // + // NOTE: This differs from K8s Ingress, since we also desire to have a cluster-local + // DNS name to allow routing in case of not having a mesh. + // + // +optional + DomainInternal string `json:"domainInternal,omitempty"` +} + +// ConditionType represents a ClusterIngress condition value +const ( + // ClusterIngressConditionReady is set when the clusterIngress networking setting is + // configured and it has a load balancer address. + ClusterIngressConditionReady = duckv1alpha1.ConditionReady + + // ClusterIngressConditionNetworkConfigured is set when the ClusterIngress's underlying + // network programming has been configured. This doesn't include conditions of the + // backends, so even if this should remain true when network is configured and backends + // are not ready. + ClusterIngressConditionNetworkConfigured duckv1alpha1.ConditionType = "NetworkConfigured" + + // ClusterIngressConditionLoadBalancerReady is set when the ClusterIngress has + // a ready LoadBalancer. + ClusterIngressConditionLoadBalancerReady duckv1alpha1.ConditionType = "LoadBalancerReady" +) + +var clusterIngressCondSet = duckv1alpha1.NewLivingConditionSet( + ClusterIngressConditionNetworkConfigured, + ClusterIngressConditionLoadBalancerReady) + +var _ apis.Validatable = (*ClusterIngress)(nil) +var _ apis.Defaultable = (*ClusterIngress)(nil) + +func (ci *ClusterIngress) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("ClusterIngress") +} + +// GetConditions returns the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (cis *IngressStatus) GetConditions() duckv1alpha1.Conditions { + return cis.Conditions +} + +// SetConditions sets the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (cis *IngressStatus) SetConditions(conditions duckv1alpha1.Conditions) { + cis.Conditions = conditions +} + +func (cis *IngressStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { + return clusterIngressCondSet.Manage(cis).GetCondition(t) +} + +func (cis *IngressStatus) InitializeConditions() { + clusterIngressCondSet.Manage(cis).InitializeConditions() +} + +func (cis *IngressStatus) MarkNetworkConfigured() { + clusterIngressCondSet.Manage(cis).MarkTrue(ClusterIngressConditionNetworkConfigured) +} + +// MarkLoadBalancerReady marks the Ingress with ClusterIngressConditionLoadBalancerReady, +// and also populate the address of the load balancer. +func (cis *IngressStatus) MarkLoadBalancerReady(lbs []LoadBalancerIngressStatus) { + cis.LoadBalancer = &LoadBalancerStatus{ + Ingress: []LoadBalancerIngressStatus{}, + } + for _, lb := range lbs { + cis.LoadBalancer.Ingress = append(cis.LoadBalancer.Ingress, lb) + } + clusterIngressCondSet.Manage(cis).MarkTrue(ClusterIngressConditionLoadBalancerReady) +} + +// IsReady looks at the conditions and if the Status has a condition +// ClusterIngressConditionReady returns true if ConditionStatus is True +func (cis *IngressStatus) IsReady() bool { + return clusterIngressCondSet.Manage(cis).IsHappy() +} diff --git a/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/clusteringress_validation.go b/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/clusteringress_validation.go new file mode 100644 index 00000000000..b64129b2ed1 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/clusteringress_validation.go @@ -0,0 +1,168 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "fmt" + + "github.com/knative/pkg/apis" + "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/util/intstr" +) + +func (ci *ClusterIngress) Validate() *apis.FieldError { + return ci.Spec.Validate().ViaField("spec") +} + +func (spec *IngressSpec) Validate() *apis.FieldError { + // Spec must not be empty. + if equality.Semantic.DeepEqual(spec, &IngressSpec{}) { + return apis.ErrMissingField(apis.CurrentField) + } + var all *apis.FieldError = nil + // Spec must have at least one rule. + if len(spec.Rules) == 0 { + all = all.Also(apis.ErrMissingField("rules")) + } + // Validate each rule. + for idx, rule := range spec.Rules { + all = all.Also(rule.Validate().ViaFieldIndex("rules", idx)) + } + // TLS settings are optional. However, all provided settings should be valid. + for idx, tls := range spec.TLS { + all = all.Also(tls.Validate().ViaFieldIndex("tls", idx)) + } + return all +} + +func (r *ClusterIngressRule) Validate() *apis.FieldError { + // Provided rule must not be empty. + if equality.Semantic.DeepEqual(r, &ClusterIngressRule{}) { + return apis.ErrMissingField(apis.CurrentField) + } + var all *apis.FieldError = nil + if r.HTTP == nil { + all = all.Also(apis.ErrMissingField("http")) + } else { + all = all.Also(r.HTTP.Validate().ViaField("http")) + } + return all +} + +func (h *HTTPClusterIngressRuleValue) Validate() *apis.FieldError { + if len(h.Paths) == 0 { + return apis.ErrMissingField("paths") + } + var all *apis.FieldError = nil + for idx, path := range h.Paths { + all = all.Also(path.Validate().ViaFieldIndex("paths", idx)) + } + return all +} + +func (h HTTPClusterIngressPath) Validate() *apis.FieldError { + // Provided rule must not be empty. + if equality.Semantic.DeepEqual(h, HTTPClusterIngressPath{}) { + return apis.ErrMissingField(apis.CurrentField) + } + var all *apis.FieldError = nil + // Must provide as least one split. + if len(h.Splits) == 0 { + all = all.Also(apis.ErrMissingField("splits")) + } else { + totalPct := 0 + for idx, split := range h.Splits { + if err := split.Validate(); err != nil { + return err.ViaFieldIndex("splits", idx) + } + totalPct += split.Percent + } + // If a single split is provided we allow missing Percent, and + // interpret as 100%. + if len(h.Splits) == 1 && totalPct == 0 { + totalPct = 100 + } + // Total traffic split percentage must sum up to 100%. + if totalPct != 100 { + all = all.Also(&apis.FieldError{ + Message: "Traffic split percentage must total to 100", + Paths: []string{"splits"}, + }) + } + } + if h.Retries != nil { + all = all.Also(h.Retries.Validate().ViaField("retries")) + } + return all +} + +func (s ClusterIngressBackendSplit) Validate() *apis.FieldError { + // Must not be empty. + if equality.Semantic.DeepEqual(s, ClusterIngressBackendSplit{}) { + return apis.ErrMissingField(apis.CurrentField) + } + var all *apis.FieldError = nil + // Percent must be between 0 and 100. + if s.Percent < 0 || s.Percent > 100 { + all = all.Also(apis.ErrInvalidValue(fmt.Sprintf("%d", s.Percent), "percent")) + } + return all.Also(s.ClusterIngressBackend.Validate()) +} + +// Validate inspects the fields of the type ClusterIngressBackend +// to determine if they are valid. +func (b ClusterIngressBackend) Validate() *apis.FieldError { + // Must not be empty. + if equality.Semantic.DeepEqual(b, ClusterIngressBackend{}) { + return apis.ErrMissingField(apis.CurrentField) + } + var all *apis.FieldError = nil + if b.ServiceNamespace == "" { + all = all.Also(apis.ErrMissingField("serviceNamespace")) + } + if b.ServiceName == "" { + all = all.Also(apis.ErrMissingField("serviceName")) + } + if equality.Semantic.DeepEqual(b.ServicePort, intstr.IntOrString{}) { + all = all.Also(apis.ErrMissingField("servicePort")) + } + return all +} + +func (r *HTTPRetry) Validate() *apis.FieldError { + // Attempts must be greater than 0. + if r.Attempts < 0 { + return apis.ErrInvalidValue(fmt.Sprintf("%d", r.Attempts), "attempts") + } + return nil +} + +func (t *ClusterIngressTLS) Validate() *apis.FieldError { + // Provided TLS setting must not be empty. + if equality.Semantic.DeepEqual(t, &ClusterIngressTLS{}) { + return apis.ErrMissingField(apis.CurrentField) + } + var all *apis.FieldError = nil + // SecretName and SecretNamespace must not be empty. + if t.SecretName == "" { + all = all.Also(apis.ErrMissingField("secretName")) + } + if t.SecretNamespace == "" { + all = all.Also(apis.ErrMissingField("secretNamespace")) + } + return all +} diff --git a/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/doc.go b/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/doc.go new file mode 100644 index 00000000000..f3f12f28aa8 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/doc.go @@ -0,0 +1,24 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// +k8s:deepcopy-gen=package +// +groupName=networking.internal.knative.dev +package v1alpha1 + +// ClusterIngress is heavily based on K8s Ingress +// https://godoc.org/k8s.io/api/extensions/v1beta1#Ingress with some +// highlighted modifications. See clusteringress_types.go for more +// information about the modifications that we made. diff --git a/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/register.go b/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/register.go new file mode 100644 index 00000000000..1f1290f4055 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/register.go @@ -0,0 +1,53 @@ +/* +Copyright 2018 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "github.com/knative/serving/pkg/apis/networking" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" +) + +// SchemeGroupVersion is group version used to register these objects +var SchemeGroupVersion = schema.GroupVersion{Group: networking.GroupName, Version: "v1alpha1"} + +// Kind takes an unqualified kind and returns back a Group qualified GroupKind +func Kind(kind string) schema.GroupKind { + return SchemeGroupVersion.WithKind(kind).GroupKind() +} + +// Resource takes an unqualified resource and returns a Group qualified GroupResource +func Resource(resource string) schema.GroupResource { + return SchemeGroupVersion.WithResource(resource).GroupResource() +} + +var ( + SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) + AddToScheme = SchemeBuilder.AddToScheme +) + +// Adds the list of known types to Scheme. +func addKnownTypes(scheme *runtime.Scheme) error { + scheme.AddKnownTypes(SchemeGroupVersion, + &ClusterIngress{}, + &ClusterIngressList{}, + ) + metav1.AddToGroupVersion(scheme, SchemeGroupVersion) + return nil +} diff --git a/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000000..10cc937b823 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/networking/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,368 @@ +// +build !ignore_autogenerated + +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// This file was autogenerated by deepcopy-gen. Do not edit it manually! + +package v1alpha1 + +import ( + duck_v1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterIngress) DeepCopyInto(out *ClusterIngress) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterIngress. +func (in *ClusterIngress) DeepCopy() *ClusterIngress { + if in == nil { + return nil + } + out := new(ClusterIngress) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterIngress) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } else { + return nil + } +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterIngressBackend) DeepCopyInto(out *ClusterIngressBackend) { + *out = *in + out.ServicePort = in.ServicePort + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterIngressBackend. +func (in *ClusterIngressBackend) DeepCopy() *ClusterIngressBackend { + if in == nil { + return nil + } + out := new(ClusterIngressBackend) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterIngressBackendSplit) DeepCopyInto(out *ClusterIngressBackendSplit) { + *out = *in + out.ClusterIngressBackend = in.ClusterIngressBackend + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterIngressBackendSplit. +func (in *ClusterIngressBackendSplit) DeepCopy() *ClusterIngressBackendSplit { + if in == nil { + return nil + } + out := new(ClusterIngressBackendSplit) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterIngressList) DeepCopyInto(out *ClusterIngressList) { + *out = *in + out.TypeMeta = in.TypeMeta + out.ListMeta = in.ListMeta + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ClusterIngress, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterIngressList. +func (in *ClusterIngressList) DeepCopy() *ClusterIngressList { + if in == nil { + return nil + } + out := new(ClusterIngressList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ClusterIngressList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } else { + return nil + } +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterIngressRule) DeepCopyInto(out *ClusterIngressRule) { + *out = *in + if in.Hosts != nil { + in, out := &in.Hosts, &out.Hosts + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.HTTP != nil { + in, out := &in.HTTP, &out.HTTP + if *in == nil { + *out = nil + } else { + *out = new(HTTPClusterIngressRuleValue) + (*in).DeepCopyInto(*out) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterIngressRule. +func (in *ClusterIngressRule) DeepCopy() *ClusterIngressRule { + if in == nil { + return nil + } + out := new(ClusterIngressRule) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ClusterIngressTLS) DeepCopyInto(out *ClusterIngressTLS) { + *out = *in + if in.Hosts != nil { + in, out := &in.Hosts, &out.Hosts + *out = make([]string, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ClusterIngressTLS. +func (in *ClusterIngressTLS) DeepCopy() *ClusterIngressTLS { + if in == nil { + return nil + } + out := new(ClusterIngressTLS) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPClusterIngressPath) DeepCopyInto(out *HTTPClusterIngressPath) { + *out = *in + if in.Splits != nil { + in, out := &in.Splits, &out.Splits + *out = make([]ClusterIngressBackendSplit, len(*in)) + copy(*out, *in) + } + if in.AppendHeaders != nil { + in, out := &in.AppendHeaders, &out.AppendHeaders + *out = make(map[string]string, len(*in)) + for key, val := range *in { + (*out)[key] = val + } + } + if in.Timeout != nil { + in, out := &in.Timeout, &out.Timeout + if *in == nil { + *out = nil + } else { + *out = new(v1.Duration) + **out = **in + } + } + if in.Retries != nil { + in, out := &in.Retries, &out.Retries + if *in == nil { + *out = nil + } else { + *out = new(HTTPRetry) + (*in).DeepCopyInto(*out) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPClusterIngressPath. +func (in *HTTPClusterIngressPath) DeepCopy() *HTTPClusterIngressPath { + if in == nil { + return nil + } + out := new(HTTPClusterIngressPath) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPClusterIngressRuleValue) DeepCopyInto(out *HTTPClusterIngressRuleValue) { + *out = *in + if in.Paths != nil { + in, out := &in.Paths, &out.Paths + *out = make([]HTTPClusterIngressPath, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPClusterIngressRuleValue. +func (in *HTTPClusterIngressRuleValue) DeepCopy() *HTTPClusterIngressRuleValue { + if in == nil { + return nil + } + out := new(HTTPClusterIngressRuleValue) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *HTTPRetry) DeepCopyInto(out *HTTPRetry) { + *out = *in + if in.PerTryTimeout != nil { + in, out := &in.PerTryTimeout, &out.PerTryTimeout + if *in == nil { + *out = nil + } else { + *out = new(v1.Duration) + **out = **in + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPRetry. +func (in *HTTPRetry) DeepCopy() *HTTPRetry { + if in == nil { + return nil + } + out := new(HTTPRetry) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IngressSpec) DeepCopyInto(out *IngressSpec) { + *out = *in + if in.TLS != nil { + in, out := &in.TLS, &out.TLS + *out = make([]ClusterIngressTLS, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.Rules != nil { + in, out := &in.Rules, &out.Rules + *out = make([]ClusterIngressRule, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressSpec. +func (in *IngressSpec) DeepCopy() *IngressSpec { + if in == nil { + return nil + } + out := new(IngressSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *IngressStatus) DeepCopyInto(out *IngressStatus) { + *out = *in + if in.Conditions != nil { + in, out := &in.Conditions, &out.Conditions + *out = make(duck_v1alpha1.Conditions, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.LoadBalancer != nil { + in, out := &in.LoadBalancer, &out.LoadBalancer + if *in == nil { + *out = nil + } else { + *out = new(LoadBalancerStatus) + (*in).DeepCopyInto(*out) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new IngressStatus. +func (in *IngressStatus) DeepCopy() *IngressStatus { + if in == nil { + return nil + } + out := new(IngressStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LoadBalancerIngressStatus) DeepCopyInto(out *LoadBalancerIngressStatus) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancerIngressStatus. +func (in *LoadBalancerIngressStatus) DeepCopy() *LoadBalancerIngressStatus { + if in == nil { + return nil + } + out := new(LoadBalancerIngressStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *LoadBalancerStatus) DeepCopyInto(out *LoadBalancerStatus) { + *out = *in + if in.Ingress != nil { + in, out := &in.Ingress, &out.Ingress + *out = make([]LoadBalancerIngressStatus, len(*in)) + copy(*out, *in) + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new LoadBalancerStatus. +func (in *LoadBalancerStatus) DeepCopy() *LoadBalancerStatus { + if in == nil { + return nil + } + out := new(LoadBalancerStatus) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/register.go b/vendor/github.com/knative/serving/pkg/apis/serving/register.go index d4c7ae5f880..a5daf5d0343 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/register.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/register.go @@ -27,10 +27,20 @@ const ( // generation of the Configuration that created this revision ConfigurationGenerationAnnotationKey = GroupName + "/configurationGeneration" + // RevisionLastPinnedAnnotationKey is the annotation key used for determining when a route has + // pinned a revision + RevisionLastPinnedAnnotationKey = GroupName + "/lastPinned" + // RouteLabelKey is the label key attached to a Configuration indicating by // which Route it is configured as traffic target. + // The key can also be attached to ClusterIngress resources to indicate + // which Route triggered their creation. RouteLabelKey = GroupName + "/route" + // RouteNamespaceLabelKey is the label key attached to a ClusterIngress + // by a Route to indicate which namespace the Route was created in. + RouteNamespaceLabelKey = GroupName + "/routeNamespace" + // RevisionLabelKey is the label key attached to k8s resources to indicate // which Revision triggered their creation. RevisionLabelKey = GroupName + "/revision" diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/build_compat.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/build_compat.go new file mode 100644 index 00000000000..3069caec0e7 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/build_compat.go @@ -0,0 +1,101 @@ +/* +Copyright 2018 The Knative Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "bytes" + "encoding/json" + "errors" + + "k8s.io/apimachinery/pkg/runtime" + + buildv1alpha1 "github.com/knative/build/pkg/apis/build/v1alpha1" +) + +// RawExtension is modeled after runtime.RawExtension, and should be +// replaced with it (or an alias) once we can stop supporting embedded +// BuildSpecs. +type RawExtension struct { + // Field order is the precedence for JSON marshaling if multiple + // fields are set. + Raw []byte + Object runtime.Object + BuildSpec *buildv1alpha1.BuildSpec +} + +var _ json.Unmarshaler = (*RawExtension)(nil) +var _ json.Marshaler = (*RawExtension)(nil) + +func (re *RawExtension) UnmarshalJSON(in []byte) error { + if re == nil { + return errors.New("RawExtension: UnmarshalJSON on nil pointer") + } + if !bytes.Equal(in, []byte("null")) { + re.Raw = append(re.Raw[0:0], in...) + } + return nil +} + +// MarshalJSON may get called on pointers or values, so implement MarshalJSON on value. +// http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go +func (re RawExtension) MarshalJSON() ([]byte, error) { + switch { + case re.Raw != nil: + return re.Raw, nil + + case re.Object != nil: + return json.Marshal(re.Object) + + case re.BuildSpec != nil: + return json.Marshal(re.BuildSpec) + + default: + return []byte("null"), nil + } +} + +func (re *RawExtension) ensureRaw() (err error) { + switch { + case re.Raw != nil: + // Nothing to do. + case re.Object != nil, re.BuildSpec != nil: + re.Raw, err = re.MarshalJSON() + } + return +} + +// As is a helper to decode the raw object into a particular type. +// The type is expected to exhaustively specify the fields encountered. +func (re *RawExtension) As(x interface{}) error { + if err := re.ensureRaw(); err != nil { + return err + } + decoder := json.NewDecoder(bytes.NewBuffer(re.Raw)) + decoder.DisallowUnknownFields() + return decoder.Decode(&x) +} + +// AsDuck is a helper to decode the raw object into a particular duck type. +// The type may only represent a subset of the fields present. +func (re *RawExtension) AsDuck(x interface{}) error { + if err := re.ensureRaw(); err != nil { + return err + } + decoder := json.NewDecoder(bytes.NewBuffer(re.Raw)) + // Allow unknown fields. + return decoder.Decode(&x) +} diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/configuration_defaults.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/configuration_defaults.go index 559ede792d8..150ff4a9e31 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/configuration_defaults.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/configuration_defaults.go @@ -1,5 +1,6 @@ /* -Copyright 2017 The Knative Authors +Copyright 2018 The Knative Authors + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/configuration_types.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/configuration_types.go index 0bb54743e30..503d4ea9b0f 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/configuration_types.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/configuration_types.go @@ -17,15 +17,12 @@ limitations under the License. package v1alpha1 import ( - "encoding/json" - "fmt" - "reflect" - "time" - - build "github.com/knative/build/pkg/apis/build/v1alpha1" - - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/knative/pkg/apis" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "github.com/knative/pkg/kmeta" ) // +genclient @@ -52,8 +49,14 @@ type Configuration struct { } // Check that Configuration may be validated and defaulted. -var _ Validatable = (*Configuration)(nil) -var _ Defaultable = (*Configuration)(nil) +var _ apis.Validatable = (*Configuration)(nil) +var _ apis.Defaultable = (*Configuration)(nil) + +// Check that we can create OwnerReferences to a Configuration. +var _ kmeta.OwnerRefable = (*Configuration)(nil) + +// Check that ConfigurationStatus may have its conditions managed. +var _ duckv1alpha1.ConditionsAccessor = (*ConfigurationStatus)(nil) // ConfigurationSpec holds the desired state of the Configuration (from the client). type ConfigurationSpec struct { @@ -67,7 +70,7 @@ type ConfigurationSpec struct { // Build optionally holds the specification for the build to // perform to produce the Revision's container image. // +optional - Build *build.BuildSpec `json:"build,omitempty"` + Build *RawExtension `json:"build,omitempty"` // RevisionTemplate holds the latest specification for the Revision to // be stamped out. If a Build specification is provided, then the @@ -77,32 +80,13 @@ type ConfigurationSpec struct { RevisionTemplate RevisionTemplateSpec `json:"revisionTemplate"` } -// ConfigurationConditionType is used to communicate the status of the reconciliation process. -// See also: https://github.com/knative/serving/blob/master/docs/spec/errors.md#error-conditions-and-reporting -type ConfigurationConditionType string - const ( // ConfigurationConditionReady is set when the configuration's latest // underlying revision has reported readiness. - ConfigurationConditionReady ConfigurationConditionType = "Ready" + ConfigurationConditionReady = duckv1alpha1.ConditionReady ) -// ConfigurationCondition defines a readiness condition for a Configuration. -// See: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#typical-status-properties -type ConfigurationCondition struct { - Type ConfigurationConditionType `json:"type" description:"type of Configuration condition"` - - Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` - - // +optional - LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" description:"last time the condition transit from one status to another"` - - // +optional - Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"` - - // +optional - Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"` -} +var confCondSet = duckv1alpha1.NewLivingConditionSet() // ConfigurationStatus communicates the observed state of the Configuration (from the controller). type ConfigurationStatus struct { @@ -110,7 +94,7 @@ type ConfigurationStatus struct { // reconciliation processes that bring the "spec" inline with the observed // state of the world. // +optional - Conditions []ConfigurationCondition `json:"conditions,omitempty"` + Conditions duckv1alpha1.Conditions `json:"conditions,omitempty"` // LatestReadyRevisionName holds the name of the latest Revision stamped out // from this Configuration that has had its "Ready" condition become "True". @@ -139,25 +123,13 @@ type ConfigurationList struct { Items []Configuration `json:"items"` } -func (r *Configuration) GetGeneration() int64 { - return r.Spec.Generation +func (r *Configuration) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("Configuration") } -func (r *Configuration) SetGeneration(generation int64) { - r.Spec.Generation = generation -} - -func (r *Configuration) GetSpecJSON() ([]byte, error) { - return json.Marshal(r.Spec) -} - -// IsReady looks at the conditions on the ConfigurationStatus. -// ConfigurationConditionReady returns true if ConditionStatus is True +// IsReady looks at the conditions to see if they are happy. func (cs *ConfigurationStatus) IsReady() bool { - if c := cs.GetCondition(ConfigurationConditionReady); c != nil { - return c.Status == corev1.ConditionTrue - } - return false + return confCondSet.Manage(cs).IsHappy() } // IsLatestReadyRevisionNameUpToDate returns true if the Configuration is ready @@ -168,115 +140,58 @@ func (cs *ConfigurationStatus) IsLatestReadyRevisionNameUpToDate() bool { cs.LatestCreatedRevisionName == cs.LatestReadyRevisionName } -func (config *ConfigurationStatus) GetCondition(t ConfigurationConditionType) *ConfigurationCondition { - for _, cond := range config.Conditions { - if cond.Type == t { - return &cond - } - } - return nil -} - -func (cs *ConfigurationStatus) setCondition(new *ConfigurationCondition) { - if new == nil { - return - } - t := new.Type - var conditions []ConfigurationCondition - for _, cond := range cs.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } else { - // If we'd only update the LastTransitionTime, then return. - new.LastTransitionTime = cond.LastTransitionTime - if reflect.DeepEqual(new, &cond) { - return - } - } - } - new.LastTransitionTime = metav1.NewTime(time.Now()) - conditions = append(conditions, *new) - cs.Conditions = conditions -} - -func (cs *ConfigurationStatus) RemoveCondition(t ConfigurationConditionType) { - var conditions []ConfigurationCondition - for _, cond := range cs.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - cs.Conditions = conditions +func (cs *ConfigurationStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { + return confCondSet.Manage(cs).GetCondition(t) } func (cs *ConfigurationStatus) InitializeConditions() { - for _, cond := range []ConfigurationConditionType{ - ConfigurationConditionReady, - } { - if rc := cs.GetCondition(cond); rc == nil { - cs.setCondition(&ConfigurationCondition{ - Type: cond, - Status: corev1.ConditionUnknown, - }) - } - } + confCondSet.Manage(cs).InitializeConditions() } func (cs *ConfigurationStatus) SetLatestCreatedRevisionName(name string) { cs.LatestCreatedRevisionName = name if cs.LatestReadyRevisionName != name { - cs.setCondition(&ConfigurationCondition{ - Type: ConfigurationConditionReady, - Status: corev1.ConditionUnknown, - }) + confCondSet.Manage(cs).MarkUnknown( + ConfigurationConditionReady, + "", + "") } } func (cs *ConfigurationStatus) SetLatestReadyRevisionName(name string) { cs.LatestReadyRevisionName = name - for _, cond := range []ConfigurationConditionType{ - ConfigurationConditionReady, - } { - cs.setCondition(&ConfigurationCondition{ - Type: cond, - Status: corev1.ConditionTrue, - }) - } + confCondSet.Manage(cs).MarkTrue(ConfigurationConditionReady) } func (cs *ConfigurationStatus) MarkLatestCreatedFailed(name, message string) { - cct := []ConfigurationConditionType{ConfigurationConditionReady} - if cs.LatestReadyRevisionName == "" { - cct = append(cct, ConfigurationConditionReady) - } - for _, cond := range cct { - cs.setCondition(&ConfigurationCondition{ - Type: cond, - Status: corev1.ConditionFalse, - Reason: "RevisionFailed", - Message: fmt.Sprintf("Revision %q failed with message: %q.", name, message), - }) - } + confCondSet.Manage(cs).MarkFalse( + ConfigurationConditionReady, + "RevisionFailed", + "Revision %q failed with message: %q.", name, message) } func (cs *ConfigurationStatus) MarkRevisionCreationFailed(message string) { - cs.setCondition(&ConfigurationCondition{ - Type: ConfigurationConditionReady, - Status: corev1.ConditionFalse, - Reason: "RevisionFailed", - Message: fmt.Sprintf("Revision creation failed with message: %q.", message), - }) + confCondSet.Manage(cs).MarkFalse( + ConfigurationConditionReady, + "RevisionFailed", + "Revision creation failed with message: %q.", message) } func (cs *ConfigurationStatus) MarkLatestReadyDeleted() { - cct := []ConfigurationConditionType{ConfigurationConditionReady} - for _, cond := range cct { - cs.setCondition(&ConfigurationCondition{ - Type: cond, - Status: corev1.ConditionFalse, - Reason: "RevisionDeleted", - Message: fmt.Sprintf("Revision %q was deleted.", cs.LatestReadyRevisionName), - }) - } - cs.LatestReadyRevisionName = "" + confCondSet.Manage(cs).MarkFalse( + ConfigurationConditionReady, + "RevisionDeleted", + "Revision %q was deleted.", cs.LatestReadyRevisionName) +} + +// GetConditions returns the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (cs *ConfigurationStatus) GetConditions() duckv1alpha1.Conditions { + return cs.Conditions +} + +// SetConditions sets the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (cs *ConfigurationStatus) SetConditions(conditions duckv1alpha1.Conditions) { + cs.Conditions = conditions } diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/configuration_validation.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/configuration_validation.go index 5a28faeb40d..04f5827a1be 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/configuration_validation.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/configuration_validation.go @@ -1,5 +1,6 @@ /* -Copyright 2017 The Knative Authors +Copyright 2018 The Knative Authors + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -17,20 +18,33 @@ package v1alpha1 import ( "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + + buildv1alpha1 "github.com/knative/build/pkg/apis/build/v1alpha1" + "github.com/knative/pkg/apis" ) -func (c *Configuration) Validate() *FieldError { - return c.Spec.Validate().ViaField("spec") +func (c *Configuration) Validate() *apis.FieldError { + return ValidateObjectMetadata(c.GetObjectMeta()).ViaField("metadata"). + Also(c.Spec.Validate().ViaField("spec")) } -func (cs *ConfigurationSpec) Validate() *FieldError { +func (cs *ConfigurationSpec) Validate() *apis.FieldError { if equality.Semantic.DeepEqual(cs, &ConfigurationSpec{}) { - return errMissingField(currentField) + return apis.ErrMissingField(apis.CurrentField) } - // In the context of Configuration, serving state may not be specified at all. + var errs *apis.FieldError // TODO(mattmoor): Check ObjectMeta for Name/Namespace/GenerateName - if cs.RevisionTemplate.Spec.ServingState != "" { - return errDisallowedFields("revisionTemplate.spec.servingState") + + if cs.Build == nil { + // No build was specified. + } else if err := cs.Build.As(&buildv1alpha1.BuildSpec{}); err == nil { + // It is a BuildSpec, this is the legacy path. + } else if err = cs.Build.As(&unstructured.Unstructured{}); err == nil { + // It is an unstructured.Unstructured. + } else { + errs = errs.Also(apis.ErrInvalidValue(err.Error(), "build")) } - return cs.RevisionTemplate.Validate().ViaField("revisionTemplate") + + return errs.Also(cs.RevisionTemplate.Validate().ViaField("revisionTemplate")) } diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/field_error.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/field_error.go deleted file mode 100644 index 18321cfb74e..00000000000 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/field_error.go +++ /dev/null @@ -1,106 +0,0 @@ -/* -Copyright 2017 The Knative Authors -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ - -package v1alpha1 - -import ( - "fmt" - "strings" -) - -// currentField is a constant to supply as a fieldPath for when there is -// a problem with the current field itself. -const currentField = "" - -// FieldError is used to propagate the context of errors pertaining to -// specific fields in a manner suitable for use in a recursive walk, so -// that errors contain the appropriate field context. -// +k8s:deepcopy-gen=false -type FieldError struct { - Message string - Paths []string - // Details contains an optional longer payload. - Details string -} - -// FieldError implements error -var _ error = (*FieldError)(nil) - -// Validatable indicates that a particular type may have its fields validated. -type Validatable interface { - // Validate checks the validity of this types fields. - Validate() *FieldError -} - -// HasImmutableFields indicates that a particular type has fields that should -// not change after creation. -type HasImmutableFields interface { - // CheckImmutableFields checks that the current instance's immutable - // fields haven't changed from the provided original. - CheckImmutableFields(original HasImmutableFields) *FieldError -} - -// ViaField is used to propagate a validation error along a field access. -// For example, if a type recursively validates its "spec" via: -// if err := foo.Spec.Validate(); err != nil { -// // Augment any field paths with the context that they were accessed -// // via "spec". -// return err.ViaField("spec") -// } -func (fe *FieldError) ViaField(prefix ...string) *FieldError { - if fe == nil { - return nil - } - var newPaths []string - for _, oldPath := range fe.Paths { - if oldPath == currentField { - newPaths = append(newPaths, strings.Join(prefix, ".")) - } else { - newPaths = append(newPaths, - strings.Join(append(prefix, oldPath), ".")) - } - } - fe.Paths = newPaths - return fe -} - -// Error implements error -func (fe *FieldError) Error() string { - if fe.Details == "" { - return fmt.Sprintf("%v: %v", fe.Message, strings.Join(fe.Paths, ", ")) - } - return fmt.Sprintf("%v: %v\n%v", fe.Message, strings.Join(fe.Paths, ", "), fe.Details) -} - -func errMissingField(fieldPaths ...string) *FieldError { - return &FieldError{ - Message: "missing field(s)", - Paths: fieldPaths, - } -} - -func errDisallowedFields(fieldPaths ...string) *FieldError { - return &FieldError{ - Message: "must not set the field(s)", - Paths: fieldPaths, - } -} - -func errInvalidValue(value string, fieldPath string) *FieldError { - return &FieldError{ - Message: fmt.Sprintf("invalid value %q", value), - Paths: []string{fieldPath}, - } -} diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/metadata_validation.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/metadata_validation.go new file mode 100644 index 00000000000..b6755d2bdaa --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/metadata_validation.go @@ -0,0 +1,90 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "fmt" + "strconv" + "strings" + + "github.com/knative/pkg/apis" + "github.com/knative/serving/pkg/apis/autoscaling" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func ValidateObjectMetadata(meta metav1.Object) *apis.FieldError { + name := meta.GetName() + + if strings.Contains(name, ".") { + return &apis.FieldError{ + Message: "Invalid resource name: special character . must not be present", + Paths: []string{"name"}, + } + } + + if len(name) > 63 { + return &apis.FieldError{ + Message: "Invalid resource name: length must be no more than 63 characters", + Paths: []string{"name"}, + } + } + + if err := validateScaleBoundsAnnotations(meta.GetAnnotations()); err != nil { + return err.ViaField("annotations") + } + + return nil +} + +func getIntGT0(m map[string]string, k string) (int64, *apis.FieldError) { + v, ok := m[k] + if ok { + i, err := strconv.ParseInt(v, 10, 32) + if err != nil || i < 1 { + return 0, &apis.FieldError{ + Message: fmt.Sprintf("Invalid %s annotation value: must be integer greater than 0", k), + Paths: []string{k}, + } + } + return i, nil + } + return 0, nil +} + +func validateScaleBoundsAnnotations(annotations map[string]string) *apis.FieldError { + if annotations == nil { + return nil + } + + min, err := getIntGT0(annotations, autoscaling.MinScaleAnnotationKey) + if err != nil { + return err + } + max, err := getIntGT0(annotations, autoscaling.MaxScaleAnnotationKey) + if err != nil { + return err + } + + if max != 0 && max < min { + return &apis.FieldError{ + Message: fmt.Sprintf("%s=%v is less than %s=%v", autoscaling.MaxScaleAnnotationKey, max, autoscaling.MinScaleAnnotationKey, min), + Paths: []string{autoscaling.MaxScaleAnnotationKey, autoscaling.MinScaleAnnotationKey}, + } + } + + return nil +} diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/revision_defaults.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/revision_defaults.go index db4bedfb6f1..2cd42b02e2a 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/revision_defaults.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/revision_defaults.go @@ -1,5 +1,6 @@ /* -Copyright 2017 The Knative Authors +Copyright 2018 The Knative Authors + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -16,17 +17,13 @@ limitations under the License. package v1alpha1 func (r *Revision) SetDefaults() { - // We only set the default ServingState in the context of Revision - // because we want it unspecified in other contexts (e.g. RevisionTemplateSpec). - if r.Spec.ServingState == "" { - r.Spec.ServingState = RevisionServingStateActive - } - r.Spec.SetDefaults() } func (rs *RevisionSpec) SetDefaults() { - if rs.ConcurrencyModel == "" { - rs.ConcurrencyModel = RevisionRequestConcurrencyModelMulti + // When ConcurrencyModel is specified but ContainerConcurrency + // is not (0), use the ConcurrencyModel value. + if rs.ConcurrencyModel == RevisionRequestConcurrencyModelSingle && rs.ContainerConcurrency == 0 { + rs.ContainerConcurrency = 1 } } diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/revision_types.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/revision_types.go index 7bafa1fd914..2cc72974048 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/revision_types.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/revision_types.go @@ -17,14 +17,18 @@ limitations under the License. package v1alpha1 import ( - "encoding/json" - "reflect" + "fmt" + "strconv" "time" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" - buildv1alpha1 "github.com/knative/build/pkg/apis/build/v1alpha1" + "github.com/knative/pkg/apis" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "github.com/knative/pkg/kmeta" + "github.com/knative/serving/pkg/apis/serving" ) // +genclient @@ -51,9 +55,15 @@ type Revision struct { } // Check that Revision can be validated, can be defaulted, and has immutable fields. -var _ Validatable = (*Revision)(nil) -var _ Defaultable = (*Revision)(nil) -var _ HasImmutableFields = (*Revision)(nil) +var _ apis.Validatable = (*Revision)(nil) +var _ apis.Defaultable = (*Revision)(nil) +var _ apis.Immutable = (*Revision)(nil) + +// Check that RevisionStatus may have its conditions managed. +var _ duckv1alpha1.ConditionsAccessor = (*RevisionStatus)(nil) + +// Check that we can create OwnerReferences to a Revision. +var _ kmeta.OwnerRefable = (*Revision)(nil) // RevisionTemplateSpec describes the data a revision should have when created from a template. // Based on: https://github.com/kubernetes/api/blob/e771f807/core/v1/types.go#L3179-L3190 @@ -64,28 +74,29 @@ type RevisionTemplateSpec struct { Spec RevisionSpec `json:"spec,omitempty"` } -// RevisionServingStateType is an enumeration of the levels of serving readiness of the Revision. +// DeprecatedRevisionServingStateType is an enumeration of the levels of serving readiness of the Revision. // See also: https://github.com/knative/serving/blob/master/docs/spec/errors.md#error-conditions-and-reporting -type RevisionServingStateType string +type DeprecatedRevisionServingStateType string const ( // The revision is ready to serve traffic. It should have Kubernetes // resources, and the Istio route should be pointed to the given resources. - RevisionServingStateActive RevisionServingStateType = "Active" + DeprecatedRevisionServingStateActive DeprecatedRevisionServingStateType = "Active" // The revision is not currently serving traffic, but could be made to serve // traffic quickly. It should have Kubernetes resources, but the Istio route // should be pointed to the activator. - RevisionServingStateReserve RevisionServingStateType = "Reserve" + DeprecatedRevisionServingStateReserve DeprecatedRevisionServingStateType = "Reserve" // The revision has been decommissioned and is not needed to serve traffic // anymore. It should not have any Istio routes or Kubernetes resources. // A Revision may be brought out of retirement, but it may take longer than // it would from a "Reserve" state. // Note: currently not set anywhere. See https://github.com/knative/serving/issues/1203 - RevisionServingStateRetired RevisionServingStateType = "Retired" + DeprecatedRevisionServingStateRetired DeprecatedRevisionServingStateType = "Retired" ) // RevisionRequestConcurrencyModelType is an enumeration of the // concurrency models supported by a Revision. +// Deprecated in favor of RevisionContainerConcurrencyType. type RevisionRequestConcurrencyModelType string const ( @@ -99,6 +110,15 @@ const ( RevisionRequestConcurrencyModelMulti RevisionRequestConcurrencyModelType = "Multi" ) +// RevisionContainerConcurrencyType is an integer expressing a number of +// in-flight (concurrent) requests. +type RevisionContainerConcurrencyType int64 + +const ( + // The maximum configurable container concurrency. + RevisionContainerConcurrencyMax RevisionContainerConcurrencyType = 1000 +) + // RevisionSpec holds the desired state of the Revision (from the client). type RevisionSpec struct { // TODO: Generation does not work correctly with CRD. They are scrubbed @@ -108,19 +128,28 @@ type RevisionSpec struct { // +optional Generation int64 `json:"generation,omitempty"` - // ServingState holds a value describing the desired state the Kubernetes + // DeprecatedServingState holds a value describing the desired state the Kubernetes // resources should be in for this Revision. - // Users must not specify this when creating a revision. It is expected - // that the system will manipulate this based on routability and load. + // Users must not specify this when creating a revision. These values are no longer + // updated by the system. // +optional - ServingState RevisionServingStateType `json:"servingState,omitempty"` + DeprecatedServingState DeprecatedRevisionServingStateType `json:"servingState,omitempty"` // ConcurrencyModel specifies the desired concurrency model // (Single or Multi) for the // Revision. Defaults to Multi. + // Deprecated in favor of ContainerConcurrency. // +optional ConcurrencyModel RevisionRequestConcurrencyModelType `json:"concurrencyModel,omitempty"` + // ContainerConcurrency specifies the maximum allowed + // in-flight (concurrent) requests per container of the Revision. + // Defaults to `0` which means unlimited concurrency. + // This field replaces ConcurrencyModel. A value of `1` + // is equivalent to `Single` and `0` is equivalent to `Multi`. + // +optional + ContainerConcurrency RevisionContainerConcurrencyType `json:"containerConcurrency,omitempty"` + // ServiceAccountName holds the name of the Kubernetes service account // as which the underlying K8s resources should be run. If unspecified // this will default to the "default" service account for the namespace @@ -133,9 +162,15 @@ type RevisionSpec struct { // BuildName optionally holds the name of the Build responsible for // producing the container image for its Revision. + // DEPRECATED: Use BuildRef instead. // +optional BuildName string `json:"buildName,omitempty"` + // BuildRef holds the reference to the build (if there is one) responsible + // for producing the container image for this Revision. Otherwise, nil + // +optional + BuildRef *corev1.ObjectReference `json:"buildRef,omitempty"` + // Container defines the unit of execution for this Revision. // In the context of a Revision, we disallow a number of the fields of // this Container, including: name, resources, ports, and volumeMounts. @@ -145,40 +180,29 @@ type RevisionSpec struct { Container corev1.Container `json:"container,omitempty"` } -// RevisionConditionType is used to communicate the status of the reconciliation process. -// See also: https://github.com/knative/serving/blob/master/docs/spec/errors.md#error-conditions-and-reporting -type RevisionConditionType string - const ( // RevisionConditionReady is set when the revision is starting to materialize // runtime resources, and becomes true when those resources are ready. - RevisionConditionReady RevisionConditionType = "Ready" - // RevisionConditionBuildComplete is set when the revision has an associated build - // and is marked True if/once the Build has completed succesfully. - RevisionConditionBuildSucceeded RevisionConditionType = "BuildSucceeded" + RevisionConditionReady = duckv1alpha1.ConditionReady + // RevisionConditionBuildSucceeded is set when the revision has an associated build + // and is marked True if/once the Build has completed successfully. + RevisionConditionBuildSucceeded duckv1alpha1.ConditionType = "BuildSucceeded" // RevisionConditionResourcesAvailable is set when underlying // Kubernetes resources have been provisioned. - RevisionConditionResourcesAvailable RevisionConditionType = "ResourcesAvailable" + RevisionConditionResourcesAvailable duckv1alpha1.ConditionType = "ResourcesAvailable" // RevisionConditionContainerHealthy is set when the revision readiness check completes. - RevisionConditionContainerHealthy RevisionConditionType = "ContainerHealthy" + RevisionConditionContainerHealthy duckv1alpha1.ConditionType = "ContainerHealthy" + // RevisionConditionActive is set when the revision is receiving traffic. + RevisionConditionActive duckv1alpha1.ConditionType = "Active" ) -// RevisionCondition defines a readiness condition for a Revision. -// See: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#typical-status-properties -type RevisionCondition struct { - Type RevisionConditionType `json:"type" description:"type of Revision condition"` - - Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` - - // +optional - LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" description:"last time the condition transit from one status to another"` - - // +optional - Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"` +var revCondSet = duckv1alpha1.NewLivingConditionSet( + RevisionConditionResourcesAvailable, + RevisionConditionContainerHealthy, + RevisionConditionActive, +) - // +optional - Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"` -} +var buildCondSet = duckv1alpha1.NewBatchConditionSet() // RevisionStatus communicates the observed state of the Revision (from the controller). type RevisionStatus struct { @@ -193,7 +217,7 @@ type RevisionStatus struct { // reconciliation processes that bring the "spec" inline with the observed // state of the world. // +optional - Conditions []RevisionCondition `json:"conditions,omitempty"` + Conditions duckv1alpha1.Conditions `json:"conditions,omitempty"` // ObservedGeneration is the 'Generation' of the Configuration that // was last processed by the controller. The observed generation is updated @@ -205,6 +229,14 @@ type RevisionStatus struct { // based on the revision url template specified in the controller's config. // +optional LogURL string `json:"logUrl,omitempty"` + + // ImageDigest holds the resolved digest for the image specified + // within .Spec.Container.Image. The digest is resolved during the creation + // of Revision. This field holds the digest value regardless of whether + // a tag or digest was originally specified in the Container object. It + // may be empty if the image comes from a registry listed to skip resolution. + // +optional + ImageDigest string `json:"imageDigest,omitempty"` } // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object @@ -217,31 +249,40 @@ type RevisionList struct { Items []Revision `json:"items"` } -func (r *Revision) GetGeneration() int64 { - return r.Spec.Generation +func (r *Revision) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("Revision") } -func (r *Revision) SetGeneration(generation int64) { - r.Spec.Generation = generation -} +func (r *Revision) BuildRef() *corev1.ObjectReference { + if r.Spec.BuildRef != nil { + buildRef := r.Spec.BuildRef.DeepCopy() + if buildRef.Namespace == "" { + buildRef.Namespace = r.Namespace + } + return buildRef + } -func (r *Revision) GetSpecJSON() ([]byte, error) { - return json.Marshal(r.Spec) + if r.Spec.BuildName != "" { + return &corev1.ObjectReference{ + APIVersion: "build.knative.dev/v1alpha1", + Kind: "Build", + Namespace: r.Namespace, + Name: r.Spec.BuildName, + } + } + + return nil } // IsReady looks at the conditions and if the Status has a condition // RevisionConditionReady returns true if ConditionStatus is True func (rs *RevisionStatus) IsReady() bool { - if c := rs.GetCondition(RevisionConditionReady); c != nil { - return c.Status == corev1.ConditionTrue - } - return false + return revCondSet.Manage(rs).IsHappy() } func (rs *RevisionStatus) IsActivationRequired() bool { - if c := rs.GetCondition(RevisionConditionReady); c != nil { - return (c.Reason == "Inactive" && c.Status == corev1.ConditionFalse) || - (c.Reason == "Updating" && c.Status == corev1.ConditionUnknown) + if c := revCondSet.Manage(rs).GetCondition(RevisionConditionActive); c != nil { + return c.Status != corev1.ConditionTrue } return false } @@ -250,194 +291,173 @@ func (rs *RevisionStatus) IsRoutable() bool { return rs.IsReady() || rs.IsActivationRequired() } -func (rs *RevisionStatus) GetCondition(t RevisionConditionType) *RevisionCondition { - for _, cond := range rs.Conditions { - if cond.Type == t { - return &cond - } - } - return nil -} - -func (rs *RevisionStatus) setCondition(new *RevisionCondition) { - if new == nil { - return - } - - t := new.Type - var conditions []RevisionCondition - for _, cond := range rs.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } else { - // If we'd only update the LastTransitionTime, then return. - new.LastTransitionTime = cond.LastTransitionTime - if reflect.DeepEqual(new, &cond) { - return - } - } - } - new.LastTransitionTime = metav1.NewTime(time.Now()) - conditions = append(conditions, *new) - rs.Conditions = conditions -} - -func (rs *RevisionStatus) RemoveCondition(t RevisionConditionType) { - var conditions []RevisionCondition - for _, cond := range rs.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - rs.Conditions = conditions +func (rs *RevisionStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { + return revCondSet.Manage(rs).GetCondition(t) } func (rs *RevisionStatus) InitializeConditions() { + revCondSet.Manage(rs).InitializeConditions() + // We don't include BuildSucceeded here because it could confuse users if // no `buildName` was specified. - for _, cond := range []RevisionConditionType{ - RevisionConditionResourcesAvailable, - RevisionConditionContainerHealthy, - RevisionConditionReady, - } { - if rc := rs.GetCondition(cond); rc == nil { - rs.setCondition(&RevisionCondition{ - Type: cond, - Status: corev1.ConditionUnknown, - }) - } - } } func (rs *RevisionStatus) InitializeBuildCondition() { - if rc := rs.GetCondition(RevisionConditionBuildSucceeded); rc == nil { - rs.setCondition(&RevisionCondition{ - Type: RevisionConditionBuildSucceeded, - Status: corev1.ConditionUnknown, - }) - } + revCondSet.Manage(rs).InitializeCondition(RevisionConditionBuildSucceeded) } -func (rs *RevisionStatus) PropagateBuildStatus(bs buildv1alpha1.BuildStatus) { - bc := bs.GetCondition(buildv1alpha1.BuildSucceeded) +func (rs *RevisionStatus) PropagateBuildStatus(bs duckv1alpha1.KResourceStatus) { + bc := buildCondSet.Manage(&bs).GetCondition(duckv1alpha1.ConditionSucceeded) if bc == nil { return } - rct := []RevisionConditionType{RevisionConditionBuildSucceeded} - // If the underlying Build is not ready, then mark the Revision not ready. - if bc.Status != corev1.ConditionTrue { - rct = append(rct, RevisionConditionReady) - } - reason := "Building" - if bc.Status != corev1.ConditionUnknown { - reason = bc.Reason - } - for _, cond := range rct { - rs.setCondition(&RevisionCondition{ - Type: cond, - Status: bc.Status, - Reason: reason, - Message: bc.Message, - }) + switch { + case bc.Status == corev1.ConditionUnknown: + revCondSet.Manage(rs).MarkUnknown(RevisionConditionBuildSucceeded, "Building", bc.Message) + case bc.Status == corev1.ConditionTrue: + revCondSet.Manage(rs).MarkTrue(RevisionConditionBuildSucceeded) + case bc.Status == corev1.ConditionFalse: + revCondSet.Manage(rs).MarkFalse(RevisionConditionBuildSucceeded, bc.Reason, bc.Message) } } func (rs *RevisionStatus) MarkDeploying(reason string) { - for _, cond := range []RevisionConditionType{ - RevisionConditionResourcesAvailable, - RevisionConditionContainerHealthy, - RevisionConditionReady, - } { - rs.setCondition(&RevisionCondition{ - Type: cond, - Status: corev1.ConditionUnknown, - Reason: reason, - }) - } + revCondSet.Manage(rs).MarkUnknown(RevisionConditionResourcesAvailable, reason, "") + revCondSet.Manage(rs).MarkUnknown(RevisionConditionContainerHealthy, reason, "") } func (rs *RevisionStatus) MarkServiceTimeout() { - for _, cond := range []RevisionConditionType{ - RevisionConditionResourcesAvailable, - RevisionConditionReady, - } { - rs.setCondition(&RevisionCondition{ - Type: cond, - Status: corev1.ConditionFalse, - Reason: "ServiceTimeout", - Message: "Timed out waiting for a service endpoint to become ready", - }) - } + revCondSet.Manage(rs).MarkFalse(RevisionConditionResourcesAvailable, "ServiceTimeout", + "Timed out waiting for a service endpoint to become ready") } func (rs *RevisionStatus) MarkProgressDeadlineExceeded(message string) { - for _, cond := range []RevisionConditionType{ - RevisionConditionResourcesAvailable, - RevisionConditionReady, - } { - rs.setCondition(&RevisionCondition{ - Type: cond, - Status: corev1.ConditionFalse, - Reason: "ProgressDeadlineExceeded", - Message: message, - }) - } + revCondSet.Manage(rs).MarkFalse(RevisionConditionResourcesAvailable, "ProgressDeadlineExceeded", message) } func (rs *RevisionStatus) MarkContainerHealthy() { - rs.setCondition(&RevisionCondition{ - Type: RevisionConditionContainerHealthy, - Status: corev1.ConditionTrue, - }) - rs.checkAndMarkReady() + revCondSet.Manage(rs).MarkTrue(RevisionConditionContainerHealthy) } func (rs *RevisionStatus) MarkResourcesAvailable() { - rs.setCondition(&RevisionCondition{ - Type: RevisionConditionResourcesAvailable, - Status: corev1.ConditionTrue, - }) - rs.checkAndMarkReady() + revCondSet.Manage(rs).MarkTrue(RevisionConditionResourcesAvailable) +} + +func (rs *RevisionStatus) MarkActive() { + revCondSet.Manage(rs).MarkTrue(RevisionConditionActive) } -func (rs *RevisionStatus) MarkInactive() { - rs.setCondition(&RevisionCondition{ - Type: RevisionConditionReady, - Status: corev1.ConditionFalse, - Reason: "Inactive", - }) +func (rs *RevisionStatus) MarkActivating(reason, message string) { + revCondSet.Manage(rs).MarkUnknown(RevisionConditionActive, reason, message) +} + +func (rs *RevisionStatus) MarkInactive(reason, message string) { + revCondSet.Manage(rs).MarkFalse(RevisionConditionActive, reason, message) } func (rs *RevisionStatus) MarkContainerMissing(message string) { - for _, cond := range []RevisionConditionType{ - RevisionConditionContainerHealthy, - RevisionConditionReady, - } { - rs.setCondition(&RevisionCondition{ - Type: cond, - Status: corev1.ConditionFalse, - Reason: "ContainerMissing", - Message: message, - }) + revCondSet.Manage(rs).MarkFalse(RevisionConditionContainerHealthy, "ContainerMissing", message) +} + +// GetConditions returns the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (rs *RevisionStatus) GetConditions() duckv1alpha1.Conditions { + return rs.Conditions +} + +// SetConditions sets the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (rs *RevisionStatus) SetConditions(conditions duckv1alpha1.Conditions) { + rs.Conditions = conditions +} + +const ( + AnnotationParseErrorTypeMissing = "Missing" + AnnotationParseErrorTypeInvalid = "Invalid" +) + +// +k8s:deepcopy-gen=false +type AnnotationParseError struct { + Type string + Value string + Err error +} + +// +k8s:deepcopy-gen=false +type LastPinnedParseError AnnotationParseError + +func (e LastPinnedParseError) Error() string { + return fmt.Sprintf("%v lastPinned value: %q", e.Type, e.Value) +} + +// +k8s:deepcopy-gen=false +type configurationGenerationParseError AnnotationParseError + +func (e configurationGenerationParseError) Error() string { + return fmt.Sprintf("%v configurationGeneration value: %q", e.Type, e.Value) +} + +func RevisionLastPinnedString(t time.Time) string { + return fmt.Sprintf("%d", t.Unix()) +} + +func (r *Revision) SetLastPinned(t time.Time) { + if r.ObjectMeta.Annotations == nil { + r.ObjectMeta.Annotations = make(map[string]string) } + + r.ObjectMeta.Annotations[serving.RevisionLastPinnedAnnotationKey] = RevisionLastPinnedString(t) } -func (rs *RevisionStatus) checkAndMarkReady() { - for _, cond := range []RevisionConditionType{ - RevisionConditionContainerHealthy, - RevisionConditionResourcesAvailable, - } { - c := rs.GetCondition(cond) - if c == nil || c.Status != corev1.ConditionTrue { - return +func (r *Revision) GetLastPinned() (time.Time, error) { + if r.Annotations == nil { + return time.Time{}, LastPinnedParseError{ + Type: AnnotationParseErrorTypeMissing, + } + } + + str, ok := r.ObjectMeta.Annotations[serving.RevisionLastPinnedAnnotationKey] + if !ok { + // If a revision is past the create delay without an annotation it is stale + return time.Time{}, LastPinnedParseError{ + Type: AnnotationParseErrorTypeMissing, + } + } + + secs, err := strconv.ParseInt(str, 10, 64) + if err != nil { + return time.Time{}, LastPinnedParseError{ + Type: AnnotationParseErrorTypeInvalid, + Value: str, + Err: err, } } - rs.markReady() + + return time.Unix(secs, 0), nil } -func (rs *RevisionStatus) markReady() { - rs.setCondition(&RevisionCondition{ - Type: RevisionConditionReady, - Status: corev1.ConditionTrue, - }) +func (r *Revision) GetConfigurationGeneration() (int64, error) { + if r.Annotations == nil { + return 0, configurationGenerationParseError{ + Type: AnnotationParseErrorTypeMissing, + } + } + + str, ok := r.ObjectMeta.Annotations[serving.ConfigurationGenerationAnnotationKey] + if !ok { + return 0, configurationGenerationParseError{ + Type: AnnotationParseErrorTypeMissing, + } + } + + gen, err := strconv.ParseInt(str, 10, 64) + if err != nil { + return 0, configurationGenerationParseError{ + Type: AnnotationParseErrorTypeInvalid, + Value: str, + Err: err, + } + } + + return gen, nil } diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/revision_validation.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/revision_validation.go index fc90195fce8..882682d939d 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/revision_validation.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/revision_validation.go @@ -1,5 +1,6 @@ /* -Copyright 2017 The Knative Authors +Copyright 2018 The Knative Authors + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -16,60 +17,87 @@ limitations under the License. package v1alpha1 import ( + "strconv" + "github.com/google/go-cmp/cmp" - "github.com/google/go-cmp/cmp/cmpopts" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/util/validation" + + "github.com/knative/pkg/apis" ) -func (rt *Revision) Validate() *FieldError { - return rt.Spec.Validate().ViaField("spec") +func (rt *Revision) Validate() *apis.FieldError { + return ValidateObjectMetadata(rt.GetObjectMeta()).ViaField("metadata"). + Also(rt.Spec.Validate().ViaField("spec")) } -func (rt *RevisionTemplateSpec) Validate() *FieldError { +func (rt *RevisionTemplateSpec) Validate() *apis.FieldError { return rt.Spec.Validate().ViaField("spec") } -func (rs *RevisionSpec) Validate() *FieldError { +func (rs *RevisionSpec) Validate() *apis.FieldError { if equality.Semantic.DeepEqual(rs, &RevisionSpec{}) { - return errMissingField(currentField) + return apis.ErrMissingField(apis.CurrentField) } - if err := rs.ServingState.Validate(); err != nil { - return err.ViaField("servingState") - } - if err := validateContainer(rs.Container); err != nil { - return err.ViaField("container") + errs := validateContainer(rs.Container).ViaField("container"). + Also(validateBuildRef(rs.BuildRef).ViaField("buildRef")) + + if err := rs.ConcurrencyModel.Validate().ViaField("concurrencyModel"); err != nil { + errs = errs.Also(err) + } else if err := ValidateContainerConcurrency(rs.ContainerConcurrency, rs.ConcurrencyModel); err != nil { + errs = errs.Also(err) } - return rs.ConcurrencyModel.Validate().ViaField("concurrencyModel") + return errs } -func (ss RevisionServingStateType) Validate() *FieldError { +func (ss DeprecatedRevisionServingStateType) Validate() *apis.FieldError { switch ss { - case RevisionServingStateType(""), - RevisionServingStateRetired, - RevisionServingStateReserve, - RevisionServingStateActive: + case DeprecatedRevisionServingStateType(""), + DeprecatedRevisionServingStateRetired, + DeprecatedRevisionServingStateReserve, + DeprecatedRevisionServingStateActive: return nil default: - return errInvalidValue(string(ss), currentField) + return apis.ErrInvalidValue(string(ss), apis.CurrentField) } } -func (cm RevisionRequestConcurrencyModelType) Validate() *FieldError { +func (cm RevisionRequestConcurrencyModelType) Validate() *apis.FieldError { switch cm { case RevisionRequestConcurrencyModelType(""), RevisionRequestConcurrencyModelMulti, RevisionRequestConcurrencyModelSingle: return nil default: - return errInvalidValue(string(cm), currentField) + return apis.ErrInvalidValue(string(cm), apis.CurrentField) } } -func validateContainer(container corev1.Container) *FieldError { +func ValidateContainerConcurrency(cc RevisionContainerConcurrencyType, cm RevisionRequestConcurrencyModelType) *apis.FieldError { + // Validate ContainerConcurrency alone + if cc < 0 || cc > RevisionContainerConcurrencyMax { + return apis.ErrInvalidValue(strconv.Itoa(int(cc)), "containerConcurrency") + } + + // Validate combinations of ConcurrencyModel and ContainerConcurrency + if cc == 0 && cm != RevisionRequestConcurrencyModelMulti && cm != RevisionRequestConcurrencyModelType("") { + return apis.ErrMultipleOneOf("containerConcurrency", "concurrencyModel") + } + if cc == 1 && cm != RevisionRequestConcurrencyModelSingle && cm != RevisionRequestConcurrencyModelType("") { + return apis.ErrMultipleOneOf("containerConcurrency", "concurrencyModel") + } + if cc > 1 && cm != RevisionRequestConcurrencyModelType("") { + return apis.ErrMultipleOneOf("containerConcurrency", "concurrencyModel") + } + + return nil +} + +func validateContainer(container corev1.Container) *apis.FieldError { if equality.Semantic.DeepEqual(container, corev1.Container{}) { - return errMissingField(currentField) + return apis.ErrMissingField(apis.CurrentField) } // Some corev1.Container fields are set by Knative Serving controller. We disallow them // here to avoid silently overwriting these fields and causing confusions for @@ -90,21 +118,54 @@ func validateContainer(container corev1.Container) *FieldError { if container.Lifecycle != nil { ignoredFields = append(ignoredFields, "lifecycle") } + var errs *apis.FieldError if len(ignoredFields) > 0 { // Complain about all ignored fields so that user can remove them all at once. - return errDisallowedFields(ignoredFields...) + errs = errs.Also(apis.ErrDisallowedFields(ignoredFields...)) } // Validate our probes - if err := validateProbe(container.ReadinessProbe); err != nil { - return err.ViaField("readinessProbe") + if err := validateProbe(container.ReadinessProbe).ViaField("readinessProbe"); err != nil { + errs = errs.Also(err) + } + if err := validateProbe(container.LivenessProbe).ViaField("livenessProbe"); err != nil { + errs = errs.Also(err) + } + return errs +} + +func validateBuildRef(buildRef *corev1.ObjectReference) *apis.FieldError { + if buildRef == nil { + return nil + } + if len(validation.IsQualifiedName(buildRef.APIVersion)) != 0 { + return apis.ErrInvalidValue(buildRef.APIVersion, "apiVersion") + } + if len(validation.IsCIdentifier(buildRef.Kind)) != 0 { + return apis.ErrInvalidValue(buildRef.Kind, "kind") + } + if len(validation.IsDNS1123Label(buildRef.Name)) != 0 { + return apis.ErrInvalidValue(buildRef.Name, "name") + } + var disallowedFields []string + if buildRef.Namespace != "" { + disallowedFields = append(disallowedFields, "namespace") + } + if buildRef.FieldPath != "" { + disallowedFields = append(disallowedFields, "fieldPath") + } + if buildRef.ResourceVersion != "" { + disallowedFields = append(disallowedFields, "resourceVersion") + } + if buildRef.UID != "" { + disallowedFields = append(disallowedFields, "uid") } - if err := validateProbe(container.LivenessProbe); err != nil { - return err.ViaField("livenessProbe") + if len(disallowedFields) != 0 { + return apis.ErrDisallowedFields(disallowedFields...) } return nil } -func validateProbe(p *corev1.Probe) *FieldError { +func validateProbe(p *corev1.Probe) *apis.FieldError { if p == nil { return nil } @@ -112,26 +173,24 @@ func validateProbe(p *corev1.Probe) *FieldError { switch { case p.Handler.HTTPGet != nil: if p.Handler.HTTPGet.Port != emptyPort { - return errDisallowedFields("httpGet.port") + return apis.ErrDisallowedFields("httpGet.port") } case p.Handler.TCPSocket != nil: if p.Handler.TCPSocket.Port != emptyPort { - return errDisallowedFields("tcpSocket.port") + return apis.ErrDisallowedFields("tcpSocket.port") } } return nil } -func (current *Revision) CheckImmutableFields(og HasImmutableFields) *FieldError { +func (current *Revision) CheckImmutableFields(og apis.Immutable) *apis.FieldError { original, ok := og.(*Revision) if !ok { - return &FieldError{Message: "The provided original was not a Revision"} + return &apis.FieldError{Message: "The provided original was not a Revision"} } - // The autoscaler is allowed to change ServingState, but consider the rest. - ignoreServingState := cmpopts.IgnoreFields(RevisionSpec{}, "ServingState") - if diff := cmp.Diff(original.Spec, current.Spec, ignoreServingState); diff != "" { - return &FieldError{ + if diff := cmp.Diff(original.Spec, current.Spec); diff != "" { + return &apis.FieldError{ Message: "Immutable fields changed (-old +new)", Paths: []string{"spec"}, Details: diff, diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/route_defaults.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/route_defaults.go index a385269be46..c9f0ec3bdc9 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/route_defaults.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/route_defaults.go @@ -1,5 +1,6 @@ /* -Copyright 2017 The Knative Authors +Copyright 2018 The Knative Authors + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/route_types.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/route_types.go index 3a33ad03703..0dc3d678972 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/route_types.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/route_types.go @@ -17,13 +17,14 @@ limitations under the License. package v1alpha1 import ( - "encoding/json" - "fmt" - "reflect" - "time" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/knative/pkg/apis" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "github.com/knative/pkg/kmeta" + "github.com/knative/serving/pkg/apis/networking/v1alpha1" ) // +genclient @@ -50,8 +51,14 @@ type Route struct { } // Check that Route may be validated and defaulted. -var _ Validatable = (*Route)(nil) -var _ Defaultable = (*Route)(nil) +var _ apis.Validatable = (*Route)(nil) +var _ apis.Defaultable = (*Route)(nil) + +// Check that we can create OwnerReferences to a Route. +var _ kmeta.OwnerRefable = (*Route)(nil) + +// Check that RouteStatus may have its conditions managed. +var _ duckv1alpha1.ConditionsAccessor = (*RouteStatus)(nil) // TrafficTarget holds a single entry of the routing table for a Route. type TrafficTarget struct { @@ -93,37 +100,23 @@ type RouteSpec struct { Traffic []TrafficTarget `json:"traffic,omitempty"` } -// RouteCondition defines a readiness condition. -// See: https://github.com/kubernetes/community/blob/master/contributors/devel/api-conventions.md#typical-status-properties -type RouteCondition struct { - Type RouteConditionType `json:"type"` - - Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` - - // +optional - LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" description:"last time the condition transit from one status to another"` - - // +optional - Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"` - // +optional - Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"` -} - -// RouteConditionType is used to communicate the status of the reconciliation process. -// See also: https://github.com/knative/serving/blob/master/docs/spec/errors.md#error-conditions-and-reporting -type RouteConditionType string - const ( // RouteConditionReady is set when the service is configured // and has available backends ready to receive traffic. - RouteConditionReady RouteConditionType = "Ready" + RouteConditionReady = duckv1alpha1.ConditionReady // RouteConditionAllTrafficAssigned is set to False when the // service is not configured properly or has no available // backends ready to receive traffic. - RouteConditionAllTrafficAssigned RouteConditionType = "AllTrafficAssigned" + RouteConditionAllTrafficAssigned duckv1alpha1.ConditionType = "AllTrafficAssigned" + + // RouteConditionIngressReady is set to False when the + // ClusterIngress fails to become Ready. + RouteConditionIngressReady duckv1alpha1.ConditionType = "IngressReady" ) +var routeCondSet = duckv1alpha1.NewLivingConditionSet(RouteConditionAllTrafficAssigned, RouteConditionIngressReady) + // RouteStatus communicates the observed state of the Route (from the controller). type RouteStatus struct { // Domain holds the top-level domain that will distribute traffic over the provided targets. @@ -134,9 +127,14 @@ type RouteStatus struct { // DomainInternal holds the top-level domain that will distribute traffic over the provided // targets from inside the cluster. It generally has the form // {route-name}.{route-namespace}.svc.cluster.local + // DEPRECATED: Use Address instead. // +optional DomainInternal string `json:"domainInternal,omitempty"` + // Address holds the information needed for a Route to be the target of an event. + // +optional + Address *duckv1alpha1.Addressable `json:"address,omitempty"` + // Traffic holds the configured traffic distribution. // These entries will always contain RevisionName references. // When ConfigurationName appears in the spec, this will hold the @@ -148,7 +146,7 @@ type RouteStatus struct { // reconciliation processes that bring the "spec" inline with the observed // state of the world. // +optional - Conditions []RouteCondition `json:"conditions,omitempty"` + Conditions duckv1alpha1.Conditions `json:"conditions,omitempty"` // ObservedGeneration is the 'Generation' of the Configuration that // was last processed by the controller. The observed generation is updated @@ -167,177 +165,85 @@ type RouteList struct { Items []Route `json:"items"` } -func (r *Route) GetGeneration() int64 { - return r.Spec.Generation -} - -func (r *Route) SetGeneration(generation int64) { - r.Spec.Generation = generation -} - -func (r *Route) GetSpecJSON() ([]byte, error) { - return json.Marshal(r.Spec) +func (r *Route) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("Route") } func (rs *RouteStatus) IsReady() bool { - if c := rs.GetCondition(RouteConditionReady); c != nil { - return c.Status == corev1.ConditionTrue - } - return false + return routeCondSet.Manage(rs).IsHappy() } -func (rs *RouteStatus) GetCondition(t RouteConditionType) *RouteCondition { - for _, cond := range rs.Conditions { - if cond.Type == t { - return &cond - } - } - return nil -} - -func (rs *RouteStatus) setCondition(new *RouteCondition) { - if new == nil { - return - } - - t := new.Type - var conditions []RouteCondition - for _, cond := range rs.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } else { - // If we'd only update the LastTransitionTime, then return. - new.LastTransitionTime = cond.LastTransitionTime - if reflect.DeepEqual(new, &cond) { - return - } - } - } - new.LastTransitionTime = metav1.NewTime(time.Now()) - conditions = append(conditions, *new) - rs.Conditions = conditions -} - -func (rs *RouteStatus) RemoveCondition(t RouteConditionType) { - var conditions []RouteCondition - for _, cond := range rs.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - rs.Conditions = conditions +func (rs *RouteStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { + return routeCondSet.Manage(rs).GetCondition(t) } func (rs *RouteStatus) InitializeConditions() { - for _, cond := range []RouteConditionType{ - RouteConditionAllTrafficAssigned, - RouteConditionReady, - } { - if rc := rs.GetCondition(cond); rc == nil { - rs.setCondition(&RouteCondition{ - Type: cond, - Status: corev1.ConditionUnknown, - }) - } - } + routeCondSet.Manage(rs).InitializeConditions() } func (rs *RouteStatus) MarkTrafficAssigned() { - rs.setCondition(&RouteCondition{ - Type: RouteConditionAllTrafficAssigned, - Status: corev1.ConditionTrue, - }) - rs.checkAndMarkReady() -} - -func (rs *RouteStatus) markTrafficTargetNotReady(reason, msg string) { - rs.setCondition(&RouteCondition{ - Type: RouteConditionAllTrafficAssigned, - Status: corev1.ConditionUnknown, - Reason: reason, - Message: msg, - }) - // TODO(tcnghia): when we start with new RouteConditionReady every revision, - // uncomment the short-circuiting below. - // - // // Do not downgrade Ready condition. - // if c := rs.GetCondition(RouteConditionReady); c != nil && c.Status == corev1.ConditionFalse { - // return - // } - // - // For now, the following is harmless because RouteConditionAllTrafficAssigned - // is the only condition RouteConditionReady depends on. - rs.setCondition(&RouteCondition{ - Type: RouteConditionReady, - Status: corev1.ConditionUnknown, - Reason: reason, - Message: msg, - }) -} - -func (rs *RouteStatus) markTrafficTargetFailed(reason, msg string) { - for _, cond := range []RouteConditionType{ - RouteConditionAllTrafficAssigned, - RouteConditionReady, - } { - rs.setCondition(&RouteCondition{ - Type: cond, - Status: corev1.ConditionFalse, - Reason: reason, - Message: msg, - }) - } + routeCondSet.Manage(rs).MarkTrue(RouteConditionAllTrafficAssigned) } func (rs *RouteStatus) MarkUnknownTrafficError(msg string) { - rs.markTrafficTargetNotReady("Unknown", msg) + routeCondSet.Manage(rs).MarkUnknown(RouteConditionAllTrafficAssigned, "Unknown", msg) } func (rs *RouteStatus) MarkConfigurationNotReady(name string) { - reason := "RevisionMissing" - msg := fmt.Sprintf("Configuration %q is waiting for a Revision to become ready.", name) - rs.markTrafficTargetNotReady(reason, msg) + routeCondSet.Manage(rs).MarkUnknown(RouteConditionAllTrafficAssigned, + "RevisionMissing", + "Configuration %q is waiting for a Revision to become ready.", name) } func (rs *RouteStatus) MarkConfigurationFailed(name string) { - reason := "RevisionMissing" - msg := fmt.Sprintf("Configuration %q does not have any ready Revision.", name) - rs.markTrafficTargetFailed(reason, msg) + routeCondSet.Manage(rs).MarkFalse(RouteConditionAllTrafficAssigned, + "RevisionMissing", + "Configuration %q does not have any ready Revision.", name) } func (rs *RouteStatus) MarkRevisionNotReady(name string) { - reason := "RevisionMissing" - msg := fmt.Sprintf("Revision %q is not yet ready.", name) - rs.markTrafficTargetNotReady(reason, msg) + routeCondSet.Manage(rs).MarkUnknown(RouteConditionAllTrafficAssigned, + "RevisionMissing", + "Revision %q is not yet ready.", name) } func (rs *RouteStatus) MarkRevisionFailed(name string) { - reason := "RevisionMissing" - msg := fmt.Sprintf("Revision %q failed to become ready.", name) - rs.markTrafficTargetFailed(reason, msg) + routeCondSet.Manage(rs).MarkFalse(RouteConditionAllTrafficAssigned, + "RevisionMissing", + "Revision %q failed to become ready.", name) } func (rs *RouteStatus) MarkMissingTrafficTarget(kind, name string) { - reason := kind + "Missing" - msg := fmt.Sprintf("%s %q referenced in traffic not found.", kind, name) - rs.markTrafficTargetFailed(reason, msg) + routeCondSet.Manage(rs).MarkFalse(RouteConditionAllTrafficAssigned, + kind+"Missing", + "%s %q referenced in traffic not found.", kind, name) } -func (rs *RouteStatus) checkAndMarkReady() { - for _, cond := range []RouteConditionType{ - RouteConditionAllTrafficAssigned, - } { - ata := rs.GetCondition(cond) - if ata == nil || ata.Status != corev1.ConditionTrue { - return - } +// PropagateClusterIngressStatus update RouteConditionIngressReady condition +// in RouteStatus according to IngressStatus. +func (rs *RouteStatus) PropagateClusterIngressStatus(cs v1alpha1.IngressStatus) { + cc := cs.GetCondition(v1alpha1.ClusterIngressConditionReady) + if cc == nil { + return + } + switch { + case cc.Status == corev1.ConditionUnknown: + routeCondSet.Manage(rs).MarkUnknown(RouteConditionIngressReady, cc.Reason, cc.Message) + case cc.Status == corev1.ConditionTrue: + routeCondSet.Manage(rs).MarkTrue(RouteConditionIngressReady) + case cc.Status == corev1.ConditionFalse: + routeCondSet.Manage(rs).MarkFalse(RouteConditionIngressReady, cc.Reason, cc.Message) } - rs.markReady() } -func (rs *RouteStatus) markReady() { - rs.setCondition(&RouteCondition{ - Type: RouteConditionReady, - Status: corev1.ConditionTrue, - }) +// GetConditions returns the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (rs *RouteStatus) GetConditions() duckv1alpha1.Conditions { + return rs.Conditions +} + +// SetConditions sets the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (rs *RouteStatus) SetConditions(conditions duckv1alpha1.Conditions) { + rs.Conditions = conditions } diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/route_validation.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/route_validation.go index a4356d897e3..58f311fde59 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/route_validation.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/route_validation.go @@ -1,5 +1,6 @@ /* -Copyright 2017 The Knative Authors +Copyright 2018 The Knative Authors + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -19,15 +20,19 @@ import ( "fmt" "k8s.io/apimachinery/pkg/api/equality" + + "github.com/knative/pkg/apis" + "k8s.io/apimachinery/pkg/util/validation" ) -func (rt *Route) Validate() *FieldError { - return rt.Spec.Validate().ViaField("spec") +func (r *Route) Validate() *apis.FieldError { + return ValidateObjectMetadata(r.GetObjectMeta()).ViaField("metadata"). + Also(r.Spec.Validate().ViaField("spec")) } -func (rs *RouteSpec) Validate() *FieldError { +func (rs *RouteSpec) Validate() *apis.FieldError { if equality.Semantic.DeepEqual(rs, &RouteSpec{}) { - return errMissingField(currentField) + return apis.ErrMissingField(apis.CurrentField) } // Where a named traffic target points @@ -40,11 +45,11 @@ func (rs *RouteSpec) Validate() *FieldError { // Track the targets of named TrafficTarget entries (to detect duplicates). trafficMap := make(map[string]namedTarget) + var errs *apis.FieldError percentSum := 0 for i, tt := range rs.Traffic { - if err := tt.Validate(); err != nil { - return err.ViaField(fmt.Sprintf("traffic[%d]", i)) - } + errs = errs.Also(tt.Validate().ViaFieldIndex("traffic", i)) + percentSum += tt.Percent if tt.Name == "" { @@ -60,43 +65,43 @@ func (rs *RouteSpec) Validate() *FieldError { // No entry exists, so add ours trafficMap[tt.Name] = nt } else if ent.r != nt.r || ent.c != nt.c { - return &FieldError{ + errs = errs.Also(&apis.FieldError{ Message: fmt.Sprintf("Multiple definitions for %q", tt.Name), Paths: []string{ fmt.Sprintf("traffic[%d].name", ent.i), fmt.Sprintf("traffic[%d].name", nt.i), }, - } + }) } } if percentSum != 100 { - return &FieldError{ + errs = errs.Also(&apis.FieldError{ Message: fmt.Sprintf("Traffic targets sum to %d, want 100", percentSum), Paths: []string{"traffic"}, - } + }) } - return nil + return errs } -func (tt *TrafficTarget) Validate() *FieldError { +func (tt *TrafficTarget) Validate() *apis.FieldError { + var errs *apis.FieldError switch { case tt.RevisionName != "" && tt.ConfigurationName != "": - return &FieldError{ - Message: "Expected exactly one, got both", - Paths: []string{"revisionName", "configurationName"}, - } + errs = apis.ErrMultipleOneOf("revisionName", "configurationName") case tt.RevisionName != "": + if verrs := validation.IsQualifiedName(tt.RevisionName); len(verrs) > 0 { + errs = apis.ErrInvalidKeyName(tt.RevisionName, "revisionName", verrs...) + } case tt.ConfigurationName != "": - // These are fine. - default: - return &FieldError{ - Message: "Expected exactly one, got neither", - Paths: []string{"revisionName", "configurationName"}, + if verrs := validation.IsQualifiedName(tt.ConfigurationName); len(verrs) > 0 { + errs = apis.ErrInvalidKeyName(tt.ConfigurationName, "configurationName", verrs...) } + default: + errs = apis.ErrMissingOneOf("revisionName", "configurationName") } if tt.Percent < 0 || tt.Percent > 100 { - return errInvalidValue(fmt.Sprintf("%d", tt.Percent), "percent") + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%d", tt.Percent), "percent")) } - return nil + return errs } diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/service_defaults.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/service_defaults.go index 3e46151cd2e..4f66f434545 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/service_defaults.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/service_defaults.go @@ -1,5 +1,6 @@ /* -Copyright 2017 The Knative Authors +Copyright 2018 The Knative Authors + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -24,5 +25,7 @@ func (ss *ServiceSpec) SetDefaults() { ss.RunLatest.Configuration.SetDefaults() } else if ss.Pinned != nil { ss.Pinned.Configuration.SetDefaults() + } else if ss.Release != nil { + ss.Release.Configuration.SetDefaults() } } diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/service_types.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/service_types.go index c9df581e63c..551456eef8f 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/service_types.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/service_types.go @@ -17,12 +17,13 @@ limitations under the License. package v1alpha1 import ( - "encoding/json" - "reflect" - "time" - corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + "github.com/knative/pkg/apis" + duckv1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + "github.com/knative/pkg/kmeta" ) // +genclient @@ -51,8 +52,14 @@ type Service struct { } // Check that Service may be validated and defaulted. -var _ Validatable = (*Service)(nil) -var _ Defaultable = (*Service)(nil) +var _ apis.Validatable = (*Service)(nil) +var _ apis.Defaultable = (*Service)(nil) + +// Check that we can create OwnerReferences to a Service. +var _ kmeta.OwnerRefable = (*Service)(nil) + +// Check that ServiceStatus may have its conditions managed. +var _ duckv1alpha1.ConditionsAccessor = (*ServiceStatus)(nil) // ServiceSpec represents the configuration for the Service object. Exactly one // of its members (other than Generation) must be specified. Services can either @@ -74,16 +81,59 @@ type ServiceSpec struct { // Pins this service to a specific revision name. The revision must // be owned by the configuration provided. + // PinnedType is DEPRECATED in favor of ReleaseType // +optional Pinned *PinnedType `json:"pinned,omitempty"` + + // Manual mode enables users to start managing the underlying Route and Configuration + // resources directly. This advanced usage is intended as a path for users to graduate + // from the limited capabilities of Service to the full power of Route. + // +optional + Manual *ManualType `json:"manual,omitempty"` + + // Release enables gradual promotion of new revisions by allowing traffic + // to be split between two revisions. This type replaces the deprecated Pinned type. + // +optional + Release *ReleaseType `json:"release,omitempty"` +} + +// ManualType contains the options for configuring a manual service. See ServiceSpec for +// more details. +type ManualType struct { + // Manual type does not contain a configuration as this type provides the + // user complete control over the configuration and route. +} + +// ReleaseType contains the options for slowly releasing revisions. See ServiceSpec for +// more details. +type ReleaseType struct { + // Revisions is an ordered list of 1 or 2 revisions. The first will + // have a TrafficTarget with a name of "current" and the second will have + // a name of "candidate". + // +optional + Revisions []string `json:"revisions,omitempty"` + + // RolloutPercent is the percent of traffic that should be sent to the "candidate" + // revision. Valid values are between 0 and 99 inclusive. + // +optional + RolloutPercent int `json:"rolloutPercent,omitempty"` + + // The configuration for this service. All revisions from this service must + // come from a single configuration. + // +optional + Configuration ConfigurationSpec `json:"configuration,omitempty"` } +// RunLatestType contains the options for always having a route to the latest configuration. See +// ServiceSpec for more details. type RunLatestType struct { // The configuration for this service. // +optional Configuration ConfigurationSpec `json:"configuration,omitempty"` } +// PinnedType is DEPRECATED. ReleaseType should be used instead. To get the behavior of PinnedType set +// ReleaseType.Revisions to []string{PinnedType.RevisionName} and ReleaseType.RolloutPercent to 0. type PinnedType struct { // The revision name to pin this service to until changed // to a different service type. @@ -95,38 +145,24 @@ type PinnedType struct { Configuration ConfigurationSpec `json:"configuration,omitempty"` } -type ServiceCondition struct { - Type ServiceConditionType `json:"type"` - - Status corev1.ConditionStatus `json:"status" description:"status of the condition, one of True, False, Unknown"` - - // +optional - LastTransitionTime metav1.Time `json:"lastTransitionTime,omitempty" description:"last time the condition transit from one status to another"` - - // +optional - Reason string `json:"reason,omitempty" description:"one-word CamelCase reason for the condition's last transition"` - // +optional - Message string `json:"message,omitempty" description:"human-readable message indicating details about last transition"` -} - -// ServiceConditionType represents an Service condition value -type ServiceConditionType string - +// ConditionType represents a Service condition value const ( // ServiceConditionReady is set when the service is configured // and has available backends ready to receive traffic. - ServiceConditionReady ServiceConditionType = "Ready" + ServiceConditionReady = duckv1alpha1.ConditionReady // ServiceConditionRoutesReady is set when the service's underlying // routes have reported readiness. - ServiceConditionRoutesReady ServiceConditionType = "RoutesReady" + ServiceConditionRoutesReady duckv1alpha1.ConditionType = "RoutesReady" // ServiceConditionConfigurationsReady is set when the service's underlying // configurations have reported readiness. - ServiceConditionConfigurationsReady ServiceConditionType = "ConfigurationsReady" + ServiceConditionConfigurationsReady duckv1alpha1.ConditionType = "ConfigurationsReady" ) +var serviceCondSet = duckv1alpha1.NewLivingConditionSet(ServiceConditionConfigurationsReady, ServiceConditionRoutesReady) + type ServiceStatus struct { // +optional - Conditions []ServiceCondition `json:"conditions,omitempty"` + Conditions duckv1alpha1.Conditions `json:"conditions,omitempty"` // From RouteStatus. // Domain holds the top-level domain that will distribute traffic over the provided targets. @@ -138,9 +174,14 @@ type ServiceStatus struct { // DomainInternal holds the top-level domain that will distribute traffic over the provided // targets from inside the cluster. It generally has the form // {route-name}.{route-namespace}.svc.cluster.local + // DEPRECATED: Use Address instead. // +optional DomainInternal string `json:"domainInternal,omitempty"` + // Address holds the information needed for a Route to be the target of an event. + // +optional + Address *duckv1alpha1.Addressable `json:"address,omitempty"` + // From RouteStatus. // Traffic holds the configured traffic distribution. // These entries will always contain RevisionName references. @@ -177,80 +218,20 @@ type ServiceList struct { Items []Service `json:"items"` } -func (s *Service) GetGeneration() int64 { - return s.Spec.Generation -} - -func (s *Service) SetGeneration(generation int64) { - s.Spec.Generation = generation -} - -func (s *Service) GetSpecJSON() ([]byte, error) { - return json.Marshal(s.Spec) +func (s *Service) GetGroupVersionKind() schema.GroupVersionKind { + return SchemeGroupVersion.WithKind("Service") } func (ss *ServiceStatus) IsReady() bool { - if c := ss.GetCondition(ServiceConditionReady); c != nil { - return c.Status == corev1.ConditionTrue - } - return false -} - -func (ss *ServiceStatus) GetCondition(t ServiceConditionType) *ServiceCondition { - for _, cond := range ss.Conditions { - if cond.Type == t { - return &cond - } - } - return nil -} - -func (ss *ServiceStatus) setCondition(new *ServiceCondition) { - if new == nil { - return - } - - t := new.Type - var conditions []ServiceCondition - for _, cond := range ss.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } else { - // If we'd only update the LastTransitionTime, then return. - new.LastTransitionTime = cond.LastTransitionTime - if reflect.DeepEqual(new, &cond) { - return - } - } - } - new.LastTransitionTime = metav1.NewTime(time.Now()) - conditions = append(conditions, *new) - ss.Conditions = conditions + return serviceCondSet.Manage(ss).IsHappy() } -func (ss *ServiceStatus) RemoveCondition(t ServiceConditionType) { - var conditions []ServiceCondition - for _, cond := range ss.Conditions { - if cond.Type != t { - conditions = append(conditions, cond) - } - } - ss.Conditions = conditions +func (ss *ServiceStatus) GetCondition(t duckv1alpha1.ConditionType) *duckv1alpha1.Condition { + return serviceCondSet.Manage(ss).GetCondition(t) } func (ss *ServiceStatus) InitializeConditions() { - for _, cond := range []ServiceConditionType{ - ServiceConditionReady, - ServiceConditionConfigurationsReady, - ServiceConditionRoutesReady, - } { - if rc := ss.GetCondition(cond); rc == nil { - ss.setCondition(&ServiceCondition{ - Type: cond, - Status: corev1.ConditionUnknown, - }) - } - } + serviceCondSet.Manage(ss).InitializeConditions() } func (ss *ServiceStatus) PropagateConfigurationStatus(cs ConfigurationStatus) { @@ -261,67 +242,65 @@ func (ss *ServiceStatus) PropagateConfigurationStatus(cs ConfigurationStatus) { if cc == nil { return } - sct := []ServiceConditionType{ServiceConditionConfigurationsReady} - // If the underlying Configuration reported not ready, then bubble it up. - if cc.Status != corev1.ConditionTrue { - sct = append(sct, ServiceConditionReady) - } - for _, cond := range sct { - ss.setCondition(&ServiceCondition{ - Type: cond, - Status: cc.Status, - Reason: cc.Reason, - Message: cc.Message, - }) - } - if cc.Status == corev1.ConditionTrue { - ss.checkAndMarkReady() + switch { + case cc.Status == corev1.ConditionUnknown: + serviceCondSet.Manage(ss).MarkUnknown(ServiceConditionConfigurationsReady, cc.Reason, cc.Message) + case cc.Status == corev1.ConditionTrue: + serviceCondSet.Manage(ss).MarkTrue(ServiceConditionConfigurationsReady) + case cc.Status == corev1.ConditionFalse: + serviceCondSet.Manage(ss).MarkFalse(ServiceConditionConfigurationsReady, cc.Reason, cc.Message) } } func (ss *ServiceStatus) PropagateRouteStatus(rs RouteStatus) { ss.Domain = rs.Domain ss.DomainInternal = rs.DomainInternal + ss.Address = rs.Address ss.Traffic = rs.Traffic rc := rs.GetCondition(RouteConditionReady) if rc == nil { return } - sct := []ServiceConditionType{ServiceConditionRoutesReady} - // If the underlying Route reported not ready, then bubble it up. - if rc.Status != corev1.ConditionTrue { - sct = append(sct, ServiceConditionReady) - } - for _, cond := range sct { - ss.setCondition(&ServiceCondition{ - Type: cond, - Status: rc.Status, - Reason: rc.Reason, - Message: rc.Message, - }) - } - if rc.Status == corev1.ConditionTrue { - ss.checkAndMarkReady() + switch { + case rc.Status == corev1.ConditionUnknown: + serviceCondSet.Manage(ss).MarkUnknown(ServiceConditionRoutesReady, rc.Reason, rc.Message) + case rc.Status == corev1.ConditionTrue: + serviceCondSet.Manage(ss).MarkTrue(ServiceConditionRoutesReady) + case rc.Status == corev1.ConditionFalse: + serviceCondSet.Manage(ss).MarkFalse(ServiceConditionRoutesReady, rc.Reason, rc.Message) } } -func (ss *ServiceStatus) checkAndMarkReady() { - for _, cond := range []ServiceConditionType{ - ServiceConditionConfigurationsReady, - ServiceConditionRoutesReady, - } { - c := ss.GetCondition(cond) - if c == nil || c.Status != corev1.ConditionTrue { - return - } - } - ss.markReady() +// SetManualStatus updates the service conditions to unknown as the underlying Route +// can have TrafficTargets to Configurations not owned by the service. We do not want to falsely +// report Ready. +func (ss *ServiceStatus) SetManualStatus() { + reason := "Manual" + message := "Service is set to Manual, and is not managing underlying resources." + + // Clear our fields by creating a new status and copying over only the fields and conditions we want + newStatus := &ServiceStatus{} + newStatus.InitializeConditions() + serviceCondSet.Manage(newStatus).MarkUnknown(ServiceConditionConfigurationsReady, reason, message) + serviceCondSet.Manage(newStatus).MarkUnknown(ServiceConditionRoutesReady, reason, message) + + newStatus.Address = ss.Address + newStatus.Domain = ss.Domain + newStatus.DomainInternal = ss.DomainInternal + + *ss = *newStatus + } -func (ss *ServiceStatus) markReady() { - ss.setCondition(&ServiceCondition{ - Type: ServiceConditionReady, - Status: corev1.ConditionTrue, - }) +// GetConditions returns the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (ss *ServiceStatus) GetConditions() duckv1alpha1.Conditions { + return ss.Conditions +} + +// SetConditions sets the Conditions array. This enables generic handling of +// conditions by implementing the duckv1alpha1.Conditions interface. +func (ss *ServiceStatus) SetConditions(conditions duckv1alpha1.Conditions) { + ss.Conditions = conditions } diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/service_validation.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/service_validation.go index 8cc2c9b3cd8..8ff4ff3a8cf 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/service_validation.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/service_validation.go @@ -1,5 +1,6 @@ /* -Copyright 2017 The Knative Authors +Copyright 2018 The Knative Authors + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -15,43 +16,99 @@ limitations under the License. package v1alpha1 -func (s *Service) Validate() *FieldError { - return s.Spec.Validate().ViaField("spec") +import ( + "fmt" + "github.com/knative/pkg/apis" +) + +// Validate validates the fields belonging to Service +func (s *Service) Validate() *apis.FieldError { + return ValidateObjectMetadata(s.GetObjectMeta()).ViaField("metadata"). + Also(s.Spec.Validate().ViaField("spec")) } -func (ss *ServiceSpec) Validate() *FieldError { +// Validate validates the fields belonging to ServiceSpec recursively +func (ss *ServiceSpec) Validate() *apis.FieldError { // We would do this semantic DeepEqual, but the spec is comprised // entirely of a oneof, the validation for which produces a clearer // error message. // if equality.Semantic.DeepEqual(ss, &ServiceSpec{}) { - // return errMissingField(currentField) + // return apis.ErrMissingField(currentField) // } - switch { - case ss.RunLatest != nil && ss.Pinned != nil: - return &FieldError{ - Message: "Expected exactly one, got both", - Paths: []string{"runLatest", "pinned"}, - } - case ss.RunLatest != nil: - return ss.RunLatest.Validate().ViaField("runLatest") - case ss.Pinned != nil: - return ss.Pinned.Validate().ViaField("pinned") - default: - return &FieldError{ - Message: "Expected exactly one, got neither", - Paths: []string{"runLatest", "pinned"}, - } + var errs *apis.FieldError + set := []string{} + + if ss.RunLatest != nil { + set = append(set, "runLatest") + errs = errs.Also(ss.RunLatest.Validate().ViaField("runLatest")) + } + if ss.Release != nil { + set = append(set, "release") + errs = errs.Also(ss.Release.Validate().ViaField("release")) } + if ss.Manual != nil { + set = append(set, "manual") + errs = errs.Also(ss.Manual.Validate().ViaField("manual")) + } + if ss.Pinned != nil { + set = append(set, "pinned") + errs = errs.Also(ss.Pinned.Validate().ViaField("pinned")) + } + + if len(set) > 1 { + errs = errs.Also(apis.ErrMultipleOneOf(set...)) + } else if len(set) == 0 { + errs = errs.Also(apis.ErrMissingOneOf("runLatest", "release", "manual", "pinned")) + } + return errs } -func (pt *PinnedType) Validate() *FieldError { +// Validate validates the fields belonging to PinnedType +func (pt *PinnedType) Validate() *apis.FieldError { + var errs *apis.FieldError if pt.RevisionName == "" { - return errMissingField("revisionName") + errs = apis.ErrMissingField("revisionName") } - return pt.Configuration.Validate().ViaField("configuration") + return errs.Also(pt.Configuration.Validate().ViaField("configuration")) } -func (rlt *RunLatestType) Validate() *FieldError { +// Validate validates the fields belonging to RunLatestType +func (rlt *RunLatestType) Validate() *apis.FieldError { return rlt.Configuration.Validate().ViaField("configuration") } + +// Validate validates the fields belonging to ManualType +func (m *ManualType) Validate() *apis.FieldError { + return nil +} + +// Validate validates the fields belonging to ReleaseType +func (rt *ReleaseType) Validate() *apis.FieldError { + var errs *apis.FieldError + minRevisions := 1 + maxRevisions := 2 + + numRevisions := len(rt.Revisions) + if numRevisions < minRevisions { + errs = errs.Also(apis.ErrMissingField("revisions")) + } + + if numRevisions > maxRevisions { + outOfRange := &apis.FieldError{ + Message: fmt.Sprintf("expected number of elements in range [%v, %v], got %v", minRevisions, maxRevisions, numRevisions), + Paths: []string{"revisions"}, + } + errs = errs.Also(outOfRange) + } + + if numRevisions < 2 && rt.RolloutPercent != 0 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%v", rt.RolloutPercent), "rolloutPercent")) + } + + if rt.RolloutPercent < 0 || rt.RolloutPercent > 99 { + errs = errs.Also(apis.ErrInvalidValue(fmt.Sprintf("%v", rt.RolloutPercent), "rolloutPercent")) + } + + return errs.Also(rt.Configuration.Validate().ViaField("configuration")) +} diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/zz_generated.deepcopy.go b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/zz_generated.deepcopy.go index 62e5b279e2e..3504d03a30a 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/zz_generated.deepcopy.go +++ b/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/zz_generated.deepcopy.go @@ -22,6 +22,8 @@ package v1alpha1 import ( build_v1alpha1 "github.com/knative/build/pkg/apis/build/v1alpha1" + duck_v1alpha1 "github.com/knative/pkg/apis/duck/v1alpha1" + v1 "k8s.io/api/core/v1" runtime "k8s.io/apimachinery/pkg/runtime" ) @@ -54,23 +56,6 @@ func (in *Configuration) DeepCopyObject() runtime.Object { } } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ConfigurationCondition) DeepCopyInto(out *ConfigurationCondition) { - *out = *in - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ConfigurationCondition. -func (in *ConfigurationCondition) DeepCopy() *ConfigurationCondition { - if in == nil { - return nil - } - out := new(ConfigurationCondition) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ConfigurationList) DeepCopyInto(out *ConfigurationList) { *out = *in @@ -113,7 +98,7 @@ func (in *ConfigurationSpec) DeepCopyInto(out *ConfigurationSpec) { if *in == nil { *out = nil } else { - *out = new(build_v1alpha1.BuildSpec) + *out = new(RawExtension) (*in).DeepCopyInto(*out) } } @@ -136,7 +121,7 @@ func (in *ConfigurationStatus) DeepCopyInto(out *ConfigurationStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]ConfigurationCondition, len(*in)) + *out = make(duck_v1alpha1.Conditions, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -154,6 +139,22 @@ func (in *ConfigurationStatus) DeepCopy() *ConfigurationStatus { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ManualType) DeepCopyInto(out *ManualType) { + *out = *in + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManualType. +func (in *ManualType) DeepCopy() *ManualType { + if in == nil { + return nil + } + out := new(ManualType) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *PinnedType) DeepCopyInto(out *PinnedType) { *out = *in @@ -171,6 +172,63 @@ func (in *PinnedType) DeepCopy() *PinnedType { return out } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *RawExtension) DeepCopyInto(out *RawExtension) { + *out = *in + if in.Raw != nil { + in, out := &in.Raw, &out.Raw + *out = make([]byte, len(*in)) + copy(*out, *in) + } + if in.Object == nil { + out.Object = nil + } else { + out.Object = in.Object.DeepCopyObject() + } + if in.BuildSpec != nil { + in, out := &in.BuildSpec, &out.BuildSpec + if *in == nil { + *out = nil + } else { + *out = new(build_v1alpha1.BuildSpec) + (*in).DeepCopyInto(*out) + } + } + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RawExtension. +func (in *RawExtension) DeepCopy() *RawExtension { + if in == nil { + return nil + } + out := new(RawExtension) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ReleaseType) DeepCopyInto(out *ReleaseType) { + *out = *in + if in.Revisions != nil { + in, out := &in.Revisions, &out.Revisions + *out = make([]string, len(*in)) + copy(*out, *in) + } + in.Configuration.DeepCopyInto(&out.Configuration) + return +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ReleaseType. +func (in *ReleaseType) DeepCopy() *ReleaseType { + if in == nil { + return nil + } + out := new(ReleaseType) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *Revision) DeepCopyInto(out *Revision) { *out = *in @@ -200,23 +258,6 @@ func (in *Revision) DeepCopyObject() runtime.Object { } } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RevisionCondition) DeepCopyInto(out *RevisionCondition) { - *out = *in - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RevisionCondition. -func (in *RevisionCondition) DeepCopy() *RevisionCondition { - if in == nil { - return nil - } - out := new(RevisionCondition) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RevisionList) DeepCopyInto(out *RevisionList) { *out = *in @@ -254,6 +295,15 @@ func (in *RevisionList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RevisionSpec) DeepCopyInto(out *RevisionSpec) { *out = *in + if in.BuildRef != nil { + in, out := &in.BuildRef, &out.BuildRef + if *in == nil { + *out = nil + } else { + *out = new(v1.ObjectReference) + **out = **in + } + } in.Container.DeepCopyInto(&out.Container) return } @@ -273,7 +323,7 @@ func (in *RevisionStatus) DeepCopyInto(out *RevisionStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]RevisionCondition, len(*in)) + *out = make(duck_v1alpha1.Conditions, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -338,23 +388,6 @@ func (in *Route) DeepCopyObject() runtime.Object { } } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *RouteCondition) DeepCopyInto(out *RouteCondition) { - *out = *in - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new RouteCondition. -func (in *RouteCondition) DeepCopy() *RouteCondition { - if in == nil { - return nil - } - out := new(RouteCondition) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RouteList) DeepCopyInto(out *RouteList) { *out = *in @@ -413,6 +446,15 @@ func (in *RouteSpec) DeepCopy() *RouteSpec { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *RouteStatus) DeepCopyInto(out *RouteStatus) { *out = *in + if in.Address != nil { + in, out := &in.Address, &out.Address + if *in == nil { + *out = nil + } else { + *out = new(duck_v1alpha1.Addressable) + **out = **in + } + } if in.Traffic != nil { in, out := &in.Traffic, &out.Traffic *out = make([]TrafficTarget, len(*in)) @@ -420,7 +462,7 @@ func (in *RouteStatus) DeepCopyInto(out *RouteStatus) { } if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]RouteCondition, len(*in)) + *out = make(duck_v1alpha1.Conditions, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } @@ -484,23 +526,6 @@ func (in *Service) DeepCopyObject() runtime.Object { } } -// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *ServiceCondition) DeepCopyInto(out *ServiceCondition) { - *out = *in - in.LastTransitionTime.DeepCopyInto(&out.LastTransitionTime) - return -} - -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ServiceCondition. -func (in *ServiceCondition) DeepCopy() *ServiceCondition { - if in == nil { - return nil - } - out := new(ServiceCondition) - in.DeepCopyInto(out) - return out -} - // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ServiceList) DeepCopyInto(out *ServiceList) { *out = *in @@ -556,6 +581,24 @@ func (in *ServiceSpec) DeepCopyInto(out *ServiceSpec) { (*in).DeepCopyInto(*out) } } + if in.Manual != nil { + in, out := &in.Manual, &out.Manual + if *in == nil { + *out = nil + } else { + *out = new(ManualType) + **out = **in + } + } + if in.Release != nil { + in, out := &in.Release, &out.Release + if *in == nil { + *out = nil + } else { + *out = new(ReleaseType) + (*in).DeepCopyInto(*out) + } + } return } @@ -574,11 +617,20 @@ func (in *ServiceStatus) DeepCopyInto(out *ServiceStatus) { *out = *in if in.Conditions != nil { in, out := &in.Conditions, &out.Conditions - *out = make([]ServiceCondition, len(*in)) + *out = make(duck_v1alpha1.Conditions, len(*in)) for i := range *in { (*in)[i].DeepCopyInto(&(*out)[i]) } } + if in.Address != nil { + in, out := &in.Address, &out.Address + if *in == nil { + *out = nil + } else { + *out = new(duck_v1alpha1.Addressable) + **out = **in + } + } if in.Traffic != nil { in, out := &in.Traffic, &out.Traffic *out = make([]TrafficTarget, len(*in)) diff --git a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/clientset.go b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/clientset.go index 2041ea8b12e..3a8f4a4d858 100644 --- a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/clientset.go +++ b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/clientset.go @@ -16,8 +16,8 @@ limitations under the License. package versioned import ( - glog "github.com/golang/glog" - networkingv1alpha3 "github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3" + autoscalingv1alpha1 "github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1" + networkingv1alpha1 "github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1" servingv1alpha1 "github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1" discovery "k8s.io/client-go/discovery" rest "k8s.io/client-go/rest" @@ -26,9 +26,12 @@ import ( type Interface interface { Discovery() discovery.DiscoveryInterface - NetworkingV1alpha3() networkingv1alpha3.NetworkingV1alpha3Interface + AutoscalingV1alpha1() autoscalingv1alpha1.AutoscalingV1alpha1Interface // Deprecated: please explicitly pick a version if possible. - Networking() networkingv1alpha3.NetworkingV1alpha3Interface + Autoscaling() autoscalingv1alpha1.AutoscalingV1alpha1Interface + NetworkingV1alpha1() networkingv1alpha1.NetworkingV1alpha1Interface + // Deprecated: please explicitly pick a version if possible. + Networking() networkingv1alpha1.NetworkingV1alpha1Interface ServingV1alpha1() servingv1alpha1.ServingV1alpha1Interface // Deprecated: please explicitly pick a version if possible. Serving() servingv1alpha1.ServingV1alpha1Interface @@ -38,19 +41,31 @@ type Interface interface { // version included in a Clientset. type Clientset struct { *discovery.DiscoveryClient - networkingV1alpha3 *networkingv1alpha3.NetworkingV1alpha3Client - servingV1alpha1 *servingv1alpha1.ServingV1alpha1Client + autoscalingV1alpha1 *autoscalingv1alpha1.AutoscalingV1alpha1Client + networkingV1alpha1 *networkingv1alpha1.NetworkingV1alpha1Client + servingV1alpha1 *servingv1alpha1.ServingV1alpha1Client } -// NetworkingV1alpha3 retrieves the NetworkingV1alpha3Client -func (c *Clientset) NetworkingV1alpha3() networkingv1alpha3.NetworkingV1alpha3Interface { - return c.networkingV1alpha3 +// AutoscalingV1alpha1 retrieves the AutoscalingV1alpha1Client +func (c *Clientset) AutoscalingV1alpha1() autoscalingv1alpha1.AutoscalingV1alpha1Interface { + return c.autoscalingV1alpha1 +} + +// Deprecated: Autoscaling retrieves the default version of AutoscalingClient. +// Please explicitly pick a version. +func (c *Clientset) Autoscaling() autoscalingv1alpha1.AutoscalingV1alpha1Interface { + return c.autoscalingV1alpha1 +} + +// NetworkingV1alpha1 retrieves the NetworkingV1alpha1Client +func (c *Clientset) NetworkingV1alpha1() networkingv1alpha1.NetworkingV1alpha1Interface { + return c.networkingV1alpha1 } // Deprecated: Networking retrieves the default version of NetworkingClient. // Please explicitly pick a version. -func (c *Clientset) Networking() networkingv1alpha3.NetworkingV1alpha3Interface { - return c.networkingV1alpha3 +func (c *Clientset) Networking() networkingv1alpha1.NetworkingV1alpha1Interface { + return c.networkingV1alpha1 } // ServingV1alpha1 retrieves the ServingV1alpha1Client @@ -80,7 +95,11 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { } var cs Clientset var err error - cs.networkingV1alpha3, err = networkingv1alpha3.NewForConfig(&configShallowCopy) + cs.autoscalingV1alpha1, err = autoscalingv1alpha1.NewForConfig(&configShallowCopy) + if err != nil { + return nil, err + } + cs.networkingV1alpha1, err = networkingv1alpha1.NewForConfig(&configShallowCopy) if err != nil { return nil, err } @@ -91,7 +110,6 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfig(&configShallowCopy) if err != nil { - glog.Errorf("failed to create the DiscoveryClient: %v", err) return nil, err } return &cs, nil @@ -101,7 +119,8 @@ func NewForConfig(c *rest.Config) (*Clientset, error) { // panics if there is an error in the config. func NewForConfigOrDie(c *rest.Config) *Clientset { var cs Clientset - cs.networkingV1alpha3 = networkingv1alpha3.NewForConfigOrDie(c) + cs.autoscalingV1alpha1 = autoscalingv1alpha1.NewForConfigOrDie(c) + cs.networkingV1alpha1 = networkingv1alpha1.NewForConfigOrDie(c) cs.servingV1alpha1 = servingv1alpha1.NewForConfigOrDie(c) cs.DiscoveryClient = discovery.NewDiscoveryClientForConfigOrDie(c) @@ -111,7 +130,8 @@ func NewForConfigOrDie(c *rest.Config) *Clientset { // New creates a new Clientset for the given RESTClient. func New(c rest.Interface) *Clientset { var cs Clientset - cs.networkingV1alpha3 = networkingv1alpha3.New(c) + cs.autoscalingV1alpha1 = autoscalingv1alpha1.New(c) + cs.networkingV1alpha1 = networkingv1alpha1.New(c) cs.servingV1alpha1 = servingv1alpha1.New(c) cs.DiscoveryClient = discovery.NewDiscoveryClient(c) diff --git a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/scheme/register.go b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/scheme/register.go index f9745bd9766..9d38af8707a 100644 --- a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/scheme/register.go +++ b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/scheme/register.go @@ -16,7 +16,8 @@ limitations under the License. package scheme import ( - networkingv1alpha3 "github.com/knative/serving/pkg/apis/istio/v1alpha3" + autoscalingv1alpha1 "github.com/knative/serving/pkg/apis/autoscaling/v1alpha1" + networkingv1alpha1 "github.com/knative/serving/pkg/apis/networking/v1alpha1" servingv1alpha1 "github.com/knative/serving/pkg/apis/serving/v1alpha1" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" runtime "k8s.io/apimachinery/pkg/runtime" @@ -48,6 +49,7 @@ func init() { // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types // correctly. func AddToScheme(scheme *runtime.Scheme) { - networkingv1alpha3.AddToScheme(scheme) + autoscalingv1alpha1.AddToScheme(scheme) + networkingv1alpha1.AddToScheme(scheme) servingv1alpha1.AddToScheme(scheme) } diff --git a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/autoscaling_client.go b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/autoscaling_client.go new file mode 100644 index 00000000000..04623c09ef3 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/autoscaling_client.go @@ -0,0 +1,87 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/serving/pkg/apis/autoscaling/v1alpha1" + "github.com/knative/serving/pkg/client/clientset/versioned/scheme" + serializer "k8s.io/apimachinery/pkg/runtime/serializer" + rest "k8s.io/client-go/rest" +) + +type AutoscalingV1alpha1Interface interface { + RESTClient() rest.Interface + PodAutoscalersGetter +} + +// AutoscalingV1alpha1Client is used to interact with features provided by the autoscaling.internal.knative.dev group. +type AutoscalingV1alpha1Client struct { + restClient rest.Interface +} + +func (c *AutoscalingV1alpha1Client) PodAutoscalers(namespace string) PodAutoscalerInterface { + return newPodAutoscalers(c, namespace) +} + +// NewForConfig creates a new AutoscalingV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*AutoscalingV1alpha1Client, error) { + config := *c + if err := setConfigDefaults(&config); err != nil { + return nil, err + } + client, err := rest.RESTClientFor(&config) + if err != nil { + return nil, err + } + return &AutoscalingV1alpha1Client{client}, nil +} + +// NewForConfigOrDie creates a new AutoscalingV1alpha1Client for the given config and +// panics if there is an error in the config. +func NewForConfigOrDie(c *rest.Config) *AutoscalingV1alpha1Client { + client, err := NewForConfig(c) + if err != nil { + panic(err) + } + return client +} + +// New creates a new AutoscalingV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *AutoscalingV1alpha1Client { + return &AutoscalingV1alpha1Client{c} +} + +func setConfigDefaults(config *rest.Config) error { + gv := v1alpha1.SchemeGroupVersion + config.GroupVersion = &gv + config.APIPath = "/apis" + config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} + + if config.UserAgent == "" { + config.UserAgent = rest.DefaultKubernetesUserAgent() + } + + return nil +} + +// RESTClient returns a RESTClient that is used to communicate +// with API server by this client implementation. +func (c *AutoscalingV1alpha1Client) RESTClient() rest.Interface { + if c == nil { + return nil + } + return c.restClient +} diff --git a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/doc.go b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/doc.go similarity index 97% rename from vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/doc.go rename to vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/doc.go index d1909a47d81..10ede9da2f0 100644 --- a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/doc.go +++ b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/doc.go @@ -14,4 +14,4 @@ See the License for the specific language governing permissions and limitations under the License. */ // This package has the automatically generated typed clients. -package v1alpha3 +package v1alpha1 diff --git a/vendor/github.com/knative/serving/pkg/apis/istio/register.go b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/generated_expansion.go similarity index 90% rename from vendor/github.com/knative/serving/pkg/apis/istio/register.go rename to vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/generated_expansion.go index 647eb38a0e1..933bb688f91 100644 --- a/vendor/github.com/knative/serving/pkg/apis/istio/register.go +++ b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/generated_expansion.go @@ -13,9 +13,6 @@ 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 v1alpha1 -package istio - -const ( - GroupName = "networking.istio.io" -) +type PodAutoscalerExpansion interface{} diff --git a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/podautoscaler.go b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/podautoscaler.go new file mode 100644 index 00000000000..687cda122cd --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/autoscaling/v1alpha1/podautoscaler.go @@ -0,0 +1,171 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/serving/pkg/apis/autoscaling/v1alpha1" + scheme "github.com/knative/serving/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// PodAutoscalersGetter has a method to return a PodAutoscalerInterface. +// A group's client should implement this interface. +type PodAutoscalersGetter interface { + PodAutoscalers(namespace string) PodAutoscalerInterface +} + +// PodAutoscalerInterface has methods to work with PodAutoscaler resources. +type PodAutoscalerInterface interface { + Create(*v1alpha1.PodAutoscaler) (*v1alpha1.PodAutoscaler, error) + Update(*v1alpha1.PodAutoscaler) (*v1alpha1.PodAutoscaler, error) + UpdateStatus(*v1alpha1.PodAutoscaler) (*v1alpha1.PodAutoscaler, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.PodAutoscaler, error) + List(opts v1.ListOptions) (*v1alpha1.PodAutoscalerList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PodAutoscaler, err error) + PodAutoscalerExpansion +} + +// podAutoscalers implements PodAutoscalerInterface +type podAutoscalers struct { + client rest.Interface + ns string +} + +// newPodAutoscalers returns a PodAutoscalers +func newPodAutoscalers(c *AutoscalingV1alpha1Client, namespace string) *podAutoscalers { + return &podAutoscalers{ + client: c.RESTClient(), + ns: namespace, + } +} + +// Get takes name of the podAutoscaler, and returns the corresponding podAutoscaler object, and an error if there is any. +func (c *podAutoscalers) Get(name string, options v1.GetOptions) (result *v1alpha1.PodAutoscaler, err error) { + result = &v1alpha1.PodAutoscaler{} + err = c.client.Get(). + Namespace(c.ns). + Resource("podautoscalers"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of PodAutoscalers that match those selectors. +func (c *podAutoscalers) List(opts v1.ListOptions) (result *v1alpha1.PodAutoscalerList, err error) { + result = &v1alpha1.PodAutoscalerList{} + err = c.client.Get(). + Namespace(c.ns). + Resource("podautoscalers"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested podAutoscalers. +func (c *podAutoscalers) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Namespace(c.ns). + Resource("podautoscalers"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Create takes the representation of a podAutoscaler and creates it. Returns the server's representation of the podAutoscaler, and an error, if there is any. +func (c *podAutoscalers) Create(podAutoscaler *v1alpha1.PodAutoscaler) (result *v1alpha1.PodAutoscaler, err error) { + result = &v1alpha1.PodAutoscaler{} + err = c.client.Post(). + Namespace(c.ns). + Resource("podautoscalers"). + Body(podAutoscaler). + Do(). + Into(result) + return +} + +// Update takes the representation of a podAutoscaler and updates it. Returns the server's representation of the podAutoscaler, and an error, if there is any. +func (c *podAutoscalers) Update(podAutoscaler *v1alpha1.PodAutoscaler) (result *v1alpha1.PodAutoscaler, err error) { + result = &v1alpha1.PodAutoscaler{} + err = c.client.Put(). + Namespace(c.ns). + Resource("podautoscalers"). + Name(podAutoscaler.Name). + Body(podAutoscaler). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *podAutoscalers) UpdateStatus(podAutoscaler *v1alpha1.PodAutoscaler) (result *v1alpha1.PodAutoscaler, err error) { + result = &v1alpha1.PodAutoscaler{} + err = c.client.Put(). + Namespace(c.ns). + Resource("podautoscalers"). + Name(podAutoscaler.Name). + SubResource("status"). + Body(podAutoscaler). + Do(). + Into(result) + return +} + +// Delete takes name of the podAutoscaler and deletes it. Returns an error if one occurs. +func (c *podAutoscalers) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("podautoscalers"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *podAutoscalers) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.client.Delete(). + Namespace(c.ns). + Resource("podautoscalers"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched podAutoscaler. +func (c *podAutoscalers) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.PodAutoscaler, err error) { + result = &v1alpha1.PodAutoscaler{} + err = c.client.Patch(pt). + Namespace(c.ns). + Resource("podautoscalers"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/gateway.go b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/gateway.go deleted file mode 100644 index 70ccd02d145..00000000000 --- a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/gateway.go +++ /dev/null @@ -1,154 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package v1alpha3 - -import ( - v1alpha3 "github.com/knative/serving/pkg/apis/istio/v1alpha3" - scheme "github.com/knative/serving/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// GatewaysGetter has a method to return a GatewayInterface. -// A group's client should implement this interface. -type GatewaysGetter interface { - Gateways(namespace string) GatewayInterface -} - -// GatewayInterface has methods to work with Gateway resources. -type GatewayInterface interface { - Create(*v1alpha3.Gateway) (*v1alpha3.Gateway, error) - Update(*v1alpha3.Gateway) (*v1alpha3.Gateway, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha3.Gateway, error) - List(opts v1.ListOptions) (*v1alpha3.GatewayList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.Gateway, err error) - GatewayExpansion -} - -// gateways implements GatewayInterface -type gateways struct { - client rest.Interface - ns string -} - -// newGateways returns a Gateways -func newGateways(c *NetworkingV1alpha3Client, namespace string) *gateways { - return &gateways{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the gateway, and returns the corresponding gateway object, and an error if there is any. -func (c *gateways) Get(name string, options v1.GetOptions) (result *v1alpha3.Gateway, err error) { - result = &v1alpha3.Gateway{} - err = c.client.Get(). - Namespace(c.ns). - Resource("gateways"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of Gateways that match those selectors. -func (c *gateways) List(opts v1.ListOptions) (result *v1alpha3.GatewayList, err error) { - result = &v1alpha3.GatewayList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("gateways"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested gateways. -func (c *gateways) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("gateways"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a gateway and creates it. Returns the server's representation of the gateway, and an error, if there is any. -func (c *gateways) Create(gateway *v1alpha3.Gateway) (result *v1alpha3.Gateway, err error) { - result = &v1alpha3.Gateway{} - err = c.client.Post(). - Namespace(c.ns). - Resource("gateways"). - Body(gateway). - Do(). - Into(result) - return -} - -// Update takes the representation of a gateway and updates it. Returns the server's representation of the gateway, and an error, if there is any. -func (c *gateways) Update(gateway *v1alpha3.Gateway) (result *v1alpha3.Gateway, err error) { - result = &v1alpha3.Gateway{} - err = c.client.Put(). - Namespace(c.ns). - Resource("gateways"). - Name(gateway.Name). - Body(gateway). - Do(). - Into(result) - return -} - -// Delete takes name of the gateway and deletes it. Returns an error if one occurs. -func (c *gateways) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("gateways"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *gateways) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("gateways"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched gateway. -func (c *gateways) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.Gateway, err error) { - result = &v1alpha3.Gateway{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("gateways"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/virtualservice.go b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/virtualservice.go deleted file mode 100644 index feec0590710..00000000000 --- a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/virtualservice.go +++ /dev/null @@ -1,154 +0,0 @@ -/* -Copyright 2018 The Knative Authors - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -*/ -package v1alpha3 - -import ( - v1alpha3 "github.com/knative/serving/pkg/apis/istio/v1alpha3" - scheme "github.com/knative/serving/pkg/client/clientset/versioned/scheme" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - types "k8s.io/apimachinery/pkg/types" - watch "k8s.io/apimachinery/pkg/watch" - rest "k8s.io/client-go/rest" -) - -// VirtualServicesGetter has a method to return a VirtualServiceInterface. -// A group's client should implement this interface. -type VirtualServicesGetter interface { - VirtualServices(namespace string) VirtualServiceInterface -} - -// VirtualServiceInterface has methods to work with VirtualService resources. -type VirtualServiceInterface interface { - Create(*v1alpha3.VirtualService) (*v1alpha3.VirtualService, error) - Update(*v1alpha3.VirtualService) (*v1alpha3.VirtualService, error) - Delete(name string, options *v1.DeleteOptions) error - DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error - Get(name string, options v1.GetOptions) (*v1alpha3.VirtualService, error) - List(opts v1.ListOptions) (*v1alpha3.VirtualServiceList, error) - Watch(opts v1.ListOptions) (watch.Interface, error) - Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.VirtualService, err error) - VirtualServiceExpansion -} - -// virtualServices implements VirtualServiceInterface -type virtualServices struct { - client rest.Interface - ns string -} - -// newVirtualServices returns a VirtualServices -func newVirtualServices(c *NetworkingV1alpha3Client, namespace string) *virtualServices { - return &virtualServices{ - client: c.RESTClient(), - ns: namespace, - } -} - -// Get takes name of the virtualService, and returns the corresponding virtualService object, and an error if there is any. -func (c *virtualServices) Get(name string, options v1.GetOptions) (result *v1alpha3.VirtualService, err error) { - result = &v1alpha3.VirtualService{} - err = c.client.Get(). - Namespace(c.ns). - Resource("virtualservices"). - Name(name). - VersionedParams(&options, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// List takes label and field selectors, and returns the list of VirtualServices that match those selectors. -func (c *virtualServices) List(opts v1.ListOptions) (result *v1alpha3.VirtualServiceList, err error) { - result = &v1alpha3.VirtualServiceList{} - err = c.client.Get(). - Namespace(c.ns). - Resource("virtualservices"). - VersionedParams(&opts, scheme.ParameterCodec). - Do(). - Into(result) - return -} - -// Watch returns a watch.Interface that watches the requested virtualServices. -func (c *virtualServices) Watch(opts v1.ListOptions) (watch.Interface, error) { - opts.Watch = true - return c.client.Get(). - Namespace(c.ns). - Resource("virtualservices"). - VersionedParams(&opts, scheme.ParameterCodec). - Watch() -} - -// Create takes the representation of a virtualService and creates it. Returns the server's representation of the virtualService, and an error, if there is any. -func (c *virtualServices) Create(virtualService *v1alpha3.VirtualService) (result *v1alpha3.VirtualService, err error) { - result = &v1alpha3.VirtualService{} - err = c.client.Post(). - Namespace(c.ns). - Resource("virtualservices"). - Body(virtualService). - Do(). - Into(result) - return -} - -// Update takes the representation of a virtualService and updates it. Returns the server's representation of the virtualService, and an error, if there is any. -func (c *virtualServices) Update(virtualService *v1alpha3.VirtualService) (result *v1alpha3.VirtualService, err error) { - result = &v1alpha3.VirtualService{} - err = c.client.Put(). - Namespace(c.ns). - Resource("virtualservices"). - Name(virtualService.Name). - Body(virtualService). - Do(). - Into(result) - return -} - -// Delete takes name of the virtualService and deletes it. Returns an error if one occurs. -func (c *virtualServices) Delete(name string, options *v1.DeleteOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("virtualservices"). - Name(name). - Body(options). - Do(). - Error() -} - -// DeleteCollection deletes a collection of objects. -func (c *virtualServices) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { - return c.client.Delete(). - Namespace(c.ns). - Resource("virtualservices"). - VersionedParams(&listOptions, scheme.ParameterCodec). - Body(options). - Do(). - Error() -} - -// Patch applies the patch and returns the patched virtualService. -func (c *virtualServices) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha3.VirtualService, err error) { - result = &v1alpha3.VirtualService{} - err = c.client.Patch(pt). - Namespace(c.ns). - Resource("virtualservices"). - SubResource(subresources...). - Name(name). - Body(data). - Do(). - Into(result) - return -} diff --git a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/clusteringress.go b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/clusteringress.go new file mode 100644 index 00000000000..18231946ba8 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/clusteringress.go @@ -0,0 +1,160 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +package v1alpha1 + +import ( + v1alpha1 "github.com/knative/serving/pkg/apis/networking/v1alpha1" + scheme "github.com/knative/serving/pkg/client/clientset/versioned/scheme" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + types "k8s.io/apimachinery/pkg/types" + watch "k8s.io/apimachinery/pkg/watch" + rest "k8s.io/client-go/rest" +) + +// ClusterIngressesGetter has a method to return a ClusterIngressInterface. +// A group's client should implement this interface. +type ClusterIngressesGetter interface { + ClusterIngresses() ClusterIngressInterface +} + +// ClusterIngressInterface has methods to work with ClusterIngress resources. +type ClusterIngressInterface interface { + Create(*v1alpha1.ClusterIngress) (*v1alpha1.ClusterIngress, error) + Update(*v1alpha1.ClusterIngress) (*v1alpha1.ClusterIngress, error) + UpdateStatus(*v1alpha1.ClusterIngress) (*v1alpha1.ClusterIngress, error) + Delete(name string, options *v1.DeleteOptions) error + DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error + Get(name string, options v1.GetOptions) (*v1alpha1.ClusterIngress, error) + List(opts v1.ListOptions) (*v1alpha1.ClusterIngressList, error) + Watch(opts v1.ListOptions) (watch.Interface, error) + Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterIngress, err error) + ClusterIngressExpansion +} + +// clusterIngresses implements ClusterIngressInterface +type clusterIngresses struct { + client rest.Interface +} + +// newClusterIngresses returns a ClusterIngresses +func newClusterIngresses(c *NetworkingV1alpha1Client) *clusterIngresses { + return &clusterIngresses{ + client: c.RESTClient(), + } +} + +// Get takes name of the clusterIngress, and returns the corresponding clusterIngress object, and an error if there is any. +func (c *clusterIngresses) Get(name string, options v1.GetOptions) (result *v1alpha1.ClusterIngress, err error) { + result = &v1alpha1.ClusterIngress{} + err = c.client.Get(). + Resource("clusteringresses"). + Name(name). + VersionedParams(&options, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// List takes label and field selectors, and returns the list of ClusterIngresses that match those selectors. +func (c *clusterIngresses) List(opts v1.ListOptions) (result *v1alpha1.ClusterIngressList, err error) { + result = &v1alpha1.ClusterIngressList{} + err = c.client.Get(). + Resource("clusteringresses"). + VersionedParams(&opts, scheme.ParameterCodec). + Do(). + Into(result) + return +} + +// Watch returns a watch.Interface that watches the requested clusterIngresses. +func (c *clusterIngresses) Watch(opts v1.ListOptions) (watch.Interface, error) { + opts.Watch = true + return c.client.Get(). + Resource("clusteringresses"). + VersionedParams(&opts, scheme.ParameterCodec). + Watch() +} + +// Create takes the representation of a clusterIngress and creates it. Returns the server's representation of the clusterIngress, and an error, if there is any. +func (c *clusterIngresses) Create(clusterIngress *v1alpha1.ClusterIngress) (result *v1alpha1.ClusterIngress, err error) { + result = &v1alpha1.ClusterIngress{} + err = c.client.Post(). + Resource("clusteringresses"). + Body(clusterIngress). + Do(). + Into(result) + return +} + +// Update takes the representation of a clusterIngress and updates it. Returns the server's representation of the clusterIngress, and an error, if there is any. +func (c *clusterIngresses) Update(clusterIngress *v1alpha1.ClusterIngress) (result *v1alpha1.ClusterIngress, err error) { + result = &v1alpha1.ClusterIngress{} + err = c.client.Put(). + Resource("clusteringresses"). + Name(clusterIngress.Name). + Body(clusterIngress). + Do(). + Into(result) + return +} + +// UpdateStatus was generated because the type contains a Status member. +// Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). + +func (c *clusterIngresses) UpdateStatus(clusterIngress *v1alpha1.ClusterIngress) (result *v1alpha1.ClusterIngress, err error) { + result = &v1alpha1.ClusterIngress{} + err = c.client.Put(). + Resource("clusteringresses"). + Name(clusterIngress.Name). + SubResource("status"). + Body(clusterIngress). + Do(). + Into(result) + return +} + +// Delete takes name of the clusterIngress and deletes it. Returns an error if one occurs. +func (c *clusterIngresses) Delete(name string, options *v1.DeleteOptions) error { + return c.client.Delete(). + Resource("clusteringresses"). + Name(name). + Body(options). + Do(). + Error() +} + +// DeleteCollection deletes a collection of objects. +func (c *clusterIngresses) DeleteCollection(options *v1.DeleteOptions, listOptions v1.ListOptions) error { + return c.client.Delete(). + Resource("clusteringresses"). + VersionedParams(&listOptions, scheme.ParameterCodec). + Body(options). + Do(). + Error() +} + +// Patch applies the patch and returns the patched clusterIngress. +func (c *clusterIngresses) Patch(name string, pt types.PatchType, data []byte, subresources ...string) (result *v1alpha1.ClusterIngress, err error) { + result = &v1alpha1.ClusterIngress{} + err = c.client.Patch(pt). + Resource("clusteringresses"). + SubResource(subresources...). + Name(name). + Body(data). + Do(). + Into(result) + return +} diff --git a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/doc.go b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/doc.go new file mode 100644 index 00000000000..10ede9da2f0 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/doc.go @@ -0,0 +1,17 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// This package has the automatically generated typed clients. +package v1alpha1 diff --git a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/immutable.go b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/generated_expansion.go similarity index 87% rename from vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/immutable.go rename to vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/generated_expansion.go index d2883e221cd..73063b5d3de 100644 --- a/vendor/github.com/knative/serving/pkg/apis/serving/v1alpha1/immutable.go +++ b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/generated_expansion.go @@ -1,5 +1,6 @@ /* -Copyright 2017 The Knative Authors +Copyright 2018 The Knative Authors + Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at @@ -12,5 +13,6 @@ 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 v1alpha1 + +type ClusterIngressExpansion interface{} diff --git a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/istio_client.go b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/networking_client.go similarity index 59% rename from vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/istio_client.go rename to vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/networking_client.go index e2869ab0b29..3fb18887b2f 100644 --- a/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/istio/v1alpha3/istio_client.go +++ b/vendor/github.com/knative/serving/pkg/client/clientset/versioned/typed/networking/v1alpha1/networking_client.go @@ -13,36 +13,31 @@ 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 v1alpha3 +package v1alpha1 import ( - v1alpha3 "github.com/knative/serving/pkg/apis/istio/v1alpha3" + v1alpha1 "github.com/knative/serving/pkg/apis/networking/v1alpha1" "github.com/knative/serving/pkg/client/clientset/versioned/scheme" serializer "k8s.io/apimachinery/pkg/runtime/serializer" rest "k8s.io/client-go/rest" ) -type NetworkingV1alpha3Interface interface { +type NetworkingV1alpha1Interface interface { RESTClient() rest.Interface - GatewaysGetter - VirtualServicesGetter + ClusterIngressesGetter } -// NetworkingV1alpha3Client is used to interact with features provided by the networking.istio.io group. -type NetworkingV1alpha3Client struct { +// NetworkingV1alpha1Client is used to interact with features provided by the networking.internal.knative.dev group. +type NetworkingV1alpha1Client struct { restClient rest.Interface } -func (c *NetworkingV1alpha3Client) Gateways(namespace string) GatewayInterface { - return newGateways(c, namespace) +func (c *NetworkingV1alpha1Client) ClusterIngresses() ClusterIngressInterface { + return newClusterIngresses(c) } -func (c *NetworkingV1alpha3Client) VirtualServices(namespace string) VirtualServiceInterface { - return newVirtualServices(c, namespace) -} - -// NewForConfig creates a new NetworkingV1alpha3Client for the given config. -func NewForConfig(c *rest.Config) (*NetworkingV1alpha3Client, error) { +// NewForConfig creates a new NetworkingV1alpha1Client for the given config. +func NewForConfig(c *rest.Config) (*NetworkingV1alpha1Client, error) { config := *c if err := setConfigDefaults(&config); err != nil { return nil, err @@ -51,12 +46,12 @@ func NewForConfig(c *rest.Config) (*NetworkingV1alpha3Client, error) { if err != nil { return nil, err } - return &NetworkingV1alpha3Client{client}, nil + return &NetworkingV1alpha1Client{client}, nil } -// NewForConfigOrDie creates a new NetworkingV1alpha3Client for the given config and +// NewForConfigOrDie creates a new NetworkingV1alpha1Client for the given config and // panics if there is an error in the config. -func NewForConfigOrDie(c *rest.Config) *NetworkingV1alpha3Client { +func NewForConfigOrDie(c *rest.Config) *NetworkingV1alpha1Client { client, err := NewForConfig(c) if err != nil { panic(err) @@ -64,13 +59,13 @@ func NewForConfigOrDie(c *rest.Config) *NetworkingV1alpha3Client { return client } -// New creates a new NetworkingV1alpha3Client for the given RESTClient. -func New(c rest.Interface) *NetworkingV1alpha3Client { - return &NetworkingV1alpha3Client{c} +// New creates a new NetworkingV1alpha1Client for the given RESTClient. +func New(c rest.Interface) *NetworkingV1alpha1Client { + return &NetworkingV1alpha1Client{c} } func setConfigDefaults(config *rest.Config) error { - gv := v1alpha3.SchemeGroupVersion + gv := v1alpha1.SchemeGroupVersion config.GroupVersion = &gv config.APIPath = "/apis" config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs} @@ -84,7 +79,7 @@ func setConfigDefaults(config *rest.Config) error { // RESTClient returns a RESTClient that is used to communicate // with API server by this client implementation. -func (c *NetworkingV1alpha3Client) RESTClient() rest.Interface { +func (c *NetworkingV1alpha1Client) RESTClient() rest.Interface { if c == nil { return nil } diff --git a/vendor/github.com/knative/serving/pkg/controller/revision/config/testdata/config-controller.yaml b/vendor/github.com/knative/serving/pkg/controller/revision/config/testdata/config-controller.yaml deleted file mode 120000 index f1afbe74ee7..00000000000 --- a/vendor/github.com/knative/serving/pkg/controller/revision/config/testdata/config-controller.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../../config/config-controller.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/pkg/controller/revision/config/testdata/config-network.yaml b/vendor/github.com/knative/serving/pkg/controller/revision/config/testdata/config-network.yaml deleted file mode 120000 index b774d24cbb4..00000000000 --- a/vendor/github.com/knative/serving/pkg/controller/revision/config/testdata/config-network.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../../config/config-network.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/pkg/controller/revision/config/testdata/config-observability.yaml b/vendor/github.com/knative/serving/pkg/controller/revision/config/testdata/config-observability.yaml deleted file mode 120000 index ecbbeaaee73..00000000000 --- a/vendor/github.com/knative/serving/pkg/controller/revision/config/testdata/config-observability.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../../config/config-observability.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/pkg/controller/route/config/testdata/config-domain.yaml b/vendor/github.com/knative/serving/pkg/controller/route/config/testdata/config-domain.yaml deleted file mode 120000 index fd6402b7c48..00000000000 --- a/vendor/github.com/knative/serving/pkg/controller/route/config/testdata/config-domain.yaml +++ /dev/null @@ -1 +0,0 @@ -../../../../../config/config-domain.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/pkg/gc/testdata/config-gc.yaml b/vendor/github.com/knative/serving/pkg/gc/testdata/config-gc.yaml new file mode 120000 index 00000000000..f6ee95a3269 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/gc/testdata/config-gc.yaml @@ -0,0 +1 @@ +../../../config/config-gc.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/configuration/config/testdata/config-gc.yaml b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/configuration/config/testdata/config-gc.yaml new file mode 120000 index 00000000000..dc98107b5c4 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/configuration/config/testdata/config-gc.yaml @@ -0,0 +1 @@ +../../../../../../config/config-gc.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-autoscaler.yaml b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-autoscaler.yaml new file mode 120000 index 00000000000..17e4b72c26e --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-autoscaler.yaml @@ -0,0 +1 @@ +../../../../../../config/config-autoscaler.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-controller.yaml b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-controller.yaml new file mode 120000 index 00000000000..b8047a772b9 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-controller.yaml @@ -0,0 +1 @@ +../../../../../../config/config-controller.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-logging.yaml b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-logging.yaml new file mode 120000 index 00000000000..cd048f0179d --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-logging.yaml @@ -0,0 +1 @@ +../../../../../../config/config-logging.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-network.yaml b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-network.yaml new file mode 120000 index 00000000000..f17cdcd38ac --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-network.yaml @@ -0,0 +1 @@ +../../../../../../config/config-network.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-observability.yaml b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-observability.yaml new file mode 120000 index 00000000000..a89f2a95310 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/revision/config/testdata/config-observability.yaml @@ -0,0 +1 @@ +../../../../../../config/config-observability.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/route/config/testdata/config-domain.yaml b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/route/config/testdata/config-domain.yaml new file mode 120000 index 00000000000..41119172560 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/route/config/testdata/config-domain.yaml @@ -0,0 +1 @@ +../../../../../../config/config-domain.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/route/config/testdata/config-gc.yaml b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/route/config/testdata/config-gc.yaml new file mode 120000 index 00000000000..dc98107b5c4 --- /dev/null +++ b/vendor/github.com/knative/serving/pkg/reconciler/v1alpha1/route/config/testdata/config-gc.yaml @@ -0,0 +1 @@ +../../../../../../config/config-gc.yaml \ No newline at end of file diff --git a/vendor/github.com/knative/serving/third_party/config/monitoring/common/kubernetes/LICENSE b/vendor/github.com/knative/serving/third_party/config/monitoring/logging/elasticsearch/LICENSE similarity index 100% rename from vendor/github.com/knative/serving/third_party/config/monitoring/common/kubernetes/LICENSE rename to vendor/github.com/knative/serving/third_party/config/monitoring/logging/elasticsearch/LICENSE diff --git a/vendor/github.com/knative/serving/third_party/config/monitoring/common/istio/LICENSE b/vendor/github.com/knative/serving/third_party/config/monitoring/metrics/prometheus/istio/LICENSE similarity index 100% rename from vendor/github.com/knative/serving/third_party/config/monitoring/common/istio/LICENSE rename to vendor/github.com/knative/serving/third_party/config/monitoring/metrics/prometheus/istio/LICENSE diff --git a/vendor/github.com/knative/serving/third_party/config/monitoring/elasticsearch/LICENSE b/vendor/github.com/knative/serving/third_party/config/monitoring/metrics/prometheus/kubernetes/LICENSE similarity index 100% rename from vendor/github.com/knative/serving/third_party/config/monitoring/elasticsearch/LICENSE rename to vendor/github.com/knative/serving/third_party/config/monitoring/metrics/prometheus/kubernetes/LICENSE diff --git a/vendor/github.com/knative/serving/third_party/config/monitoring/common/prometheus-operator/LICENSE b/vendor/github.com/knative/serving/third_party/config/monitoring/metrics/prometheus/prometheus-operator/LICENSE similarity index 100% rename from vendor/github.com/knative/serving/third_party/config/monitoring/common/prometheus-operator/LICENSE rename to vendor/github.com/knative/serving/third_party/config/monitoring/metrics/prometheus/prometheus-operator/LICENSE diff --git a/vendor/github.com/knative/serving/third_party/config/monitoring/common/prometheus-operator/NOTICE b/vendor/github.com/knative/serving/third_party/config/monitoring/metrics/prometheus/prometheus-operator/NOTICE similarity index 100% rename from vendor/github.com/knative/serving/third_party/config/monitoring/common/prometheus-operator/NOTICE rename to vendor/github.com/knative/serving/third_party/config/monitoring/metrics/prometheus/prometheus-operator/NOTICE From 3ffd634b6b5b93116696e1a0f0a3f60d643100f5 Mon Sep 17 00:00:00 2001 From: Grant Rodgers Date: Mon, 12 Nov 2018 12:05:45 -0800 Subject: [PATCH 41/54] Remove manual pruning of knative/test-infra (#606) This was removing tools we wanted to keep, like dep-collector. --- hack/update-deps.sh | 3 - .../.github/pull-request-template.md | 7 + .../knative/test-infra/CONTRIBUTING.md | 3 + .../github.com/knative/test-infra/Gopkg.lock | 28 + .../github.com/knative/test-infra/Gopkg.toml | 14 + vendor/github.com/knative/test-infra/LICENSE | 202 ++ vendor/github.com/knative/test-infra/OWNERS | 9 + .../github.com/knative/test-infra/README.md | 17 + .../github.com/knative/test-infra/WORKSPACE | 52 + .../knative/test-infra/ci/README.md | 3 + .../knative/test-infra/ci/gubernator/Makefile | 33 + .../test-infra/ci/gubernator/README.md | 7 + .../test-infra/ci/gubernator/config.yaml | 71 + .../test-infra/ci/gubernator/redir_github.py | 25 + .../knative/test-infra/ci/prow/Makefile | 42 + .../knative/test-infra/ci/prow/README.md | 10 + .../test-infra/ci/prow/boskos/README.md | 6 + .../test-infra/ci/prow/boskos/config.yaml | 152 ++ .../ci/prow/boskos/config_start.yaml | 23 + .../test-infra/ci/prow/boskos/resources.yaml | 38 + .../knative/test-infra/ci/prow/cluster.yaml | 350 +++ .../knative/test-infra/ci/prow/config.yaml | 2250 +++++++++++++++++ .../test-infra/ci/prow/config_start.yaml | 339 +++ .../knative/test-infra/ci/prow/plugins.yaml | 41 + .../knative/test-infra/ci/prow/prow_setup.md | 71 + .../knative/test-infra/ci/testgrid/Makefile | 29 + .../knative/test-infra/ci/testgrid/README.md | 6 + .../test-infra/ci/testgrid/config.yaml | 216 ++ vendor/github.com/knative/test-infra/dummy.go | 27 + .../knative/test-infra/images/README.md | 3 + .../test-infra/images/apicoverage/Dockerfile | 20 + .../test-infra/images/apicoverage/Makefile | 23 + .../test-infra/images/apicoverage/README.md | 3 + .../test-infra/images/prow-tests/Dockerfile | 56 + .../test-infra/images/prow-tests/Makefile | 34 + .../test-infra/images/prow-tests/README.md | 13 + .../knative/test-infra/test/e2e-tests.sh | 50 + .../test-infra/test/presubmit-tests.sh | 50 + .../test/unit/e2e-custom-flag-tests.sh | 38 + .../test-infra/test/unit/library-tests.sh | 50 + ...presubmit-full-custom-integration-tests.sh | 28 + .../presubmit-integration-tests-common.sh | 48 + ...submit-partial-custom-integration-tests.sh | 33 + .../test-infra/test/unit/release-tests.sh | 108 + .../knative/test-infra/tools/README.md | 3 + .../test-infra/tools/apicoverage/README.md | 14 + .../tools/apicoverage/apicoverage.go | 241 ++ .../test-infra/tools/dep-collector/README.md | 88 + .../test-infra/tools/dep-collector/imports.go | 94 + .../tools/dep-collector/licenses.go | 203 ++ .../test-infra/tools/dep-collector/main.go | 81 + .../knative/test-infra/tools/gcs/gcs.go | 112 + .../test-infra/tools/githubhelper/Makefile | 17 + .../test-infra/tools/githubhelper/README.md | 10 + .../tools/githubhelper/githubhelper.go | 85 + .../test-infra/tools/testgrid/testgrid.go | 69 + 56 files changed, 5645 insertions(+), 3 deletions(-) create mode 100644 vendor/github.com/knative/test-infra/.github/pull-request-template.md create mode 100644 vendor/github.com/knative/test-infra/CONTRIBUTING.md create mode 100644 vendor/github.com/knative/test-infra/Gopkg.lock create mode 100644 vendor/github.com/knative/test-infra/Gopkg.toml create mode 100644 vendor/github.com/knative/test-infra/LICENSE create mode 100644 vendor/github.com/knative/test-infra/OWNERS create mode 100644 vendor/github.com/knative/test-infra/README.md create mode 100644 vendor/github.com/knative/test-infra/WORKSPACE create mode 100644 vendor/github.com/knative/test-infra/ci/README.md create mode 100644 vendor/github.com/knative/test-infra/ci/gubernator/Makefile create mode 100644 vendor/github.com/knative/test-infra/ci/gubernator/README.md create mode 100644 vendor/github.com/knative/test-infra/ci/gubernator/config.yaml create mode 100644 vendor/github.com/knative/test-infra/ci/gubernator/redir_github.py create mode 100644 vendor/github.com/knative/test-infra/ci/prow/Makefile create mode 100644 vendor/github.com/knative/test-infra/ci/prow/README.md create mode 100644 vendor/github.com/knative/test-infra/ci/prow/boskos/README.md create mode 100644 vendor/github.com/knative/test-infra/ci/prow/boskos/config.yaml create mode 100644 vendor/github.com/knative/test-infra/ci/prow/boskos/config_start.yaml create mode 100644 vendor/github.com/knative/test-infra/ci/prow/boskos/resources.yaml create mode 100644 vendor/github.com/knative/test-infra/ci/prow/cluster.yaml create mode 100644 vendor/github.com/knative/test-infra/ci/prow/config.yaml create mode 100644 vendor/github.com/knative/test-infra/ci/prow/config_start.yaml create mode 100644 vendor/github.com/knative/test-infra/ci/prow/plugins.yaml create mode 100644 vendor/github.com/knative/test-infra/ci/prow/prow_setup.md create mode 100644 vendor/github.com/knative/test-infra/ci/testgrid/Makefile create mode 100644 vendor/github.com/knative/test-infra/ci/testgrid/README.md create mode 100644 vendor/github.com/knative/test-infra/ci/testgrid/config.yaml create mode 100644 vendor/github.com/knative/test-infra/dummy.go create mode 100644 vendor/github.com/knative/test-infra/images/README.md create mode 100644 vendor/github.com/knative/test-infra/images/apicoverage/Dockerfile create mode 100644 vendor/github.com/knative/test-infra/images/apicoverage/Makefile create mode 100644 vendor/github.com/knative/test-infra/images/apicoverage/README.md create mode 100644 vendor/github.com/knative/test-infra/images/prow-tests/Dockerfile create mode 100644 vendor/github.com/knative/test-infra/images/prow-tests/Makefile create mode 100644 vendor/github.com/knative/test-infra/images/prow-tests/README.md create mode 100755 vendor/github.com/knative/test-infra/test/e2e-tests.sh create mode 100755 vendor/github.com/knative/test-infra/test/presubmit-tests.sh create mode 100755 vendor/github.com/knative/test-infra/test/unit/e2e-custom-flag-tests.sh create mode 100755 vendor/github.com/knative/test-infra/test/unit/library-tests.sh create mode 100755 vendor/github.com/knative/test-infra/test/unit/presubmit-full-custom-integration-tests.sh create mode 100755 vendor/github.com/knative/test-infra/test/unit/presubmit-integration-tests-common.sh create mode 100755 vendor/github.com/knative/test-infra/test/unit/presubmit-partial-custom-integration-tests.sh create mode 100755 vendor/github.com/knative/test-infra/test/unit/release-tests.sh create mode 100644 vendor/github.com/knative/test-infra/tools/README.md create mode 100644 vendor/github.com/knative/test-infra/tools/apicoverage/README.md create mode 100644 vendor/github.com/knative/test-infra/tools/apicoverage/apicoverage.go create mode 100644 vendor/github.com/knative/test-infra/tools/dep-collector/README.md create mode 100644 vendor/github.com/knative/test-infra/tools/dep-collector/imports.go create mode 100644 vendor/github.com/knative/test-infra/tools/dep-collector/licenses.go create mode 100644 vendor/github.com/knative/test-infra/tools/dep-collector/main.go create mode 100644 vendor/github.com/knative/test-infra/tools/gcs/gcs.go create mode 100644 vendor/github.com/knative/test-infra/tools/githubhelper/Makefile create mode 100644 vendor/github.com/knative/test-infra/tools/githubhelper/README.md create mode 100644 vendor/github.com/knative/test-infra/tools/githubhelper/githubhelper.go create mode 100644 vendor/github.com/knative/test-infra/tools/testgrid/testgrid.go diff --git a/hack/update-deps.sh b/hack/update-deps.sh index e4ef7ab9542..5d7bd82fc59 100755 --- a/hack/update-deps.sh +++ b/hack/update-deps.sh @@ -28,8 +28,5 @@ dep ensure rm -rf $(find vendor/ -name 'BUILD') rm -rf $(find vendor/ -name 'BUILD.bazel') -# Keep the only dir in knative/test-infra we're interested in -find vendor/github.com/knative/test-infra -mindepth 1 -maxdepth 1 ! -name scripts -exec rm -fr {} \; - update_licenses third_party/VENDOR-LICENSE \ $(find . -name "*.go" | grep -v vendor | xargs grep "package main" | cut -d: -f1 | xargs -n1 dirname | uniq) diff --git a/vendor/github.com/knative/test-infra/.github/pull-request-template.md b/vendor/github.com/knative/test-infra/.github/pull-request-template.md new file mode 100644 index 00000000000..9b2b7820f61 --- /dev/null +++ b/vendor/github.com/knative/test-infra/.github/pull-request-template.md @@ -0,0 +1,7 @@ + + +Fixes # diff --git a/vendor/github.com/knative/test-infra/CONTRIBUTING.md b/vendor/github.com/knative/test-infra/CONTRIBUTING.md new file mode 100644 index 00000000000..bcfe857fda4 --- /dev/null +++ b/vendor/github.com/knative/test-infra/CONTRIBUTING.md @@ -0,0 +1,3 @@ +# Contribution guidelines + +So you want to hack on Knative Test Infrastructure? Yay! Please refer to Knative's overall [contribution guidelines](https://github.com/knative/docs/blob/master/community/CONTRIBUTING.md) to find out how you can help. diff --git a/vendor/github.com/knative/test-infra/Gopkg.lock b/vendor/github.com/knative/test-infra/Gopkg.lock new file mode 100644 index 00000000000..e0347ada5a9 --- /dev/null +++ b/vendor/github.com/knative/test-infra/Gopkg.lock @@ -0,0 +1,28 @@ +# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'. + + +[[projects]] + branch = "master" + name = "github.com/google/licenseclassifier" + packages = [ + ".", + "internal/sets", + "stringclassifier", + "stringclassifier/internal/pq", + "stringclassifier/searchset", + "stringclassifier/searchset/tokenizer" + ] + revision = "3c8ad1f0b0644b6646210ee9cf2f34ff907e2e18" + +[[projects]] + name = "github.com/sergi/go-diff" + packages = ["diffmatchpatch"] + revision = "1744e2970ca51c86172c8190fadad617561ed6e7" + version = "v1.0.0" + +[solve-meta] + analyzer-name = "dep" + analyzer-version = 1 + inputs-digest = "aea50f9014005bedc3dc202c5fbf9d2d8c7a6f7beac2337fd863b23f411c4125" + solver-name = "gps-cdcl" + solver-version = 1 diff --git a/vendor/github.com/knative/test-infra/Gopkg.toml b/vendor/github.com/knative/test-infra/Gopkg.toml new file mode 100644 index 00000000000..1a03ba55a89 --- /dev/null +++ b/vendor/github.com/knative/test-infra/Gopkg.toml @@ -0,0 +1,14 @@ +# Refer to https://golang.github.io/dep/docs/Gopkg.toml.html +# for detailed Gopkg.toml documentation. + +required = [ + "github.com/google/licenseclassifier/licenses", +] + +# TODO(mattmoor): Find a way to bundle the licenseclassifier's +# license database, so folks don't have to go get it. + +[prune] + go-tests = true + unused-packages = true + non-go = true diff --git a/vendor/github.com/knative/test-infra/LICENSE b/vendor/github.com/knative/test-infra/LICENSE new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/vendor/github.com/knative/test-infra/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/knative/test-infra/OWNERS b/vendor/github.com/knative/test-infra/OWNERS new file mode 100644 index 00000000000..ed8dd603cf2 --- /dev/null +++ b/vendor/github.com/knative/test-infra/OWNERS @@ -0,0 +1,9 @@ +# The OWNERS file is used by prow to automatically merge approved PRs. + +approvers: +- adrcunha +- dushyanthsc +- ericKlawitter +- jessiezcc +- srinivashegde86 +- steuhs diff --git a/vendor/github.com/knative/test-infra/README.md b/vendor/github.com/knative/test-infra/README.md new file mode 100644 index 00000000000..88f40521e11 --- /dev/null +++ b/vendor/github.com/knative/test-infra/README.md @@ -0,0 +1,17 @@ +# Knative Test Infrastructure + +The `test-infra` repository contains a collection of tools for testing Knative, collecting metrics +and displaying test results. + +## High level architecture + +Knative uses [Prow](https://github.com/kubernetes/test-infra/tree/master/prow) to schedule testing and update issues. + +### Gubernator + +Knative uses [gubernator](https://github.com/kubernetes/test-infra) to provide +a [PR dashboard](https://gubernator.knative.dev/pr) for contributions in the Knative github organization. + +### E2E Testing + +Our E2E testing uses [kubetest](https://github.com/kubernetes/test-infra/blob/master/kubetest) to build/deploy/test Knative clusters. diff --git a/vendor/github.com/knative/test-infra/WORKSPACE b/vendor/github.com/knative/test-infra/WORKSPACE new file mode 100644 index 00000000000..91db673d566 --- /dev/null +++ b/vendor/github.com/knative/test-infra/WORKSPACE @@ -0,0 +1,52 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Required rules for building kubernetes/test-infra +# These all come from http://github.com/kubernetes/test-infra/blob/master/WORKSPACE + +http_archive( + name = "io_bazel_rules_go", + sha256 = "1868ff68d6079e31b2f09b828b58d62e57ca8e9636edff699247c9108518570b", + url = "https://github.com/bazelbuild/rules_go/releases/download/0.11.1/rules_go-0.11.1.tar.gz", +) + +load("@io_bazel_rules_go//go:def.bzl", "go_rules_dependencies", "go_register_toolchains") + +go_rules_dependencies() + +go_register_toolchains( + go_version = "1.10.2", +) + +git_repository( + name = "io_bazel_rules_k8s", + commit = "3756369d4920033c32c12d16207e8ee14fee1b18", + remote = "https://github.com/bazelbuild/rules_k8s.git", +) + +http_archive( + name = "io_bazel_rules_docker", + sha256 = "cef4e7adfc1df999891e086bf42bed9092cfdf374adb902f18de2c1d6e1e0197", + strip_prefix = "rules_docker-198367210c55fba5dded22274adde1a289801dc4", + urls = ["https://github.com/bazelbuild/rules_docker/archive/198367210c55fba5dded22274adde1a289801dc4.tar.gz"], +) + +# External repositories + +git_repository( + name = "k8s", + remote = "http://github.com/kubernetes/test-infra.git", + commit = "dd12621d6178838097847abf5842ad8d08fc9308", # HEAD as of 8/1/2018 +) + diff --git a/vendor/github.com/knative/test-infra/ci/README.md b/vendor/github.com/knative/test-infra/ci/README.md new file mode 100644 index 00000000000..51b28eddac4 --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/README.md @@ -0,0 +1,3 @@ +# Continuous Integration / Continuous Deployment system + +This directory contains the configs for all systems related to Knative's CI/CD system. diff --git a/vendor/github.com/knative/test-infra/ci/gubernator/Makefile b/vendor/github.com/knative/test-infra/ci/gubernator/Makefile new file mode 100644 index 00000000000..5307d95d94f --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/gubernator/Makefile @@ -0,0 +1,33 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +SRC := test-infra/gubernator + +deploy: + # Fetch latest source, patch for our instance + rm -fr test-infra + git clone http://github.com/kubernetes/test-infra.git + cp config.yaml $(SRC) + cp redir_github.py $(SRC) + sed -i -e '/^runtime: .*/a service: gubernator' $(SRC)/app.yaml + sed -i -e "/^handlers:/a\- url: /timeline\n script: redir_github.app\n" $(SRC)/app.yaml + sed -i -e 's/user:kubernetes/user:knative/' $(SRC)/view_pr.py + sed -i -e 's/Kubernetes/Knative/' $(SRC)/templates/index.html + sed -i -e 's/k8s-testgrid.appspot.com/testgrid.knative.dev/' $(SRC)/filters.py + sed -i -e 's/k8s-testgrid/knative-testgrid/' $(SRC)/testgrid.py + # Deploy + make -C ../prow get-cluster-credentials + PROJECT=knative-tests make -C $(SRC) deploy + # Cleanup + rm -fr test-infra diff --git a/vendor/github.com/knative/test-infra/ci/gubernator/README.md b/vendor/github.com/knative/test-infra/ci/gubernator/README.md new file mode 100644 index 00000000000..14508c3dfda --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/gubernator/README.md @@ -0,0 +1,7 @@ +# Gubernator config + +This directory contains the config for our [Gubernator](https://github.com/kubernetes/test-infra/tree/master/gubernator) instance, plus a makefile for deploying it. + +* `config.yaml` Gubernator configuration. +* `Makefile` Recipe for deploying a Gubernator instance. +* `redir_github.py` Simple redirection handler to Gubernator's GitHub service. diff --git a/vendor/github.com/knative/test-infra/ci/gubernator/config.yaml b/vendor/github.com/knative/test-infra/ci/gubernator/config.yaml new file mode 100644 index 00000000000..794b4ce2bdd --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/gubernator/config.yaml @@ -0,0 +1,71 @@ + +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +default_external_services: + gcs_pull_prefix: knative-prow/pr-logs/pull + prow_url: prow.knative.dev +default_org: knative +default_repo: serving +external_services: + knative: + gcs_bucket: knative-prow/ + gcs_pull_prefix: knative-prow/pr-logs/pull + prow_url: prow.knative.dev +jobs: + knative-prow/pr-logs/directory/: + - pull-knative-serving-build-tests + - pull-knative-serving-integration-tests + - pull-knative-serving-unit-tests + - pull-knative-eventing-build-tests + - pull-knative-eventing-integration-tests + - pull-knative-eventing-unit-tests + - pull-knative-eventing-sources-build-tests + - pull-knative-eventing-sources-integration-tests + - pull-knative-eventing-sources-unit-tests + - pull-knative-docs-build-tests + - pull-knative-docs-unit-tests + - pull-knative-docs-integration-tests + - pull-knative-build-templates-unit-tests + - pull-knative-build-templates-build-tests + - pull-knative-build-templates-integration-tests + - pull-knative-build-pipeline-build-tests + - pull-knative-build-pipeline-unit-tests + - pull-knative-build-build-tests + - pull-knative-build-unit-tests + - pull-knative-build-integration-tests + - pull-knative-pkg-build-tests + - pull-knative-pkg-unit-tests + - pull-knative-pkg-integration-tests + - pull-knative-test-infra-build-tests + - pull-knative-test-infra-unit-tests + - pull-knative-test-infra-integration-tests + - pull-knative-caching-build-tests + - pull-knative-caching-unit-tests + - pull-knative-caching-integration-tests + knative-prow/logs/: + - ci-knative-serving-continuous + - ci-knative-serving-release + - ci-knative-serving-playground + - ci-knative-build-continuous + - ci-knative-build-release + - ci-knative-eventing-continuous + - ci-knative-eventing-release + - ci-knative-eventing-sources-continuous + - ci-knative-eventing-sources-release + - ci-knative-build-templates-continuous + - ci-knative-docs-continuous + - ci-knative-pkg-continuous + - ci-knative-caching-continuous +recursive_artifacts: false diff --git a/vendor/github.com/knative/test-infra/ci/gubernator/redir_github.py b/vendor/github.com/knative/test-infra/ci/gubernator/redir_github.py new file mode 100644 index 00000000000..e168d6adbc5 --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/gubernator/redir_github.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python + +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +"""Simple redirection handler to Gubernator's GitHub service.""" + +import webapp2 + +class GitHubRedirect(webapp2.RequestHandler): + def get(self): + self.redirect("https://github-dot-knative-tests.appspot.com" + self.request.path_qs) + +app = webapp2.WSGIApplication([(r'/.*', GitHubRedirect),], debug=True, config={}) diff --git a/vendor/github.com/knative/test-infra/ci/prow/Makefile b/vendor/github.com/knative/test-infra/ci/prow/Makefile new file mode 100644 index 00000000000..9b6fcfbea17 --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/prow/Makefile @@ -0,0 +1,42 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +CLUSTER ?= prow +PROJECT ?= knative-tests +ZONE ?= us-central1-f +JOB_NAMESPACE ?= test-pods + +PROW_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +get-cluster-credentials: + gcloud container clusters get-credentials "$(CLUSTER)" --project="$(PROJECT)" --zone="$(ZONE)" + +update-config: get-cluster-credentials + kubectl create configmap config --from-file=config.yaml=config.yaml --dry-run -o yaml | kubectl replace configmap config -f - + +update-plugins: get-cluster-credentials + kubectl create configmap plugins --from-file=plugins.yaml=plugins.yaml --dry-run -o yaml | kubectl replace configmap plugins -f - + +update-boskos: get-cluster-credentials + kubectl apply -f boskos/config.yaml + +update-boskos-config: get-cluster-credentials + kubectl create configmap resources --from-file=config=boskos/resources.yaml --dry-run -o yaml | kubectl --namespace="$(JOB_NAMESPACE)" replace configmap resources -f - + +update-cluster: get-cluster-credentials + kubectl apply -f cluster.yaml + +test: + bazel run @k8s//prow/cmd/config -- --plugin-config=$(PROW_DIR)/plugins.yaml + bazel run @k8s//prow/cmd/config -- --config-path=$(PROW_DIR)/config.yaml diff --git a/vendor/github.com/knative/test-infra/ci/prow/README.md b/vendor/github.com/knative/test-infra/ci/prow/README.md new file mode 100644 index 00000000000..04fd12e6bb2 --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/prow/README.md @@ -0,0 +1,10 @@ +# Prow config + +This directory contains the config for our [Prow](https://github.com/kubernetes/test-infra/tree/master/prow) instance. + +* `boskos` Configuration for the Boskos instance. +* `Makefile` Commands to interact with the Prow instance regarding updates. +* `cluster.yaml` Configuration of the Prow cluster. +* `config.yaml` Configuration of the Prow jobs. +* `config_start.yaml` Initial, empty configuration for Prow. +* `plugins.yaml` Configuration of the Prow plugins. diff --git a/vendor/github.com/knative/test-infra/ci/prow/boskos/README.md b/vendor/github.com/knative/test-infra/ci/prow/boskos/README.md new file mode 100644 index 00000000000..8e6e90ab6b1 --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/prow/boskos/README.md @@ -0,0 +1,6 @@ +# Boskos config + +This directory contains the config for our [Boskos](https://github.com/kubernetes/test-infra/tree/master/boskos) instance. + +* `config.yaml` Boskos configuration. +* `resources.yaml` Pool of projects used by Boskos. diff --git a/vendor/github.com/knative/test-infra/ci/prow/boskos/config.yaml b/vendor/github.com/knative/test-infra/ci/prow/boskos/config.yaml new file mode 100644 index 00000000000..a444d6cff8b --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/prow/boskos/config.yaml @@ -0,0 +1,152 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Boskos deployment for Knative Prow instance +--- +apiVersion: v1 +kind: PersistentVolume +metadata: + labels: + app: boskos + name: boskos-storage + namespace: test-pods +spec: + claimRef: + name: boskos-volume-boskos-0 + namespace: test-pods + capacity: + storage: 1Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + gcePersistentDisk: + pdName: boskos-storage + fsType: ext4 +--- +# Start of StatefulSet +apiVersion: apps/v1beta1 +kind: StatefulSet +metadata: + name: boskos + namespace: test-pods +spec: + serviceName: "boskos" + replicas: 1 # one canonical source of resources + template: + metadata: + labels: + app: boskos + namespace: test-pods + spec: + serviceAccountName: "boskos" + terminationGracePeriodSeconds: 30 + containers: + - name: boskos + image: gcr.io/k8s-testimages/boskos:v20180405-12e892d69 + args: + - --storage=/store/boskos.json + - --config=/etc/config/config + - --namespace=test-pods + ports: + - containerPort: 8080 + protocol: TCP + volumeMounts: + - name: boskos-volume + mountPath: /store + - name: boskos-config + mountPath: /etc/config + readOnly: true + volumes: + - name: boskos-config + configMap: + name: resources + volumeClaimTemplates: + - metadata: + name: boskos-volume + spec: + accessModes: [ "ReadWriteOnce" ] + storageClassName: boskos + resources: + requests: + storage: 1Gi +--- +apiVersion: v1 +kind: Service +metadata: + name: boskos + namespace: test-pods +spec: + selector: + app: boskos + ports: + - name: default + protocol: TCP + port: 80 + targetPort: 8080 +--- +# Janitor +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: boskos-janitor + labels: + app: boskos-janitor + namespace: test-pods +spec: + replicas: 3 # 3 distributed janitor instances + template: + metadata: + labels: + app: boskos-janitor + spec: + serviceAccountName: "boskos" + terminationGracePeriodSeconds: 300 + containers: + - name: boskos-janitor + image: gcr.io/k8s-testimages/janitor:v20180619-83c62c891 + args: + - --service-account=/etc/service-account/service-account.json + - --resource-type=gke-project + - --pool-size=10 + volumeMounts: + - mountPath: /etc/service-account + name: service + readOnly: true + volumes: + - name: service + secret: + secretName: service-account +--- +# Reaper +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: boskos-reaper + labels: + app: boskos-reaper + namespace: test-pods +spec: + replicas: 1 # one canonical source of resources + template: + metadata: + labels: + app: boskos-reaper + spec: + serviceAccountName: "boskos" + terminationGracePeriodSeconds: 30 + containers: + - name: boskos-reaper + image: gcr.io/k8s-testimages/reaper:v20180402-43203f868 + args: + - --resource-type=gke-project diff --git a/vendor/github.com/knative/test-infra/ci/prow/boskos/config_start.yaml b/vendor/github.com/knative/test-infra/ci/prow/boskos/config_start.yaml new file mode 100644 index 00000000000..dc8d66b6295 --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/prow/boskos/config_start.yaml @@ -0,0 +1,23 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Initial configuration of Boskos + +apiVersion: v1 +kind: ConfigMap +metadata: + name: resources + namespace: test-pods +data: + resources: "" diff --git a/vendor/github.com/knative/test-infra/ci/prow/boskos/resources.yaml b/vendor/github.com/knative/test-infra/ci/prow/boskos/resources.yaml new file mode 100644 index 00000000000..58f734358f6 --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/prow/boskos/resources.yaml @@ -0,0 +1,38 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +resources: +- names: + - knative-boskos-01 + - knative-boskos-02 + - knative-boskos-03 + - knative-boskos-04 + - knative-boskos-05 + - knative-boskos-06 + - knative-boskos-07 + - knative-boskos-08 + - knative-boskos-09 + - knative-boskos-10 + - knative-boskos-11 + - knative-boskos-12 + - knative-boskos-13 + - knative-boskos-14 + - knative-boskos-15 + - knative-boskos-16 + - knative-boskos-17 + - knative-boskos-18 + - knative-boskos-19 + - knative-boskos-20 + state: dirty + type: gke-project diff --git a/vendor/github.com/knative/test-infra/ci/prow/cluster.yaml b/vendor/github.com/knative/test-infra/ci/prow/cluster.yaml new file mode 100644 index 00000000000..5031611756f --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/prow/cluster.yaml @@ -0,0 +1,350 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This file contains Kubernetes YAML files for the most important prow components. +--- +apiVersion: apiextensions.k8s.io/v1beta1 +kind: CustomResourceDefinition +metadata: + name: prowjobs.prow.k8s.io +spec: + group: prow.k8s.io + version: v1 + names: + kind: ProwJob + singular: prowjob + plural: prowjobs + scope: Namespaced + validation: + openAPIV3Schema: + properties: + spec: + properties: + max_concurrency: + type: integer + minimum: 0 + type: + type: string + enum: + - "presubmit" + - "postsubmit" + - "periodic" + - "batch" + status: + properties: + state: + type: string + enum: + - "triggered" + - "pending" + - "success" + - "failure" + - "aborted" + - "error" + anyOf: + - not: + properties: + state: + type: string + enum: + - "success" + - "failure" + - "error" + - "aborted" + - required: + - completionTime +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: hook + labels: + app: hook +spec: + replicas: 2 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + template: + metadata: + labels: + app: hook + spec: + serviceAccountName: "hook" + terminationGracePeriodSeconds: 180 + containers: + - name: hook + image: gcr.io/k8s-prow/hook:v20181023-ca14137 + imagePullPolicy: Always + args: + - --dry-run=false + ports: + - name: http + containerPort: 8888 + volumeMounts: + - name: hmac + mountPath: /etc/webhook + readOnly: true + - name: oauth + mountPath: /etc/github + readOnly: true + - name: config + mountPath: /etc/config + readOnly: true + - name: plugins + mountPath: /etc/plugins + readOnly: true + volumes: + - name: hmac + secret: + secretName: hmac-token + - name: oauth + secret: + secretName: oauth-token + - name: config + configMap: + name: config + - name: plugins + configMap: + name: plugins +--- +apiVersion: v1 +kind: Service +metadata: + name: hook +spec: + selector: + app: hook + ports: + - port: 8888 + type: NodePort +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: plank + labels: + app: plank +spec: + replicas: 1 # Do not scale up. + template: + metadata: + labels: + app: plank + spec: + serviceAccountName: "plank" + containers: + - name: plank + image: gcr.io/k8s-prow/plank:v20180709-7109caeb1 + args: + - --dry-run=false + volumeMounts: + - name: oauth + mountPath: /etc/github + readOnly: true + - name: config + mountPath: /etc/config + readOnly: true + volumes: + - name: oauth + secret: + secretName: oauth-token + - name: config + configMap: + name: config +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: sinker + labels: + app: sinker +spec: + replicas: 1 + template: + metadata: + labels: + app: sinker + spec: + serviceAccountName: "sinker" + containers: + - name: sinker + image: gcr.io/k8s-prow/sinker:v20180709-7109caeb1 + volumeMounts: + - name: config + mountPath: /etc/config + readOnly: true + volumes: + - name: config + configMap: + name: config +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: deck + labels: + app: deck +spec: + replicas: 2 + strategy: + type: RollingUpdate + rollingUpdate: + maxSurge: 1 + maxUnavailable: 1 + template: + metadata: + labels: + app: deck + spec: + serviceAccountName: "deck" + terminationGracePeriodSeconds: 30 + containers: + - name: deck + image: gcr.io/k8s-prow/deck:v20180709-7109caeb1 + args: + - --hook-url=http://hook:8888/plugin-help + - --tide-url=http://tide/ + ports: + - name: http + containerPort: 8080 + volumeMounts: + - name: config + mountPath: /etc/config + readOnly: true + volumes: + - name: config + configMap: + name: config +--- +apiVersion: v1 +kind: Service +metadata: + name: deck +spec: + selector: + app: deck + ports: + - port: 80 + targetPort: 8080 + type: NodePort +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: horologium + labels: + app: horologium +spec: + replicas: 1 + template: + metadata: + labels: + app: horologium + spec: + serviceAccountName: "horologium" + terminationGracePeriodSeconds: 30 + containers: + - name: horologium + image: gcr.io/k8s-prow/horologium:v20180709-7109caeb1 + volumeMounts: + - name: config + mountPath: /etc/config + readOnly: true + volumes: + - name: config + configMap: + name: config + +# Ingresses + +--- +apiVersion: extensions/v1beta1 +kind: Ingress +metadata: + name: deck-ing + annotations: + kubernetes.io/ingress.class: "gce" + kubernetes.io/ingress.global-static-ip-name: prow-ingress +spec: + tls: + - secretName: tls-secret + hosts: + - prow.knative.dev + rules: + - host: prow.knative.dev + http: + paths: + - path: /* + backend: + serviceName: deck + servicePort: 80 + - path: /hook + backend: + serviceName: hook + servicePort: 8888 + +# Tide + +apiVersion: v1 +kind: Service +metadata: + name: tide +spec: + selector: + app: tide + ports: + - port: 80 + targetPort: 8888 + type: NodePort +--- +apiVersion: extensions/v1beta1 +kind: Deployment +metadata: + name: tide + labels: + app: tide +spec: + replicas: 1 # Do not scale up. + template: + metadata: + labels: + app: tide + spec: + serviceAccountName: "tide" + containers: + - name: tide + image: gcr.io/k8s-prow/tide:v20180808-68cee5a41 + args: + - --dry-run=false + ports: + - name: http + containerPort: 8888 + volumeMounts: + - name: oauth + mountPath: /etc/github + readOnly: true + - name: config + mountPath: /etc/config + readOnly: true + volumes: + - name: oauth + secret: + secretName: oauth-token + - name: config + configMap: + name: config + diff --git a/vendor/github.com/knative/test-infra/ci/prow/config.yaml b/vendor/github.com/knative/test-infra/ci/prow/config.yaml new file mode 100644 index 00000000000..c88098662a1 --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/prow/config.yaml @@ -0,0 +1,2250 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +plank: + job_url_template: 'https://gubernator.knative.dev/build/knative-prow/{{if or (eq .Spec.Type "presubmit") (eq .Spec.Type "batch")}}pr-logs/pull{{with .Spec.Refs}}/{{.Org}}_{{.Repo}}{{end}}{{else}}logs{{end}}{{if eq .Spec.Type "presubmit"}}/{{with index .Spec.Refs.Pulls 0}}{{.Number}}{{end}}{{else if eq .Spec.Type "batch"}}/batch{{end}}/{{.Spec.Job}}/{{.Status.BuildID}}/' + report_template: '[Full PR test history](https://gubernator.knative.dev/pr/{{.Spec.Refs.Org}}_{{.Spec.Refs.Repo}}/{{with index .Spec.Refs.Pulls 0}}{{.Number}}{{end}}). [Your PR dashboard](https://gubernator.knative.dev/pr/{{with index .Spec.Refs.Pulls 0}}{{.Author}}{{end}}).' + pod_pending_timeout: 60m + default_decoration_config: + timeout: 7200000000000 # 2h + grace_period: 15000000000 # 15s + utility_images: + clonerefs: "gcr.io/k8s-prow/clonerefs@sha256:b62ba1f379ac19c5ec9ee7bcab14d3f0b3c31cea9cdd4bc491e98e2c5f346c07" + initupload: "gcr.io/k8s-prow/initupload@sha256:58f89f2aae68f7dc46aaf05c7e8204c4f26b53ec9ce30353d1c27ce44a60d121" + entrypoint: "gcr.io/k8s-prow/entrypoint:v20180512-0255926d1" + sidecar: "gcr.io/k8s-prow/sidecar@sha256:8807b2565f4d2699920542fcf890878824b1ede4198d7ff46bca53feb064ed44" + gcs_configuration: + bucket: "knative-prow" + path_strategy: "explicit" + gcs_credentials_secret: "service-account" + +prowjob_namespace: default +pod_namespace: test-pods +log_level: info + +branch-protection: + orgs: + knative: + # Protect all branches in knative + # This means all prow jobs with "always_run" set are required + # to pass before tide can merge the PR. + # Currently this is manually enabled by the knative org admins, + # but it's stated here for documentation and reference purposes. + protect: true + # Admins can overrule checks + enforce_admins: false + +tide: + queries: + - repos: + - knative/build + - knative/build-pipeline + - knative/build-templates + - knative/serving + - knative/eventing + - knative/eventing-sources + - knative/docs + - knative/test-infra + - knative/pkg + - knative/caching + labels: + - lgtm + - approved + missingLabels: + - do-not-merge/hold + - do-not-merge/work-in-progress + merge_method: + knative: squash + knative/build-pipeline: rebase + target_url: https://prow.knative.dev/tide.html + +presets: +- labels: + preset-service-account: "true" + env: + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /etc/service-account/service-account.json + volumes: + - name: service + secret: + secretName: service-account + volumeMounts: + - name: service + mountPath: /etc/service-account + readOnly: true +# storage / caching presets +- labels: + preset-bazel-scratch-dir: "true" + env: + - name: TEST_TMPDIR + value: /bazel-scratch/.cache/bazel + volumes: + - name: bazel-scratch + emptyDir: {} + volumeMounts: + - name: bazel-scratch + mountPath: /bazel-scratch/.cache +- labels: + preset-bazel-remote-cache-enabled: "false" + env: + - name: BAZEL_REMOTE_CACHE_ENABLED + value: "false" +# docker-in-docker presets +- labels: + preset-dind-enabled: "true" + env: + - name: DOCKER_IN_DOCKER_ENABLED + value: "true" + volumes: + - name: docker-graph + emptyDir: {} + volumeMounts: + - name: docker-graph + mountPath: /docker-graph + +presubmits: + knative/serving: + - name: pull-knative-serving-build-tests + agent: kubernetes + context: pull-knative-serving-build-tests + always_run: true + rerun_command: "/test pull-knative-serving-build-tests" + trigger: "(?m)^/test (all|pull-knative-serving-build-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--build-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-serving-unit-tests + agent: kubernetes + context: pull-knative-serving-unit-tests + always_run: true + rerun_command: "/test pull-knative-serving-unit-tests" + trigger: "(?m)^/test (all|pull-knative-serving-unit-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--unit-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-serving-integration-tests + agent: kubernetes + context: pull-knative-serving-integration-tests + always_run: true + rerun_command: "/test pull-knative-serving-integration-tests" + trigger: "(?m)^/test (all|pull-knative-serving-integration-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--integration-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-serving-go-coverage + labels: + preset-service-account: "true" + agent: kubernetes + context: pull-knative-serving-go-coverage + always_run: true + rerun_command: "/test pull-knative-serving-go-coverage" + trigger: "(?m)^/test (all|pull-knative-serving-go-coverage),?(\\s+|$)" + optional: true + decorate: true + clone_uri: "https://github.com/knative/serving.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--postsubmit-gcs-bucket=knative-prow" + - "--postsubmit-job-name=post-knative-serving-go-coverage" + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=50" + - "--github-token=/etc/github-token/token" + volumeMounts: + - name: github-token + mountPath: /etc/github-token + readOnly: true + volumes: + - name: github-token + secret: + secretName: covbot-token + + - name: pull-knative-serving-go-coverage-dev + labels: + preset-service-account: "true" + agent: kubernetes + context: pull-knative-serving-go-coverage-dev + always_run: false + rerun_command: "/test pull-knative-serving-go-coverage-dev" + trigger: "(?m)^/test (pull-knative-serving-go-coverage-dev),?(\\s+|$)" + optional: true + decorate: true + clone_uri: "https://github.com/knative/serving.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage-dev:latest-dev + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--postsubmit-gcs-bucket=knative-prow" + - "--postsubmit-job-name=post-knative-serving-go-coverage" + - "--profile-name=coverage_profile.txt" + - "--artifacts=$(ARTIFACTS)" + - "--cov-target=." + - "--cov-threshold-percentage=81" + - "--github-token=/etc/github-token/token" + volumeMounts: + - name: github-token + mountPath: /etc/github-token + readOnly: true + volumes: + - name: github-token + secret: + secretName: covbot-token + + knative/build: + - name: pull-knative-build-build-tests + agent: kubernetes + context: pull-knative-build-build-tests + always_run: true + rerun_command: "/test pull-knative-build-build-tests" + trigger: "(?m)^/test (all|pull-knative-build-build-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--build-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-build-unit-tests + agent: kubernetes + context: pull-knative-build-unit-tests + always_run: true + rerun_command: "/test pull-knative-build-unit-tests" + trigger: "(?m)^/test (all|pull-knative-build-unit-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--unit-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-build-integration-tests + agent: kubernetes + context: pull-knative-build-integration-tests + always_run: true + rerun_command: "/test pull-knative-build-integration-tests" + trigger: "(?m)^/test (all|pull-knative-build-integration-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + preset-dind-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--integration-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-build-go-coverage + labels: + preset-service-account: "true" + agent: kubernetes + context: pull-knative-build-go-coverage + always_run: true + rerun_command: "/test pull-knative-build-go-coverage" + trigger: "(?m)^/test (all|pull-knative-build-go-coverage),?(\\s+|$)" + optional: true + decorate: true + clone_uri: "https://github.com/knative/build.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--postsubmit-gcs-bucket=knative-prow" + - "--postsubmit-job-name=post-knative-build-go-coverage" + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=50" + - "--github-token=/etc/github-token/token" + volumeMounts: + - name: github-token + mountPath: /etc/github-token + readOnly: true + volumes: + - name: github-token + secret: + secretName: covbot-token + + knative/build-pipeline: + - name: pull-knative-build-pipeline-build-tests + agent: kubernetes + context: pull-knative-build-pipeline-build-tests + always_run: true + rerun_command: "/test pull-knative-build-pipeline-build-tests" + trigger: "(?m)^/test (all|pull-knative-build-pipeline-build-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--build-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-build-pipeline-unit-tests + agent: kubernetes + context: pull-knative-build-pipeline-unit-tests + always_run: true + rerun_command: "/test pull-knative-build-pipeline-unit-tests" + trigger: "(?m)^/test (all|pull-knative-build-pipeline-unit-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--unit-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-build-pipeline-integration-tests + agent: kubernetes + context: pull-knative-build-pipeline-integration-tests + always_run: true + rerun_command: "/test pull-knative-build-pipeline-integration-tests" + trigger: "(?m)^/test (all|pull-knative-build-pipeline-integration-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--integration-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-build-pipeline-go-coverage + labels: + preset-service-account: "true" + agent: kubernetes + context: pull-knative-build-pipeline-go-coverage + always_run: true + rerun_command: "/test pull-knative-build-pipeline-go-coverage" + trigger: "(?m)^/test (all|pull-knative-build-pipeline-go-coverage),?(\\s+|$)" + optional: true + decorate: true + clone_uri: "https://github.com/knative/build-pipeline.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--postsubmit-gcs-bucket=knative-prow" + - "--postsubmit-job-name=post-knative-build-pipeline-go-coverage" + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=50" + - "--github-token=/etc/github-token/token" + volumeMounts: + - name: github-token + mountPath: /etc/github-token + readOnly: true + volumes: + - name: github-token + secret: + secretName: covbot-token + + knative/eventing: + - name: pull-knative-eventing-build-tests + agent: kubernetes + context: pull-knative-eventing-build-tests + always_run: true + rerun_command: "/test pull-knative-eventing-build-tests" + trigger: "(?m)^/test (all|pull-knative-eventing-build-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--build-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-eventing-unit-tests + agent: kubernetes + context: pull-knative-eventing-unit-tests + always_run: true + rerun_command: "/test pull-knative-eventing-unit-tests" + trigger: "(?m)^/test (all|pull-knative-eventing-unit-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--unit-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-eventing-integration-tests + agent: kubernetes + context: pull-knative-eventing-integration-tests + always_run: true + rerun_command: "/test pull-knative-eventing-integration-tests" + trigger: "(?m)^/test (all|pull-knative-eventing-integration-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--integration-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-eventing-go-coverage + labels: + preset-service-account: "true" + agent: kubernetes + context: pull-knative-eventing-go-coverage + always_run: true + rerun_command: "/test pull-knative-eventing-go-coverage" + trigger: "(?m)^/test (all|pull-knative-eventing-go-coverage),?(\\s+|$)" + optional: true + decorate: true + clone_uri: "https://github.com/knative/eventing.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--postsubmit-gcs-bucket=knative-prow" + - "--postsubmit-job-name=post-knative-eventing-go-coverage" + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=50" + - "--github-token=/etc/github-token/token" + volumeMounts: + - name: github-token + mountPath: /etc/github-token + readOnly: true + volumes: + - name: github-token + secret: + secretName: covbot-token + + knative/eventing-sources: + - name: pull-knative-eventing-sources-build-tests + agent: kubernetes + context: pull-knative-eventing-sources-build-tests + always_run: true + rerun_command: "/test pull-knative-eventing-sources-build-tests" + trigger: "(?m)^/test (all|pull-knative-eventing-sources-build-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--build-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-eventing-sources-unit-tests + agent: kubernetes + context: pull-knative-eventing-sources-unit-tests + always_run: true + rerun_command: "/test pull-knative-eventing-sources-unit-tests" + trigger: "(?m)^/test (all|pull-knative-eventing-sources-unit-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--unit-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-eventing-sources-integration-tests + agent: kubernetes + context: pull-knative-eventing-sources-integration-tests + always_run: true + rerun_command: "/test pull-knative-eventing-sources-integration-tests" + trigger: "(?m)^/test (all|pull-knative-eventing-sources-integration-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--integration-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-eventing-sources-go-coverage + labels: + preset-service-account: "true" + agent: kubernetes + context: pull-knative-eventing-sources-go-coverage + always_run: true + rerun_command: "/test pull-knative-eventing-sources-go-coverage" + trigger: "(?m)^/test (all|pull-knative-eventing-sources-go-coverage),?(\\s+|$)" + optional: true + decorate: true + clone_uri: "https://github.com/knative/eventing-sources.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--postsubmit-gcs-bucket=knative-prow" + - "--postsubmit-job-name=post-knative-eventing-sources-go-coverage" + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=50" + - "--github-token=/etc/github-token/token" + volumeMounts: + - name: github-token + mountPath: /etc/github-token + readOnly: true + volumes: + - name: github-token + secret: + secretName: covbot-token + + knative/docs: + - name: pull-knative-docs-build-tests + agent: kubernetes + context: pull-knative-docs-build-tests + always_run: true + rerun_command: "/test pull-knative-docs-build-tests" + trigger: "(?m)^/test (all|pull-knative-docs-build-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--build-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-docs-unit-tests + agent: kubernetes + context: pull-knative-docs-unit-tests + always_run: true + rerun_command: "/test pull-knative-docs-unit-tests" + trigger: "(?m)^/test (all|pull-knative-docs-unit-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--unit-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-docs-integration-tests + agent: kubernetes + context: pull-knative-docs-integration-tests + always_run: true + rerun_command: "/test pull-knative-docs-integration-tests" + trigger: "(?m)^/test (all|pull-knative-docs-integration-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--integration-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-docs-go-coverage + labels: + preset-service-account: "true" + agent: kubernetes + context: pull-knative-docs-go-coverage + always_run: true + rerun_command: "/test pull-knative-docs-go-coverage" + trigger: "(?m)^/test (all|pull-knative-docs-go-coverage),?(\\s+|$)" + optional: true + decorate: true + clone_uri: "https://github.com/knative/docs.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--postsubmit-gcs-bucket=knative-prow" + - "--postsubmit-job-name=post-knative-docs-go-coverage" + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=50" + - "--github-token=/etc/github-token/token" + volumeMounts: + - name: github-token + mountPath: /etc/github-token + readOnly: true + volumes: + - name: github-token + secret: + secretName: covbot-token + + knative/build-templates: + - name: pull-knative-build-templates-build-tests + agent: kubernetes + context: pull-knative-build-templates-build-tests + always_run: true + rerun_command: "/test pull-knative-build-templates-build-tests" + trigger: "(?m)^/test (all|pull-knative-build-templates-build-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--build-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-build-templates-unit-tests + agent: kubernetes + context: pull-knative-build-templates-unit-tests + always_run: true + rerun_command: "/test pull-knative-build-templates-unit-tests" + trigger: "(?m)^/test (all|pull-knative-build-templates-unit-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--unit-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-build-templates-integration-tests + agent: kubernetes + context: pull-knative-build-templates-integration-tests + always_run: true + rerun_command: "/test pull-knative-build-templates-integration-tests" + trigger: "(?m)^/test (all|pull-knative-build-templates-integration-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--integration-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + knative/pkg: + - name: pull-knative-pkg-build-tests + agent: kubernetes + context: pull-knative-pkg-build-tests + always_run: true + rerun_command: "/test pull-knative-pkg-build-tests" + trigger: "(?m)^/test (all|pull-knative-pkg-build-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--build-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-pkg-unit-tests + agent: kubernetes + context: pull-knative-pkg-unit-tests + always_run: true + rerun_command: "/test pull-knative-pkg-unit-tests" + trigger: "(?m)^/test (all|pull-knative-pkg-unit-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--unit-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-pkg-integration-tests + agent: kubernetes + context: pull-knative-pkg-integration-tests + always_run: true + rerun_command: "/test pull-knative-pkg-integration-tests" + trigger: "(?m)^/test (all|pull-knative-pkg-integration-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--integration-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-pkg-go-coverage + labels: + preset-service-account: "true" + agent: kubernetes + context: pull-knative-pkg-go-coverage + always_run: true + rerun_command: "/test pull-knative-pkg-go-coverage" + trigger: "(?m)^/test (all|pull-knative-pkg-go-coverage),?(\\s+|$)" + optional: true + decorate: true + clone_uri: "https://github.com/knative/pkg.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--postsubmit-gcs-bucket=knative-prow" + - "--postsubmit-job-name=post-knative-pkg-go-coverage" + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=50" + - "--github-token=/etc/github-token/token" + volumeMounts: + - name: github-token + mountPath: /etc/github-token + readOnly: true + volumes: + - name: github-token + secret: + secretName: covbot-token + + knative/test-infra: + - name: pull-knative-test-infra-build-tests + agent: kubernetes + context: pull-knative-test-infra-build-tests + always_run: true + rerun_command: "/test pull-knative-test-infra-build-tests" + trigger: "(?m)^/test (all|pull-knative-test-infra-build-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--build-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-test-infra-unit-tests + agent: kubernetes + context: pull-knative-test-infra-unit-tests + always_run: true + rerun_command: "/test pull-knative-test-infra-unit-tests" + trigger: "(?m)^/test (all|pull-knative-test-infra-unit-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--unit-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-test-infra-integration-tests + agent: kubernetes + context: pull-knative-test-infra-integration-tests + always_run: true + rerun_command: "/test pull-knative-test-infra-integration-tests" + trigger: "(?m)^/test (all|pull-knative-test-infra-integration-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--integration-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + knative/caching: + - name: pull-knative-caching-build-tests + agent: kubernetes + context: pull-knative-caching-build-tests + always_run: true + rerun_command: "/test pull-knative-caching-build-tests" + trigger: "(?m)^/test (all|pull-knative-caching-build-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--build-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-caching-unit-tests + agent: kubernetes + context: pull-knative-caching-unit-tests + always_run: true + rerun_command: "/test pull-knative-caching-unit-tests" + trigger: "(?m)^/test (all|pull-knative-caching-unit-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--unit-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-caching-integration-tests + agent: kubernetes + context: pull-knative-caching-integration-tests + always_run: true + rerun_command: "/test pull-knative-caching-integration-tests" + trigger: "(?m)^/test (all|pull-knative-caching-integration-tests),?(\\s+|$)" + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/$(REPO_OWNER)/$(REPO_NAME)=$(PULL_REFS)" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/pr-logs" + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--integration-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + + - name: pull-knative-caching-go-coverage + labels: + preset-service-account: "true" + agent: kubernetes + context: pull-knative-caching-go-coverage + always_run: true + rerun_command: "/test pull-knative-caching-go-coverage" + trigger: "(?m)^/test (all|pull-knative-caching-go-coverage),?(\\s+|$)" + optional: true + decorate: true + clone_uri: "https://github.com/knative/caching.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--postsubmit-gcs-bucket=knative-prow" + - "--postsubmit-job-name=post-knative-caching-go-coverage" + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=50" + - "--github-token=/etc/github-token/token" + volumeMounts: + - name: github-token + mountPath: /etc/github-token + readOnly: true + volumes: + - name: github-token + secret: + secretName: covbot-token + +periodics: +- cron: "1 * * * *" # Run every hour and one minute + name: ci-knative-serving-continuous + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/serving" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=50" # Avoid overrun + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--all-tests" + - "--emit-metrics" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" +- cron: "1 8 * * *" # Run at 01:01PST every day (08:01 UTC) + name: ci-knative-serving-release + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/serving" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=90" # 1.5h + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./hack/release.sh" + - "--publish" + - "--tag-release" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" +- cron: "1 9 * * 6" # Run at 02:01PST every Saturday (09:01 UTC) + name: ci-knative-serving-playground + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/serving" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=90" # 1.5h + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./hack/deploy.sh" + - "knative-playground" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" +- cron: "5 8 * * *" # Run at 01:05PST every day (08:05 UTC) + name: ci-knative-serving-latency + agent: kubernetes + labels: + preset-service-account: "true" + decorate: true + extra_refs: + - org: knative + repo: serving + base_ref: master + clone_uri: "https://github.com/knative/serving.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/metrics:latest + imagePullPolicy: Always + command: + - "/metrics" + args: + - "--source-directory=ci-knative-serving-continuous" + - "--artifacts-dir=$(ARTIFACTS)" + - "--service-account=/etc/service-account/service-account.json" +- cron: "5 8 * * *" # Run at 01:05PST every day (08:05 UTC) + name: ci-knative-serving-api-coverage + agent: kubernetes + labels: + preset-service-account: "true" + decorate: true + extra_refs: + - org: knative + repo: serving + base_ref: master + clone_uri: "https://github.com/knative/serving.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/apicoverage:latest + imagePullPolicy: Always + command: + - "/apicoverage" + args: + - "--artifacts-dir=$(ARTIFACTS)" + - "--service-account=/etc/service-account/service-account.json" +- cron: "0 1 * * *" # Run at 01:00 every day + name: ci-knative-serving-go-coverage + agent: kubernetes + decorate: true + extra_refs: + - org: knative + repo: serving + base_ref: master + clone_uri: "https://github.com/knative/serving.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=80" +- cron: "0 1 * * *" # Run at 01:00 every day + name: ci-knative-serving-performance + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/serving" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=50" # Avoid overrun + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/performance-tests.sh" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + +- cron: "15 * * * *" # Run every hour and 15 minutes + name: ci-knative-build-continuous + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/build" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=50" # Avoid overrun + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--all-tests" + - "--emit-metrics" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" +- cron: "31 8 * * *" # Run at 01:31PST every day (08:31 UTC) + name: ci-knative-build-release + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + preset-dind-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/build" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=90" # 1.5h + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./hack/release.sh" + - "--publish" + - "--tag-release" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" +- cron: "5 8 * * *" # Run at 01:05PST every day (08:05 UTC) + name: ci-knative-build-latency + agent: kubernetes + labels: + preset-service-account: "true" + decorate: true + extra_refs: + - org: knative + repo: build + base_ref: master + clone_uri: "https://github.com/knative/build.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/metrics:latest + imagePullPolicy: Always + command: + - "/metrics" + args: + - "--source-directory=ci-knative-build-continuous" + - "--artifacts-dir=$(ARTIFACTS)" + - "--service-account=/etc/service-account/service-account.json" +- cron: "0 1 * * *" # Run at 01:00 every day + name: ci-knative-build-go-coverage + agent: kubernetes + decorate: true + extra_refs: + - org: knative + repo: build + base_ref: master + clone_uri: "https://github.com/knative/build.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=80" + +- cron: "0 1 * * *" # Run at 01:00 every day + name: ci-knative-build-pipeline-go-coverage + agent: kubernetes + decorate: true + extra_refs: + - org: knative + repo: build + base_ref: master + clone_uri: "https://github.com/knative/build-pipeline.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=80" + +- cron: "50 * * * *" # Run every hour and 50 minutes + name: ci-knative-docs-continuous + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/docs" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=50" # Avoid overrun + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--all-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" +- cron: "0 1 * * *" # Run at 01:00 every day + name: ci-knative-docs-go-coverage + agent: kubernetes + decorate: true + extra_refs: + - org: knative + repo: docs + base_ref: master + clone_uri: "https://github.com/knative/docs.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=80" + +- cron: "30 * * * *" # Run every hour and 30 minutes + name: ci-knative-eventing-continuous + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/eventing" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=50" # Avoid overrun + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--all-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" +- cron: "16 9 * * *" # Run at 02:16PST every day (09:16 UTC) + name: ci-knative-eventing-release + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/eventing" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=90" # 1.5h + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./hack/release.sh" + - "--publish" + - "--tag-release" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" +- cron: "0 1 * * *" # Run at 01:00 every day + name: ci-knative-eventing-go-coverage + agent: kubernetes + decorate: true + extra_refs: + - org: knative + repo: eventing + base_ref: master + clone_uri: "https://github.com/knative/eventing.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=80" + +- cron: "30 * * * *" # Run every hour and 30 minutes + name: ci-knative-eventing-sources-continuous + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/eventing-sources" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=50" # Avoid overrun + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--all-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" +- cron: "16 9 * * *" # Run at 02:16PST every day (09:16 UTC) + name: ci-knative-eventing-sources-release + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/eventing-sources" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=90" # 1.5h + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./hack/release.sh" + - "--publish" + - "--tag-release" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" +- cron: "0 1 * * *" # Run at 01:00 every day + name: ci-knative-eventing-sources-go-coverage + agent: kubernetes + decorate: true + extra_refs: + - org: knative + repo: eventing-sources + base_ref: master + clone_uri: "https://github.com/knative/eventing-sources.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=80" + +- cron: "40 * * * *" # Run every hour and 40 minutes + name: ci-knative-build-templates-continuous + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/build-templates" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=50" # Avoid overrun + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--all-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" + +- cron: "45 * * * *" # Run every hour and 45 minutes + name: ci-knative-pkg-continuous + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/pkg" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=50" # Avoid overrun + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--all-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" +- cron: "0 1 * * *" # Run at 01:00 every day + name: ci-knative-pkg-go-coverage + agent: kubernetes + decorate: true + extra_refs: + - org: knative + repo: pkg + base_ref: master + clone_uri: "https://github.com/knative/pkg.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=80" + +- cron: "30 * * * *" # Run every hour and 30 minutes + name: ci-knative-caching-continuous + agent: kubernetes + labels: + preset-service-account: "true" + preset-bazel-scratch-dir: "true" + preset-bazel-remote-cache-enabled: "true" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/prow-tests:latest + imagePullPolicy: Always + args: + - "--scenario=kubernetes_execute_bazel" + - "--clean" + - "--job=$(JOB_NAME)" + - "--repo=github.com/knative/caching" + - "--root=/go/src" + - "--service-account=/etc/service-account/service-account.json" + - "--upload=gs://knative-prow/logs" + - "--timeout=50" # Avoid overrun + - "--" # end bootstrap args, scenario args below + - "--" # end kubernetes_execute_bazel flags (consider following flags as text) + - "./test/presubmit-tests.sh" + - "--all-tests" + # Bazel needs privileged mode in order to sandbox builds. + securityContext: + privileged: true + resources: + requests: + memory: "1Gi" +- cron: "0 1 * * *" # Run at 01:00 every day + name: ci-knative-caching-go-coverage + agent: kubernetes + decorate: true + extra_refs: + - org: knative + repo: caching + base_ref: master + clone_uri: "https://github.com/knative/caching.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=80" + +postsubmits: + knative/serving: + - name: post-knative-serving-go-coverage + branches: + - master + agent: kubernetes + decorate: true + clone_uri: "https://github.com/knative/serving.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=0" + - name: post-knative-serving-go-coverage-dev + branches: + - master + agent: kubernetes + decorate: true + clone_uri: "https://github.com/knative/serving.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage-dev:latest-dev + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=0" + + knative/build: + - name: post-knative-build-go-coverage + branches: + - master + agent: kubernetes + decorate: true + clone_uri: "https://github.com/knative/build.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=0" + + knative/build-pipeline: + - name: post-knative-build-pipeline-go-coverage + branches: + - master + agent: kubernetes + decorate: true + clone_uri: "https://github.com/knative/build-pipeline.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=0" + + knative/docs: + - name: post-knative-docs-go-coverage + branches: + - master + agent: kubernetes + decorate: true + clone_uri: "https://github.com/knative/docs.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=0" + + knative/eventing: + - name: post-knative-eventing-go-coverage + branches: + - master + agent: kubernetes + decorate: true + clone_uri: "https://github.com/knative/eventing.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=0" + + knative/eventing-sources: + - name: post-knative-eventing-sources-go-coverage + branches: + - master + agent: kubernetes + decorate: true + clone_uri: "https://github.com/knative/eventing-sources.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=0" + + knative/pkg: + - name: post-knative-pkg-go-coverage + branches: + - master + agent: kubernetes + decorate: true + clone_uri: "https://github.com/knative/pkg.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=0" + + knative/caching: + - name: post-knative-caching-go-coverage + branches: + - master + agent: kubernetes + decorate: true + clone_uri: "https://github.com/knative/caching.git" + spec: + containers: + - image: gcr.io/knative-tests/test-infra/coverage:latest + imagePullPolicy: Always + command: + - "/coverage" + args: + - "--artifacts=$(ARTIFACTS)" + - "--profile-name=coverage_profile.txt" + - "--cov-target=." + - "--cov-threshold-percentage=0" diff --git a/vendor/github.com/knative/test-infra/ci/prow/config_start.yaml b/vendor/github.com/knative/test-infra/ci/prow/config_start.yaml new file mode 100644 index 00000000000..ada1a3e62fa --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/prow/config_start.yaml @@ -0,0 +1,339 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Initial configuration of prow cluster + +# Configs + +apiVersion: v1 +kind: ConfigMap +metadata: + name: plugins +data: + plugins: "" +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: config +data: + config: "" +--- + +# Namespaces + +apiVersion: v1 +kind: Namespace +metadata: + name: prow +--- +apiVersion: v1 +kind: Namespace +metadata: + name: test-pods +--- + +# Service accounts, roles and bindings + +kind: ServiceAccount +apiVersion: v1 +metadata: + name: "boskos" + namespace: test-pods +--- +kind: ClusterRoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "boskos" + namespace: test-pods +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: boskos +subjects: +- kind: ServiceAccount + name: "boskos" + namespace: test-pods +--- +kind: ClusterRole +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "boskos" + namespace: test-pods +rules: + - apiGroups: + - apiextensions.k8s.io + resources: + - customresourcedefinitions + verbs: + - create + - apiGroups: + - boskos.k8s.io + resources: + - resources + verbs: + - "*" +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: "default" + namespace: test-pods +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "test-pods-default" + namespace: test-pods +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: "test-pods-default" +subjects: +- kind: ServiceAccount + name: "default" + namespace: test-pods +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "test-pods-default" + namespace: test-pods +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - get +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: "deck" +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "deck" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: "deck" +subjects: +- kind: ServiceAccount + name: "deck" + namespace: default +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "deck" +rules: + - apiGroups: + - "" + resources: + - pods/log + verbs: + - get + - apiGroups: + - "prow.k8s.io" + resources: + - prowjobs + verbs: + - get + - list +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: "horologium" +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "horologium" +rules: + - apiGroups: + - "prow.k8s.io" + resources: + - prowjobs + verbs: + - create + - list +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "horologium" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: "horologium" +subjects: +- kind: ServiceAccount + name: "horologium" + namespace: default +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: "plank" +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "plank" +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - create + - delete + - list + - apiGroups: + - "prow.k8s.io" + resources: + - prowjobs + verbs: + - create + - list + - update +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "plank" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: "plank" +subjects: +- kind: ServiceAccount + name: "plank" + namespace: default +--- +kind: ServiceAccount +apiVersion: v1 +metadata: + name: "sinker" +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "sinker" +rules: + - apiGroups: + - "" + resources: + - pods + verbs: + - delete + - list + - apiGroups: + - "prow.k8s.io" + resources: + - prowjobs + verbs: + - delete + - list +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "sinker" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: "sinker" +subjects: +- kind: ServiceAccount + name: "sinker" + namespace: default +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: "hook" +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "hook" +rules: + - apiGroups: + - "prow.k8s.io" + resources: + - prowjobs + verbs: + - create + - get + - apiGroups: + - "" + resources: + - configmaps + verbs: + - update +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "hook" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: "hook" +subjects: +- kind: ServiceAccount + name: "hook" +--- +apiVersion: v1 +kind: ServiceAccount +metadata: + name: "tide" +--- +kind: Role +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "tide" +rules: + - apiGroups: + - "prow.k8s.io" + resources: + - prowjobs + verbs: + - create + - get + - list + - apiGroups: + - "" + resources: + - configmaps + verbs: + - update +--- +kind: RoleBinding +apiVersion: rbac.authorization.k8s.io/v1beta1 +metadata: + name: "tide" +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: Role + name: "tide" +subjects: +- kind: ServiceAccount + name: "tide" + namespace: default diff --git a/vendor/github.com/knative/test-infra/ci/prow/plugins.yaml b/vendor/github.com/knative/test-infra/ci/prow/plugins.yaml new file mode 100644 index 00000000000..57c8b2f4079 --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/prow/plugins.yaml @@ -0,0 +1,41 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +approve: +- repos: + - knative + implicit_self_approve: true + review_acts_as_approve: true + +plugins: + knative: + - approve + - assign + - blunderbuss + - buildifier + - cat + - dog + - golint + - heart + - help + - hold + - label + - lgtm + - lifecycle + - shrug + - size + - skip + - trigger + - wip + - yuks diff --git a/vendor/github.com/knative/test-infra/ci/prow/prow_setup.md b/vendor/github.com/knative/test-infra/ci/prow/prow_setup.md new file mode 100644 index 00000000000..3f04729dfb5 --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/prow/prow_setup.md @@ -0,0 +1,71 @@ +# Prow setup + +## Creating the cluster + +1. Create the GKE cluster, the role bindings and the GitHub secrets. You might need to update [Makefile](./Makefile). For details, see https://github.com/kubernetes/test-infra/blob/master/prow/getting_started.md. + +1. Ensure the GCP projects listed in [resources.yaml](./boskos/resources.yaml) are created. + +1. Apply [config_start.yaml](./config_start.yaml) to the cluster. + +1. Apply Boskos [config_start.yaml](./boskos/config_start.yaml) to the cluster. + +1. Run `make update-cluster`, `make update-boskos`, `make update-config`, `make update-plugins` and `make update-boskos-config`. + +1. If SSL needs to be reconfigured, promote your ingress IP to static in Cloud Console, and [create the TLS secret](https://kubernetes.io/docs/concepts/services-networking/ingress/#tls). + +## Expanding Boskos pool + +1. Create a new GCP project and add it to [resources.yaml](./boskos/resources.yaml). + +1. Make `knative-tests@appspot.gserviceaccount.com` an editor of the project. + +1. Enable the Compute Engine API for the project (e.g., by visiting https://console.developers.google.com/apis/api/compute.googleapis.com/overview?project=XXXXXXXX). + +1. Enable the Kubernetes Engine API for the project (e.g., by visiting https://console.cloud.google.com/apis/api/container.googleapis.com/overview?project=XXXXXXXX). + +1. Run `make update-boskos-config`. + +## Setting up Prow for a new repo + +1. Create the appropriate `OWNERS` files (at least one for the root dir). + +1. Make sure that *Knative Robots* is an Admin of the repo. + +1. Update the tide section in the Prow config file and run `make update-config` (ask one of the owners of knative/test-infra). + +1. Wait a few minutes, check that Prow is working by entering `/woof` as a comment in any PR in the new repo. + +1. Set **tide** as a required status check for the master branch. + +### Setting up jobs for a new repo + +1. Have the test infrastructure in place (usually this means having at least `//test/presubmit-tests.sh` working, and optionally `//hack/release.sh` working for automated nightly releases). + +1. Merge a pull request (e.g., https://github.com/knative/test-infra/pull/203) that: + + 1. Updates the Prow config file (usually, copy and update existing jobs from another repository). + + 1. For the presubmit tests, setup the *pull-knative-**repo**-**(build|unit|integration)**-tests* jobs. + + 1. For go test coverage, setup the ***(pull|post|ci)**-knative-**repo**-go-coverage* jobs. + + 1. For the continuous integration tests, setup the *ci-knative-**repo**-continuous* job. + + 1. For automated nightly releases, setup the *ci-knative-**repo**-release* job. + + 1. Updates the Gubernator config with the new log dirs. + + 1. Updates the Testgrid config with the new buckets, tabs and dashboard. + +1. Ask one of the owners of *knative/test-infra* to: + + 1. Run `make update-config` in `ci/prow`. + + 1. Run `make deploy` in `ci/gubernator`. + + 1. Run `make update-config` in `ci/testgrid`. + +1. Wait a few minutes, enter `/retest` as a comment in any PR in the repo and ensure the test jobs are executed. + +1. Set the new test jobs as required status checks for the master branch. diff --git a/vendor/github.com/knative/test-infra/ci/testgrid/Makefile b/vendor/github.com/knative/test-infra/ci/testgrid/Makefile new file mode 100644 index 00000000000..5cf42d995b9 --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/testgrid/Makefile @@ -0,0 +1,29 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +TESTGRID_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) + +test: + bazel run @k8s//testgrid/cmd/configurator -- \ + --yaml=$(TESTGRID_DIR)/config.yaml \ + --validate-config-file + +update-config: +ifndef GOOGLE_APPLICATION_CREDENTIALS + $(error GOOGLE_APPLICATION_CREDENTIALS not set) +endif + bazel run @k8s//testgrid/cmd/configurator -- \ + --yaml=$(TESTGRID_DIR)/config.yaml \ + --output=gs://knative-testgrid/config \ + --oneshot diff --git a/vendor/github.com/knative/test-infra/ci/testgrid/README.md b/vendor/github.com/knative/test-infra/ci/testgrid/README.md new file mode 100644 index 00000000000..7b028e040f3 --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/testgrid/README.md @@ -0,0 +1,6 @@ +# Testgrid config + +This directory contains the config for our [Testgrid](https://github.com/kubernetes/test-infra/tree/master/testgrid) instance. + +* `Makefile` Commands to interact with the Testgrid instance regarding updates. +* `config.yaml` Testgrid configuration. diff --git a/vendor/github.com/knative/test-infra/ci/testgrid/config.yaml b/vendor/github.com/knative/test-infra/ci/testgrid/config.yaml new file mode 100644 index 00000000000..9af7ce15425 --- /dev/null +++ b/vendor/github.com/knative/test-infra/ci/testgrid/config.yaml @@ -0,0 +1,216 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Default testgroup and dashboardtab, please do not change them +default_test_group: + days_of_results: 14 # Number of days of test results to gather and serve + tests_name_policy: 2 # Replace the name of the test + ignore_pending: false # Show in-progress tests + column_header: + - configuration_value: Commit # Shows the commit number on column header + - configuration_value: infra-commit + num_columns_recent: 10 # The number of columns to consider "recent" for a variety of purposes + use_kubernetes_client: true # ** This field is deprecated and should always be true ** + is_external: true # ** This field is deprecated and should always be true ** + alert_stale_results_hours: 0 # Don't alert for staleness by default + num_failures_to_alert: 3 # Consider a test failed if it has 3 or more consecutive failures + num_passes_to_disable_alert: 1 # Consider a failing test passing if it has 1 or more consecutive passes + +default_dashboard_tab: + open_test_template: # The URL template to visit after clicking on a cell + url: https://gubernator.knative.dev/build// + file_bug_template: # The URL template to visit when filing a bug + url: https://github.com/knative/serving/issues/new + options: + - key: title + value: 'Test "" failed' + - key: body + value: + attach_bug_template: # The URL template to visit when attaching a bug + url: # Empty + options: # Empty + # Text to show in the about menu as a link to another view of the results + results_text: See these results in Gubernator + results_url_template: # The URL template to visit after clicking + url: https://gubernator.knative.dev/builds/ + # URL for regression search links. + code_search_path: github.com/knative/serving/search + num_columns_recent: 10 + code_search_url_template: # The URL template to visit when searching for changelists + url: https://github.com/knative/serving/compare/... + +# Test groups + +test_groups: +- name: ci-knative-serving-continuous + gcs_prefix: knative-prow/logs/ci-knative-serving-continuous +- name: ci-knative-serving-release + gcs_prefix: knative-prow/logs/ci-knative-serving-release +- name: ci-knative-serving-playground + gcs_prefix: knative-prow/logs/ci-knative-serving-playground +- name: pull-knative-serving-test-coverage + gcs_prefix: knative-prow/logs/ci-knative-serving-go-coverage + short_text_metric: coverage +- name: ci-knative-serving-latency + gcs_prefix: knative-prow/logs/ci-knative-serving-latency + short_text_metric: latency +- name: ci-knative-serving-api-coverage + gcs_prefix: knative-prow/logs/ci-knative-serving-api-coverage + short_text_metric: api_coverage +- name: ci-knative-build-continuous + gcs_prefix: knative-prow/logs/ci-knative-build-continuous +- name: ci-knative-build-release + gcs_prefix: knative-prow/logs/ci-knative-build-release +- name: pull-knative-build-test-coverage + gcs_prefix: knative-prow/logs/ci-knative-build-go-coverage + short_text_metric: coverage +- name: ci-knative-build-latency + gcs_prefix: knative-prow/logs/ci-knative-build-latency + short_text_metric: latency +- name: ci-knative-build-templates-continuous + gcs_prefix: knative-prow/logs/ci-knative-build-templates-continuous +- name: pull-knative-build-pipeline-test-coverage + gcs_prefix: knative-prow/logs/ci-knative-build-pipeline-go-coverage + short_text_metric: coverage +- name: ci-knative-eventing-continuous + gcs_prefix: knative-prow/logs/ci-knative-eventing-continuous +- name: ci-knative-eventing-release + gcs_prefix: knative-prow/logs/ci-knative-eventing-release +- name: pull-knative-eventing-test-coverage + gcs_prefix: knative-prow/logs/ci-knative-eventing-go-coverage + short_text_metric: coverage +- name: ci-knative-eventing-sources-continuous + gcs_prefix: knative-prow/logs/ci-knative-eventing-sources-continuous +- name: ci-knative-eventing-sources-release + gcs_prefix: knative-prow/logs/ci-knative-eventing-sources-release +- name: pull-knative-eventing-sources-test-coverage + gcs_prefix: knative-prow/logs/ci-knative-eventing-sources-go-coverage + short_text_metric: coverage +- name: ci-knative-docs-continuous + gcs_prefix: knative-prow/logs/ci-knative-docs-continuous +- name: pull-knative-docs-test-coverage + gcs_prefix: knative-prow/logs/ci-knative-docs-go-coverage + short_text_metric: coverage +- name: ci-knative-pkg-continuous + gcs_prefix: knative-prow/logs/ci-knative-pkg-continuous +- name: pull-knative-pkg-test-coverage + gcs_prefix: knative-prow/logs/ci-knative-pkg-go-coverage + short_text_metric: coverage +- name: ci-knative-caching-continuous + gcs_prefix: knative-prow/logs/ci-knative-caching-continuous +- name: pull-knative-caching-test-coverage + gcs_prefix: knative-prow/logs/ci-knative-caching-go-coverage + short_text_metric: coverage + +# Dashboards + +dashboards: +- name: knative-serving + dashboard_tab: + - name: continuous + test_group_name: ci-knative-serving-continuous + - name: release + test_group_name: ci-knative-serving-release + - name: playground + test_group_name: ci-knative-serving-playground + - name: coverage + test_group_name: pull-knative-serving-test-coverage + base_options: 'exclude-filter-by-regex=Overall&group-by-directory=&expand-groups=&sort-by-name=' + - name: latency + test_group_name: ci-knative-serving-latency + description: '95% latency in ms' + base_options: 'exclude-filter-by-regex=Overall&group-by-directory=&expand-groups=&sort-by-name=' + - name: api-coverage + test_group_name: ci-knative-serving-api-coverage + description: 'Conformance tests API coverage.' + base_options: 'exclude-filter-by-regex=Overall$&group-by-directory=&expand-groups=&sort-by-name=' + - name: conformance-tests + test_group_name: ci-knative-serving-continuous + base_options: 'include-filter-by-regex=//knative/serving/test/conformance:&sort-by-name=' +- name: knative-build + dashboard_tab: + - name: continuous + test_group_name: ci-knative-build-continuous + - name: release + test_group_name: ci-knative-build-release + - name: coverage + test_group_name: pull-knative-build-test-coverage + base_options: 'exclude-filter-by-regex=Overall&group-by-directory=&expand-groups=&sort-by-name=' + - name: latency + test_group_name: ci-knative-build-latency + description: '95% latency in ms' + base_options: 'exclude-filter-by-regex=Overall&group-by-directory=&expand-groups=&sort-by-name=' +- name: knative-build-templates + dashboard_tab: + - name: continuous + test_group_name: ci-knative-build-templates-continuous +- name: knative-build-pipeline + dashboard_tab: + - name: coverage + test_group_name: pull-knative-build-pipeline-test-coverage + base_options: 'exclude-filter-by-regex=Overall&group-by-directory=&expand-groups=&sort-by-name=' +- name: knative-eventing + dashboard_tab: + - name: continuous + test_group_name: ci-knative-eventing-continuous + - name: release + test_group_name: ci-knative-eventing-release + - name: coverage + test_group_name: pull-knative-eventing-test-coverage + base_options: 'exclude-filter-by-regex=Overall&group-by-directory=&expand-groups=&sort-by-name=' +- name: knative-eventing-sources + dashboard_tab: + - name: continuous + test_group_name: ci-knative-eventing-sources-continuous + - name: release + test_group_name: ci-knative-eventing-sources-release + - name: coverage + test_group_name: pull-knative-eventing-sources-test-coverage + base_options: 'exclude-filter-by-regex=Overall&group-by-directory=&expand-groups=&sort-by-name=' +- name: knative-docs + dashboard_tab: + - name: continuous + test_group_name: ci-knative-docs-continuous + - name: coverage + test_group_name: pull-knative-docs-test-coverage + base_options: 'exclude-filter-by-regex=Overall&group-by-directory=&expand-groups=&sort-by-name=' +- name: knative-pkg + dashboard_tab: + - name: continuous + test_group_name: ci-knative-pkg-continuous + - name: coverage + test_group_name: pull-knative-pkg-test-coverage + base_options: 'exclude-filter-by-regex=Overall&group-by-directory=&expand-groups=&sort-by-name=' +- name: knative-caching + dashboard_tab: + - name: continuous + test_group_name: ci-knative-caching-continuous + - name: coverage + test_group_name: pull-knative-caching-test-coverage + base_options: 'exclude-filter-by-regex=Overall&group-by-directory=&expand-groups=&sort-by-name=' + +# Dashboard groups + +dashboard_groups: +- name: knative + dashboard_names: + - knative-serving + - knative-build + - knative-build-templates + - knative-build-pipeline + - knative-eventing + - knative-eventing-sources + - knative-docs + - knative-pkg + - knative-caching diff --git a/vendor/github.com/knative/test-infra/dummy.go b/vendor/github.com/knative/test-infra/dummy.go new file mode 100644 index 00000000000..40028c14ec7 --- /dev/null +++ b/vendor/github.com/knative/test-infra/dummy.go @@ -0,0 +1,27 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + https://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 ( + "fmt" +) + +func main() { + fmt.Println("This is a dummy go file so `go dep` can be used with knative/test-infra repo") + fmt.Println("This file can be removed once the repo contains real, useful go code in the root dir") +} + diff --git a/vendor/github.com/knative/test-infra/images/README.md b/vendor/github.com/knative/test-infra/images/README.md new file mode 100644 index 00000000000..22b9b16edd0 --- /dev/null +++ b/vendor/github.com/knative/test-infra/images/README.md @@ -0,0 +1,3 @@ +# Prow Job Images + +This directory contains custom Docker images used by our Prow jobs. diff --git a/vendor/github.com/knative/test-infra/images/apicoverage/Dockerfile b/vendor/github.com/knative/test-infra/images/apicoverage/Dockerfile new file mode 100644 index 00000000000..897ec7d82ef --- /dev/null +++ b/vendor/github.com/knative/test-infra/images/apicoverage/Dockerfile @@ -0,0 +1,20 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM golang:1.10.2 +LABEL maintainer "Srinivas Hegde " +RUN apt-get update && apt-get install -y --no-install-recommends + +COPY apicoverage /apicoverage +ENTRYPOINT ["/apicoverage"] diff --git a/vendor/github.com/knative/test-infra/images/apicoverage/Makefile b/vendor/github.com/knative/test-infra/images/apicoverage/Makefile new file mode 100644 index 00000000000..b5a87ca546c --- /dev/null +++ b/vendor/github.com/knative/test-infra/images/apicoverage/Makefile @@ -0,0 +1,23 @@ + +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +REGISTRY ?= gcr.io +PROJECT ?= knative-tests/test-infra +PUSH ?= docker push + +apicoverage-image: + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build ../../tools/apicoverage + docker build -t "$(REGISTRY)/$(PROJECT)/apicoverage:latest" . + $(PUSH) "$(REGISTRY)/$(PROJECT)/apicoverage:latest" diff --git a/vendor/github.com/knative/test-infra/images/apicoverage/README.md b/vendor/github.com/knative/test-infra/images/apicoverage/README.md new file mode 100644 index 00000000000..b855777358b --- /dev/null +++ b/vendor/github.com/knative/test-infra/images/apicoverage/README.md @@ -0,0 +1,3 @@ +# API coverage tool Image + +This directory contains the custom docker image used for calculating the API coverage by the conformance tests. diff --git a/vendor/github.com/knative/test-infra/images/prow-tests/Dockerfile b/vendor/github.com/knative/test-infra/images/prow-tests/Dockerfile new file mode 100644 index 00000000000..7baf483481e --- /dev/null +++ b/vendor/github.com/knative/test-infra/images/prow-tests/Dockerfile @@ -0,0 +1,56 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +FROM gcr.io/k8s-testimages/kubekins-e2e:v20181001-df2f5324a-master +LABEL maintainer "Adriano Cunha " + +# Install extras on top of base image + +ENV DEBIAN_FRONTEND noninteractive +RUN apt-get update +RUN gcloud components update + +# Docker +RUN gcloud components install docker-credential-gcr +RUN docker-credential-gcr configure-docker + +# Extra tools through apt-get +RUN apt-get install -y uuid-runtime # for uuidgen +RUN apt-get install -y npm # for markdown-link-check +RUN apt-get install -y rubygems # for mdl +RUN apt-get install -y build-essential libssl-dev # for wrk +RUN apt-get install -y netbase # sets up /etc/services needed for wrk + +# Extra tools through go get +RUN go get -u github.com/google/go-containerregistry/cmd/ko +RUN go get -u github.com/golang/dep/cmd/dep +RUN go get -u github.com/google/licenseclassifier + +# Extra tools through npm +RUN npm install -g markdown-link-check + +# Extra tools through gem +RUN gem install mixlib-config -v 2.2.4 # required because ruby is 2.1 +RUN gem install mdl + +# Install wrk +RUN git clone https://github.com/wg/wrk.git wrk +RUN make -C wrk/ +RUN cp wrk/wrk /usr/local/bin + +ADD . /go/src/github.com/knative/test-infra + +# Extra custom tools +RUN cp /go/src/github.com/knative/test-infra/tools/githubhelper/githubhelper . +RUN go install github.com/knative/test-infra/tools/dep-collector diff --git a/vendor/github.com/knative/test-infra/images/prow-tests/Makefile b/vendor/github.com/knative/test-infra/images/prow-tests/Makefile new file mode 100644 index 00000000000..6e1ce3c08ca --- /dev/null +++ b/vendor/github.com/knative/test-infra/images/prow-tests/Makefile @@ -0,0 +1,34 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +IMG = gcr.io/knative-tests/test-infra/prow-tests +TAG := $(shell date +v%Y%m%d)-$(shell git describe --tags --always --dirty) + +all: build + +build: + make -C ../../tools/githubhelper + docker build -t $(IMG):$(TAG) -f Dockerfile ../.. + docker tag $(IMG):$(TAG) $(IMG):latest + +push_versioned: build + docker push $(IMG):$(TAG) + +push_latest: build + docker push $(IMG):latest + +clean: + rm -fr githubhelper dep-collector + +push: push_versioned push_latest clean diff --git a/vendor/github.com/knative/test-infra/images/prow-tests/README.md b/vendor/github.com/knative/test-infra/images/prow-tests/README.md new file mode 100644 index 00000000000..d1b904427e4 --- /dev/null +++ b/vendor/github.com/knative/test-infra/images/prow-tests/README.md @@ -0,0 +1,13 @@ +# Prow Test Job Image + +This directory contains the custom Docker image used by our Prow test jobs. + +## Building and publishing a new image + +To build and push a new image, just run `make push`. + +For testing purposes you can build an image but not push it; to do so, run `make build`. + +Note that you must have proper permission in the `knative-tests` project to push new images to the GCR. + +The `prow-tests` image is pinned on a specific `kubekins` image; update `Dockerfile` if you need to use a newer/different image. This will basically define the versions of `bazel`, `go`, `kubectl` and other build tools. diff --git a/vendor/github.com/knative/test-infra/test/e2e-tests.sh b/vendor/github.com/knative/test-infra/test/e2e-tests.sh new file mode 100755 index 00000000000..128733ce38d --- /dev/null +++ b/vendor/github.com/knative/test-infra/test/e2e-tests.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script runs the end-to-end tests. + +# If you already have a Knative cluster setup and kubectl pointing +# to it, call this script with the --run-tests arguments and it will use +# the cluster and run the tests. + +# Calling this script without arguments will create a new cluster in +# project $PROJECT_ID, run the tests and delete the cluster. + +source $(dirname $0)/../scripts/e2e-tests.sh + +function parse_flags() { + if [[ "$1" == "--smoke-test-custom-flag" ]]; then + echo "--smoke-test-custom-flag passed" + exit 0 + fi + return 0 +} + +# Script entry point. + +initialize $@ + +if (( USING_EXISTING_CLUSTER )); then + echo "ERROR: this test isn't intended to run against an existing cluster" + fail_test +fi + +start_latest_knative_serving || fail_test "Knative Serving is not up" + +# This is actually a unit test, but it does exercise the necessary helper functions. +go_test_e2e -run TestE2ESucceeds ./test || fail_test + +success diff --git a/vendor/github.com/knative/test-infra/test/presubmit-tests.sh b/vendor/github.com/knative/test-infra/test/presubmit-tests.sh new file mode 100755 index 00000000000..ae0e0cc42ee --- /dev/null +++ b/vendor/github.com/knative/test-infra/test/presubmit-tests.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script runs the presubmit tests; it is started by prow for each PR. +# For convenience, it can also be executed manually. +# Running the script without parameters, or with the --all-tests +# flag, causes all tests to be executed, in the right order. +# Use the flags --build-tests, --unit-tests and --integration-tests +# to run a specific set of tests. + +source $(dirname $0)/../scripts/presubmit-tests.sh + +function build_tests() { + header "Running build tests" + local failed=0 + make -C ci/prow test || failed=1 + make -C ci/testgrid test || failed=1 + for script in scripts/*.sh; do + subheader "Checking integrity of ${script}" + bash -c "source ${script}" || failed=1 + done + return ${failed} +} + +function unit_tests() { + header "Running unit tests" + local failed=0 + for test in ./test/unit/*-tests.sh; do + subheader "Running tests in ${test}" + ${test} || failed=1 + done + return ${failed} +} + +# We use the default integration test runner. + +main $@ diff --git a/vendor/github.com/knative/test-infra/test/unit/e2e-custom-flag-tests.sh b/vendor/github.com/knative/test-infra/test/unit/e2e-custom-flag-tests.sh new file mode 100755 index 00000000000..b5528861752 --- /dev/null +++ b/vendor/github.com/knative/test-infra/test/unit/e2e-custom-flag-tests.sh @@ -0,0 +1,38 @@ +#!/bin/bash + +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This script runs the end-to-end tests. + +# If you already have a Knative cluster setup and kubectl pointing +# to it, call this script with the --run-tests arguments and it will use +# the cluster and run the tests. + +# Calling this script without arguments will create a new cluster in +# project $PROJECT_ID, run the tests and delete the cluster. + +source $(dirname $0)/../../scripts/e2e-tests.sh + +function parse_flags() { + if [[ "$1" == "--smoke-test-custom-flag" ]]; then + echo "OK: --smoke-test-custom-flag passed" + exit 0 + fi + fail_test "Unexpected flag $1 passed" +} + +# Script entry point. + +initialize --smoke-test-custom-flag diff --git a/vendor/github.com/knative/test-infra/test/unit/library-tests.sh b/vendor/github.com/knative/test-infra/test/unit/library-tests.sh new file mode 100755 index 00000000000..a8be4415fcd --- /dev/null +++ b/vendor/github.com/knative/test-infra/test/unit/library-tests.sh @@ -0,0 +1,50 @@ +#!/bin/bash + +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Fake we're in a Prow job, if running locally. +[[ -z "${PROW_JOB_ID:-}" ]] && PROW_JOB_ID=123 + +source $(dirname $0)/../../scripts/library.sh + +set -e + +function test_report() { + local REPORT="$(mktemp)" + report_go_test -run $1 ./test > ${REPORT} || true + cat ${REPORT} + grep "$2" ${REPORT} > /dev/null + grep "Done parsing 1 tests" ${REPORT} > /dev/null +} + +# Cleanup bazel stuff to avoid confusing Prow +function cleanup_bazel() { + bazel clean +} + +trap cleanup_bazel EXIT + +echo "Testing report_go_test" + +echo "Test pass" +test_report TestSucceeds "^- TestSucceeds :PASS:" + +echo "Test fails with fatal" +test_report TestFailsWithFatal "^- TestFailsWithFatal :FAIL:" + +echo "Test fails with SIGQUIT" +test_report TestFailsWithSigQuit "^- TestFailsWithSigQuit :FAIL:" + +echo "All tests passed" diff --git a/vendor/github.com/knative/test-infra/test/unit/presubmit-full-custom-integration-tests.sh b/vendor/github.com/knative/test-infra/test/unit/presubmit-full-custom-integration-tests.sh new file mode 100755 index 00000000000..d22b66e32dc --- /dev/null +++ b/vendor/github.com/knative/test-infra/test/unit/presubmit-full-custom-integration-tests.sh @@ -0,0 +1,28 @@ +#!/bin/bash + +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +source $(dirname $0)/presubmit-integration-tests-common.sh + +function check_results() { + (( PRE_INTEGRATION_TESTS )) || failed "Pre integration tests did not run" + (( CUSTOM_INTEGRATION_TESTS )) || failed "Custom integration tests did not run" + (( POST_INTEGRATION_TESTS )) || failed "Post integration tests did not run" + echo "Test passed" +} + +echo "Testing all custom test integration functions" + +main $@ diff --git a/vendor/github.com/knative/test-infra/test/unit/presubmit-integration-tests-common.sh b/vendor/github.com/knative/test-infra/test/unit/presubmit-integration-tests-common.sh new file mode 100755 index 00000000000..78c0f4d0646 --- /dev/null +++ b/vendor/github.com/knative/test-infra/test/unit/presubmit-integration-tests-common.sh @@ -0,0 +1,48 @@ +#!/bin/bash + +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +source $(dirname $0)/../../scripts/presubmit-tests.sh + +function failed() { + echo $1 + exit 1 +} + +function pre_integration_tests() { + PRE_INTEGRATION_TESTS=1 +} + +function integration_tests() { + CUSTOM_INTEGRATION_TESTS=1 +} + +function post_integration_tests() { + POST_INTEGRATION_TESTS=1 +} + +function build_tests() { + return 0 +} + +function unit_tests() { + return 0 +} + +PRE_INTEGRATION_TESTS=0 +CUSTOM_INTEGRATION_TESTS=0 +POST_INTEGRATION_TESTS=0 + +trap check_results EXIT diff --git a/vendor/github.com/knative/test-infra/test/unit/presubmit-partial-custom-integration-tests.sh b/vendor/github.com/knative/test-infra/test/unit/presubmit-partial-custom-integration-tests.sh new file mode 100755 index 00000000000..e0fb4ef24fa --- /dev/null +++ b/vendor/github.com/knative/test-infra/test/unit/presubmit-partial-custom-integration-tests.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Test that pre/post integration tests don't run if unset. + +source $(dirname $0)/presubmit-integration-tests-common.sh + +function check_results() { + (( ! PRE_INTEGRATION_TESTS )) || failed "Pre integration tests did run" + (( CUSTOM_INTEGRATION_TESTS )) || failed "Custom integration tests did not run" + (( ! POST_INTEGRATION_TESTS )) || failed "Post integration tests did run" + echo "Test passed" +} + +echo "Testing custom test integration function" + +unset -f pre_integration_tests +unset -f post_integration_tests + +main $@ diff --git a/vendor/github.com/knative/test-infra/test/unit/release-tests.sh b/vendor/github.com/knative/test-infra/test/unit/release-tests.sh new file mode 100755 index 00000000000..6fcd5d35f94 --- /dev/null +++ b/vendor/github.com/knative/test-infra/test/unit/release-tests.sh @@ -0,0 +1,108 @@ +#!/bin/bash + +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +source $(dirname $0)/../../scripts/release.sh + +set -e + +# Call a function and verify its return value and output. +# Parameters: $1 - expected return code. +# $2 - expected output ("" if no output is expected) +# $3 ..$n - function to call and its parameters. +function test_function() { + local expected_retcode=$1 + local expected_string=$2 + local output="$(mktemp)" + local output_code="$(mktemp)" + shift 2 + echo -n "$(trap '{ echo $? > ${output_code}; }' EXIT ; "$@")" &> ${output} + local retcode=$(cat ${output_code}) + if [[ ${retcode} -ne ${expected_retcode} ]]; then + cat ${output} + echo "Return code ${retcode} doesn't match expected return code ${expected_retcode}" + return 1 + fi + if [[ -n "${expected_string}" ]]; then + local found=1 + grep "${expected_string}" ${output} > /dev/null || found=0 + if (( ! found )); then + cat ${output} + echo "String '${expected_string}' not found" + return 1 + fi + else + if [[ -s ${output} ]]; then + ls ${output} + cat ${output} + echo "Unexpected output" + return 1 + fi + fi + echo "'$@' returns code ${expected_retcode} and displays '${expected_string}'" +} + +function mock_branch_release() { + set -e + BRANCH_RELEASE=1 + TAG=sometag + function git() { + echo $@ + } + function hub() { + echo $@ + } + branch_release "$@" 2>&1 +} + +function call_function() { + set -e + local init=$1 + shift + eval ${init} + "$@" 2>&1 +} + +echo ">> Testing initialization" + +test_function 1 "error: missing version" initialize --version +test_function 1 "error: version format" initialize --version a +test_function 1 "error: version format" initialize --version 0.0 +test_function 0 "" initialize --version 1.0.0 + +test_function 1 "error: missing branch" initialize --branch +test_function 1 "error: branch name must be" initialize --branch a +test_function 1 "error: branch name must be" initialize --branch 0.0 +test_function 0 "" initialize --branch release-0.0 + +test_function 1 "error: missing release notes" initialize --release-notes +test_function 1 "error: file a doesn't" initialize --release-notes a +test_function 0 "" initialize --release-notes $(mktemp) + +echo ">> Testing release branching" + +test_function 0 "" branch_release +test_function 129 "usage: git tag" call_function BRANCH_RELEASE=1 branch_release +test_function 1 "No such file" call_function BRANCH_RELEASE=1 branch_release "K Foo" "a.yaml b.yaml" +test_function 0 "release create" mock_branch_release "K Foo" "$(mktemp) $(mktemp)" + +echo ">> Testing validation tests" + +test_function 0 "Running release validation" run_validation_tests true +test_function 0 "" call_function SKIP_TESTS=1 run_validation_tests true +test_function 0 "i_passed" run_validation_tests "echo i_passed" +test_function 1 "validation tests failed" run_validation_tests false + +echo ">> All tests passed" diff --git a/vendor/github.com/knative/test-infra/tools/README.md b/vendor/github.com/knative/test-infra/tools/README.md new file mode 100644 index 00000000000..d4cf2a272f2 --- /dev/null +++ b/vendor/github.com/knative/test-infra/tools/README.md @@ -0,0 +1,3 @@ +# Test Infrastructure tools + +This directory contains tools used by our Prow jobs. diff --git a/vendor/github.com/knative/test-infra/tools/apicoverage/README.md b/vendor/github.com/knative/test-infra/tools/apicoverage/README.md new file mode 100644 index 00000000000..01ddf855151 --- /dev/null +++ b/vendor/github.com/knative/test-infra/tools/apicoverage/README.md @@ -0,0 +1,14 @@ +# API Coverage Tool +This tool is designed to show the field level coverage exercised by the conformance tests. + +## Read from GCS +This tool reads the logs from the latest continous build of knative/serving. The logs have the information of which CRD objects are being created and which fields are being set for the testing. +It uses the service account passed in or by default will use the GOOGLE_APPLICATION_CREDENTIALS variable to get the logs. + +## Creating Output +This tool creates an output xml in the prow artifacts directory. The prow artifacts directory is passed in or by default will use `./artifacts` directory. + +This output xml will be read by testgrid and displayed on the [dashboard](https://testgrid.knative.dev/knative-serving#api-coverage). + +## Prow Job +There is a daily prow job that triggers this tool that is run at 01:05 AM PST. This tool will then generate the output xml which is then displayed in the testgrid dashboard. diff --git a/vendor/github.com/knative/test-infra/tools/apicoverage/apicoverage.go b/vendor/github.com/knative/test-infra/tools/apicoverage/apicoverage.go new file mode 100644 index 00000000000..6e007a7270b --- /dev/null +++ b/vendor/github.com/knative/test-infra/tools/apicoverage/apicoverage.go @@ -0,0 +1,241 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// apicoverage.go parses the log file and outputs the api coverage numbers in a +// testgrid expected output xml file + +package main + +import ( + "context" + "encoding/json" + "flag" + "fmt" + "log" + "os" + "reflect" + "strings" + + "github.com/knative/serving/pkg/apis/serving/v1alpha1" + "github.com/knative/test-infra/tools/gcs" + "github.com/knative/test-infra/tools/testgrid" +) + +const ( + logDir = "logs/ci-knative-serving-continuous/" + buildFile = "build-log.txt" + apiCoverage = "api_coverage" + overallRoute = "OverallRoute" + overallConfig = "OverallConfiguration" + overallService = "OverallService" +) + +// ResourceObjects defines the resource objects in knative-serving +type ResourceObjects struct { + Route *v1alpha1.Route + Configuration *v1alpha1.Configuration + Service *v1alpha1.Service +} + +// OverallAPICoverage defines the overall api coverage for knative serving +type OverallAPICoverage struct { + RouteAPICovered map[string]int + RouteAPINotCovered map[string]int + ConfigurationAPICovered map[string]int + ConfigurationAPINotCovered map[string]int + ServiceAPICovered map[string]int + ServiceAPINotCovered map[string]int +} + +type apiObjectName string + +const ( + apiObjectRoute apiObjectName = "route" + apiObjectConfiguration = "configuration" + apiObjectService = "service" +) + +// check if the object value is nil or empty. +// Uses https://golang.org/pkg/reflect/#Kind to get the variable type +func isNil(v reflect.Value) bool { + switch v.Kind() { + case reflect.Array, reflect.Map, reflect.Slice, reflect.String: + return v.Len() == 0 + case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: + return v.Int() == 0 + case reflect.Float32, reflect.Float64: + return v.Float() == 0 + } + return false +} + +func isStruct(v reflect.Value) bool { + return v.Kind() == reflect.Struct +} + +// Parse the struct and returns a map of +func parseStruct(v reflect.Value) map[string]reflect.Value { + f := make(map[string]reflect.Value) + + for i := 0; i < v.NumField(); i++ { + // Include only public vars. https://golang.org/pkg/reflect/#StructField. + if len(v.Type().Field(i).PkgPath) == 0 { + f[v.Type().Field(i).Name] = v.Field(i) + } + } + + return f +} + +func incrementCoverageValues(name string, covered map[string]int) { + if i, ok := covered[name]; ok { + covered[name] = i + 1 + } else { + covered[name] = 1 + } +} + +func handleCovered(name string, coverage *OverallAPICoverage) { + if strings.HasPrefix(name, "route") { + incrementCoverageValues(name, coverage.RouteAPICovered) + } else if strings.HasPrefix(name, "configuration") { + incrementCoverageValues(name, coverage.ConfigurationAPICovered) + } else if strings.HasPrefix(name, "service") { + incrementCoverageValues(name, coverage.ServiceAPICovered) + } +} + +func handleNotCovered(name string, coverage *OverallAPICoverage) { + if strings.HasPrefix(name, "route") { + coverage.RouteAPINotCovered[name] = 0 + } else if strings.HasPrefix(name, "configuration") { + coverage.ConfigurationAPINotCovered[name] = 0 + } else if strings.HasPrefix(name, "service") { + coverage.ServiceAPINotCovered[name] = 0 + } +} + +func getCoverage(value reflect.Value, name string, coverage *OverallAPICoverage) { + // Parse all the fields in the struct + for key, v := range parseStruct(value) { + name := name + "." + key + if isStruct(v) { + getCoverage(v, name, coverage) + } else { + // check if it is empty/nil + if isNil(v) { + handleNotCovered(name, coverage) + } else { + handleCovered(name, coverage) + } + } + } +} + +func calculateCoverage(covLogs []string, coverage *OverallAPICoverage) { + if len(covLogs) == 0 { + return + } + + for _, f := range covLogs { + var obj ResourceObjects + if err := json.Unmarshal([]byte(f), &obj); err != nil { + log.Fatalf("Cannot read resource object: %v", err) + } else { + if obj.Route != nil { + getCoverage(reflect.ValueOf(obj.Route).Elem(), "route", coverage) + } else if obj.Configuration != nil { + getCoverage(reflect.ValueOf(obj.Configuration).Elem(), "configuration", coverage) + } else if obj.Service != nil { + getCoverage(reflect.ValueOf(obj.Service).Elem(), "service", coverage) + } + } + } +} + +func initCoverage() *OverallAPICoverage { + coverage := OverallAPICoverage{} + coverage.RouteAPICovered = make(map[string]int) + coverage.RouteAPINotCovered = make(map[string]int) + coverage.ConfigurationAPICovered = make(map[string]int) + coverage.ConfigurationAPINotCovered = make(map[string]int) + coverage.ServiceAPICovered = make(map[string]int) + coverage.ServiceAPINotCovered = make(map[string]int) + + return &coverage +} + +func getRelevantLogs(fields []string) *string { + // I0727 16:23:30.055] 2018-10-12T18:18:06.835-0700 info TestRouteCreation test/configuration.go:34 resource {: }"} + if len(fields) == 8 && fields[3] == "info" && fields[6] == "resource" { + s := strings.Join(fields[7:], " ") + return &s + } + return nil +} + +func createCases(tcName string, covered map[string]int, notCovered map[string]int) []testgrid.TestCase { + var tc []testgrid.TestCase + + var percentCovered = float32(100 * len(covered) / (len(covered) + len(notCovered))) + tp := []testgrid.TestProperty{testgrid.TestProperty{Name: apiCoverage, Value: percentCovered}} + tc = append(tc, testgrid.TestCase{Name: tcName, Properties: testgrid.TestProperties{Property: tp}, Fail: false}) + + for key, value := range covered { + tp := []testgrid.TestProperty{testgrid.TestProperty{Name: apiCoverage, Value: float32(value)}} + tc = append(tc, testgrid.TestCase{Name: tcName + "/" + key, Properties: testgrid.TestProperties{Property: tp}, Fail: false}) + } + + for key, value := range notCovered { + tp := []testgrid.TestProperty{testgrid.TestProperty{Name: apiCoverage, Value: float32(value)}} + tc = append(tc, testgrid.TestCase{Name: tcName + "/" + key, Properties: testgrid.TestProperties{Property: tp}, Fail: true}) + } + return tc +} + +func createTestgridXML(coverage *OverallAPICoverage, artifactsDir string) { + tc := createCases(overallRoute, coverage.RouteAPICovered, coverage.RouteAPINotCovered) + tc = append(tc, createCases(overallConfig, coverage.ConfigurationAPICovered, coverage.ConfigurationAPINotCovered)...) + tc = append(tc, createCases(overallService, coverage.ServiceAPICovered, coverage.ServiceAPINotCovered)...) + ts := testgrid.TestSuite{TestCases: tc} + + if err := testgrid.CreateXMLOutput(ts, artifactsDir); err != nil { + log.Fatalf("Cannot create the xml output file: %v", err) + } +} + +func main() { + + artifactsDir := flag.String("artifacts-dir", "./artifacts", "Directory to store the generated XML file") + serviceAccount := flag.String("service-account", os.Getenv("GOOGLE_APPLICATION_CREDENTIALS"), "JSON key file for service account to use") + flag.Parse() + + // Read the latest-build.txt file to get the latest build number + ctx := context.Background() + num, err := gcs.GetLatestBuildNumber(ctx, logDir, *serviceAccount) + if err != nil { + log.Fatalf("Cannot get latest build number: %v", err) + } + + // Calculate coverage + coverage := initCoverage() + calculateCoverage( + gcs.ParseLog(ctx, fmt.Sprintf("%s/%d/%s", logDir, num, buildFile), getRelevantLogs), + coverage) + + // Write the testgrid xml to artifacts + createTestgridXML(coverage, *artifactsDir) +} diff --git a/vendor/github.com/knative/test-infra/tools/dep-collector/README.md b/vendor/github.com/knative/test-infra/tools/dep-collector/README.md new file mode 100644 index 00000000000..9acf6cef809 --- /dev/null +++ b/vendor/github.com/knative/test-infra/tools/dep-collector/README.md @@ -0,0 +1,88 @@ +# dep-collector + +`dep-collector` is a tool for gathering up a collection of licenses for Go +dependencies that have been pulled into the idiomatic `vendor/` directory. +The resulting file from running `dep-collector` is intended for inclusion +in container images to respect the licenses of the included software. + +### Basic Usage + +You can run `dep-collector` on one or more Go import paths as entrypoints, +and it will: +1. Walk the transitive dependencies to identify vendored software packages, +1. Search for licenses for each vendored dependency, +1. Dump a file containing the licenses for each vendored import. + +For example (single import path): +```shell +$ dep-collector . +=========================================================== +Import: github.com/mattmoor/dep-collector/vendor/github.com/google/licenseclassifier + + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ +... + +``` + +For example (multiple import paths): + +```shell +$ dep-collector ./cmd/controller ./cmd/sleeper + +=========================================================== +Import: github.com/mattmoor/warm-image/vendor/cloud.google.com/go + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ +``` + +### CSV Usage + +You can also run `dep-collector` in a mode that produces CSV output, +including basic classification of the license. + +> In order to run dep-collector in this mode, you must first run: +> go get github.com/google/licenseclassifier + +For example: + +```shell +$ dep-collector -csv . +github.com/google/licenseclassifier,Static,,https://github.com/mattmoor/dep-collector/blob/master/vendor/github.com/google/licenseclassifier/LICENSE,Apache-2.0 +github.com/google/licenseclassifier/stringclassifier,Static,,https://github.com/mattmoor/dep-collector/blob/master/vendor/github.com/google/licenseclassifier/stringclassifier/LICENSE,Apache-2.0 +github.com/sergi/go-diff,Static,,https://github.com/mattmoor/dep-collector/blob/master/vendor/github.com/sergi/go-diff/LICENSE,MIT + +``` + +The columns here are: +* Import Path, +* How the dependency is linked in (always reports "static"), +* A column for whether any modifications have been made (always empty), +* The URL by which to access the license file (assumes `master`), +* A classification of what license this is ([using this](https://github.com/google/licenseclassifier)). + + +### Check mode + +`dep-collector` also includes a mode that will check for "forbidden" licenses. + +> In order to run dep-collector in this mode, you must first run: +> go get github.com/google/licenseclassifier + +For example (failing): +```shell +$ dep-collector -check ./foo/bar/baz +2018/07/20 22:01:29 Error checking license collection: Errors validating licenses: +Found matching forbidden license in "foo.io/bar/vendor/github.com/BurntSushi/toml": WTFPL +``` + +For example (passing): + +```shell +$ dep-collector -check . +2018/07/20 22:29:09 No errors found. +``` diff --git a/vendor/github.com/knative/test-infra/tools/dep-collector/imports.go b/vendor/github.com/knative/test-infra/tools/dep-collector/imports.go new file mode 100644 index 00000000000..924ce410228 --- /dev/null +++ b/vendor/github.com/knative/test-infra/tools/dep-collector/imports.go @@ -0,0 +1,94 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + gb "go/build" + "path/filepath" + "sort" + "strings" +) + +func CollectTransitiveImports(binaries []string) ([]string, error) { + // Perform a simple DFS to collect the binaries' transitive dependencies. + visited := make(map[string]struct{}) + for _, importpath := range binaries { + if gb.IsLocalImport(importpath) { + ip, err := qualifyLocalImport(importpath) + if err != nil { + return nil, err + } + importpath = ip + } + + pkg, err := gb.Import(importpath, WorkingDir, gb.ImportComment) + if err != nil { + return nil, err + } + if err := visit(pkg, visited); err != nil { + return nil, err + } + } + + // Sort the dependencies deterministically. + var list sort.StringSlice + for ip := range visited { + if !strings.Contains(ip, "/vendor/") { + // Skip files outside of vendor + continue + } + list = append(list, ip) + } + list.Sort() + + return list, nil +} + +func qualifyLocalImport(ip string) (string, error) { + gopathsrc := filepath.Join(gb.Default.GOPATH, "src") + if !strings.HasPrefix(WorkingDir, gopathsrc) { + return "", fmt.Errorf("working directory must be on ${GOPATH}/src = ", gopathsrc) + } + return filepath.Join(strings.TrimPrefix(WorkingDir, gopathsrc+string(filepath.Separator)), ip), nil +} + +func visit(pkg *gb.Package, visited map[string]struct{}) error { + if _, ok := visited[pkg.ImportPath]; ok { + return nil + } + visited[pkg.ImportPath] = struct{}{} + + for _, ip := range pkg.Imports { + if ip == "C" { + // skip cgo + continue + } + subpkg, err := gb.Import(ip, WorkingDir, gb.ImportComment) + if err != nil { + return fmt.Errorf("%v\n -> %v", pkg.ImportPath, err) + } + if !strings.HasPrefix(subpkg.Dir, WorkingDir) { + // Skip import paths outside of our workspace (std library) + continue + } + if err := visit(subpkg, visited); err != nil { + return fmt.Errorf("%v (%v)\n -> %v", pkg.ImportPath, pkg.Dir, err) + } + } + return nil +} diff --git a/vendor/github.com/knative/test-infra/tools/dep-collector/licenses.go b/vendor/github.com/knative/test-infra/tools/dep-collector/licenses.go new file mode 100644 index 00000000000..cb1df9ab748 --- /dev/null +++ b/vendor/github.com/knative/test-infra/tools/dep-collector/licenses.go @@ -0,0 +1,203 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "fmt" + gb "go/build" + "io/ioutil" + "os" + "path/filepath" + "sort" + "strings" + + "github.com/google/licenseclassifier" +) + +var LicenseNames = []string{ + "LICENCE", + "LICENSE", + "LICENSE.code", + "LICENSE.md", + "LICENSE.txt", + "COPYING", + "copyright", +} + +const MatchThreshold = 0.9 + +type LicenseFile struct { + EnclosingImportPath string + LicensePath string +} + +func (lf *LicenseFile) Body() (string, error) { + body, err := ioutil.ReadFile(lf.LicensePath) + if err != nil { + return "", err + } + return string(body), nil +} + +func (lt *LicenseFile) Classify(classifier *licenseclassifier.License) (string, error) { + body, err := lt.Body() + if err != nil { + return "", err + } + m := classifier.NearestMatch(body) + if m == nil { + return "", fmt.Errorf("unable to classify license: %v", lt.EnclosingImportPath) + } + return m.Name, nil +} + +func (lt *LicenseFile) Check(classifier *licenseclassifier.License) error { + body, err := lt.Body() + if err != nil { + return err + } + ms := classifier.MultipleMatch(body, false) + for _, m := range ms { + return fmt.Errorf("Found matching forbidden license in %q: %v", lt.EnclosingImportPath, m.Name) + } + return nil +} + +func (lt *LicenseFile) Entry() (string, error) { + body, err := lt.Body() + if err != nil { + return "", err + } + return fmt.Sprintf(` +=========================================================== +Import: %s + +%s +`, lt.EnclosingImportPath, body), nil +} + +func (lt *LicenseFile) CSVRow(classifier *licenseclassifier.License) (string, error) { + classification, err := lt.Classify(classifier) + if err != nil { + return "", err + } + parts := strings.Split(lt.EnclosingImportPath, "/vendor/") + if len(parts) != 2 { + return "", fmt.Errorf("wrong number of parts splitting import path on %q : %q", "/vendor/", lt.EnclosingImportPath) + } + return strings.Join([]string{ + parts[1], + "Static", + "", // TODO(mattmoor): Modifications? + "https://" + parts[0] + "/blob/master/vendor/" + parts[1] + "/" + filepath.Base(lt.LicensePath), + classification, + }, ","), nil +} + +func findLicense(ip string) (*LicenseFile, error) { + pkg, err := gb.Import(ip, WorkingDir, gb.ImportComment) + if err != nil { + return nil, err + } + dir := pkg.Dir + + for { + // When we reach the root of our workspace, stop searching. + if dir == WorkingDir { + return nil, fmt.Errorf("unable to find license for %q", pkg.ImportPath) + } + + for _, name := range LicenseNames { + p := filepath.Join(dir, name) + if _, err := os.Stat(p); err != nil { + continue + } + + return &LicenseFile{ + EnclosingImportPath: ip, + LicensePath: p, + }, nil + } + + // Walk to the parent directory / import path + dir = filepath.Dir(dir) + ip = filepath.Dir(ip) + } +} + +type LicenseCollection []*LicenseFile + +func (lc LicenseCollection) Entries() (string, error) { + sections := make([]string, 0, len(lc)) + for _, key := range lc { + entry, err := key.Entry() + if err != nil { + return "", err + } + sections = append(sections, entry) + } + return strings.Join(sections, "\n"), nil +} + +func (lc LicenseCollection) CSV(classifier *licenseclassifier.License) (string, error) { + sections := make([]string, 0, len(lc)) + for _, entry := range lc { + row, err := entry.CSVRow(classifier) + if err != nil { + return "", err + } + sections = append(sections, row) + } + return strings.Join(sections, "\n"), nil +} + +func (lc LicenseCollection) Check(classifier *licenseclassifier.License) error { + errors := []string{} + for _, entry := range lc { + if err := entry.Check(classifier); err != nil { + errors = append(errors, err.Error()) + } + } + if len(errors) == 0 { + return nil + } + return fmt.Errorf("Errors validating licenses:\n%v", strings.Join(errors, "\n")) +} + +func CollectLicenses(imports []string) (LicenseCollection, error) { + // for each of the import paths, search for a license file. + licenseFiles := make(map[string]*LicenseFile) + for _, ip := range imports { + lf, err := findLicense(ip) + if err != nil { + return nil, err + } + licenseFiles[lf.EnclosingImportPath] = lf + } + + order := sort.StringSlice{} + for key := range licenseFiles { + order = append(order, key) + } + order.Sort() + + licenseTypes := LicenseCollection{} + for _, key := range order { + licenseTypes = append(licenseTypes, licenseFiles[key]) + } + return licenseTypes, nil +} diff --git a/vendor/github.com/knative/test-infra/tools/dep-collector/main.go b/vendor/github.com/knative/test-infra/tools/dep-collector/main.go new file mode 100644 index 00000000000..4532942751d --- /dev/null +++ b/vendor/github.com/knative/test-infra/tools/dep-collector/main.go @@ -0,0 +1,81 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "flag" + "log" + "os" + + "github.com/google/licenseclassifier" +) + +var WorkingDir, _ = os.Getwd() + +var ( + csv = flag.Bool("csv", false, "Whether to print in CSV format (with slow classification).") + check = flag.Bool("check", false, "Whether to just check license files for forbidden licenses.") +) + +func main() { + flag.Parse() + if flag.NArg() == 0 { + log.Fatalf("Expected a list of import paths, got: %v", flag.Args()) + } + + // Perform a simple DFS to collect the binaries' transitive dependencies. + transitiveImports, err := CollectTransitiveImports(flag.Args()) + if err != nil { + log.Fatalf("Error collecting transitive dependencies: %v", err) + } + + // Gather all of the license data from the imports. + collection, err := CollectLicenses(transitiveImports) + if err != nil { + log.Fatalf("Error identifying licenses for transitive dependencies: %v", err) + } + + if *check { + classifier, err := licenseclassifier.NewWithForbiddenLicenses(MatchThreshold) + if err != nil { + log.Fatalf("Error creating license classifier: %v", err) + } + if err := collection.Check(classifier); err != nil { + log.Fatalf("Error checking license collection: %v", err) + } + log.Printf("No errors found.") + return + } + + if *csv { + classifier, err := licenseclassifier.New(MatchThreshold) + if err != nil { + log.Fatalf("Error creating license classifier: %v", err) + } + output, err := collection.CSV(classifier) + if err != nil { + log.Fatalf("Error generating CSV: %v", err) + } + os.Stdout.Write([]byte(output)) + } else { + entries, err := collection.Entries() + if err != nil { + log.Fatalf("Error generating entries: %v", err) + } + os.Stdout.Write([]byte(entries)) + } +} diff --git a/vendor/github.com/knative/test-infra/tools/gcs/gcs.go b/vendor/github.com/knative/test-infra/tools/gcs/gcs.go new file mode 100644 index 00000000000..a41fbbb21a0 --- /dev/null +++ b/vendor/github.com/knative/test-infra/tools/gcs/gcs.go @@ -0,0 +1,112 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// gcs.go defines functions to use GCS + +package gcs + +import ( + "bufio" + "context" + "fmt" + "io/ioutil" + "log" + "strconv" + "strings" + + "cloud.google.com/go/storage" + "google.golang.org/api/option" +) + +const ( + bucketName = "knative-prow" + latest = "latest-build.txt" +) + +var client *storage.Client + +func createStorageClient(ctx context.Context, sa string) error { + var err error + client, err = storage.NewClient(ctx, option.WithCredentialsFile(sa)) + return err +} + +func createStorageObject(filename string) *storage.ObjectHandle { + return client.Bucket(bucketName).Object(filename) +} + +// GetLatestBuildNumber gets the latest build number for the specified log directory +func GetLatestBuildNumber(ctx context.Context, logDir string, sa string) (int, error) { + contents, err := ReadGcsFile(ctx, logDir+latest, sa) + if err != nil { + return 0, err + } + latestBuild, err := strconv.Atoi(string(contents)) + if err != nil { + return 0, err + } + + return latestBuild, nil +} + +//ReadGcsFile reads the specified file using the provided service account +func ReadGcsFile(ctx context.Context, filename string, sa string) ([]byte, error) { + // Create a new GCS client + if err := createStorageClient(ctx, sa); err != nil { + log.Fatalf("Failed to create GCS client: %v", err) + } + o := createStorageObject(filename) + if _, err := o.Attrs(ctx); err != nil { + return []byte(fmt.Sprintf("Cannot get attributes of '%s'", filename)), err + } + f, err := o.NewReader(ctx) + if err != nil { + return []byte(fmt.Sprintf("Cannot open '%s'", filename)), err + } + defer f.Close() + contents, err := ioutil.ReadAll(f) + if err != nil { + return []byte(fmt.Sprintf("Cannot read '%s'", filename)), err + } + return contents, nil +} + +// ParseLog parses the log and returns the lines where the checkLog func does not return an empty slice. +// checkLog function should take in the log statement and return a part from that statement that should be in the log output. +func ParseLog(ctx context.Context, filename string, checkLog func(s []string) *string) []string { + var logs []string + + log.Printf("Parsing '%s'", filename) + o := createStorageObject(filename) + if _, err := o.Attrs(ctx); err != nil { + log.Printf("Cannot get attributes of '%s', assuming not ready yet: %v", filename, err) + return nil + } + f, err := o.NewReader(ctx) + if err != nil { + log.Fatalf("Error opening '%s': %v", filename, err) + } + defer f.Close() + + scanner := bufio.NewScanner(f) + + for scanner.Scan() { + if s := checkLog(strings.Fields(scanner.Text())); s != nil { + logs = append(logs, *s) + } + } + return logs +} diff --git a/vendor/github.com/knative/test-infra/tools/githubhelper/Makefile b/vendor/github.com/knative/test-infra/tools/githubhelper/Makefile new file mode 100644 index 00000000000..c8fef33a770 --- /dev/null +++ b/vendor/github.com/knative/test-infra/tools/githubhelper/Makefile @@ -0,0 +1,17 @@ +# Copyright 2018 The Knative Authors +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +all: + go get -u github.com/google/go-github/github + CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build . diff --git a/vendor/github.com/knative/test-infra/tools/githubhelper/README.md b/vendor/github.com/knative/test-infra/tools/githubhelper/README.md new file mode 100644 index 00000000000..5975a23521b --- /dev/null +++ b/vendor/github.com/knative/test-infra/tools/githubhelper/README.md @@ -0,0 +1,10 @@ +# GitHub Helper Tool + +This tool is designed to interact with GitHub, providing useful data for a Prow job. Actions performed and the output are governed by the flags used. + +Currently the tool makes unauthenticated requests to GitHub API. + +## Flags + +* `-list-changed-files` will list the files that are touched by the current PR in a Prow job. +* `-verbose` will dump extra info on output when executing the comments; it is intended for debugging. diff --git a/vendor/github.com/knative/test-infra/tools/githubhelper/githubhelper.go b/vendor/github.com/knative/test-infra/tools/githubhelper/githubhelper.go new file mode 100644 index 00000000000..d45fad475cc --- /dev/null +++ b/vendor/github.com/knative/test-infra/tools/githubhelper/githubhelper.go @@ -0,0 +1,85 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// githubhelper.go interacts with GitHub, providing useful data for a Prow job. + +package main + +import ( + "context" + "flag" + "fmt" + "log" + "os" + "strconv" + + "github.com/google/go-github/github" +) + +var ( + // Info about the current PR + repoOwner = os.Getenv("REPO_OWNER") + repoName = os.Getenv("REPO_NAME") + pullNumber = atoi(os.Getenv("PULL_NUMBER"), "pull number") + + // Shared useful variables + ctx = context.Background() + onePageList = &github.ListOptions{Page: 1} + verbose = false + anonymousGitHubClient *github.Client +) + +// atoi is a convenience function to convert a string to integer, failing in case of error. +func atoi(str, valueName string) int { + value, err := strconv.Atoi(str) + if err != nil { + log.Fatalf("Unexpected non number '%s' for %s: %v", str, valueName, err) + } + return value +} + +// infof if a convenience wrapper around log.Infof, and does nothing unless --verbose is passed. +func infof(template string, args ...interface{}) { + if verbose { + log.Printf(template, args...) + } +} + +// listChangedFiles simply lists the files changed by the current PR. +func listChangedFiles() { + infof("Listing changed files for PR %d in repository %s/%s", pullNumber, repoOwner, repoName) + files, _, err := anonymousGitHubClient.PullRequests.ListFiles(ctx, repoOwner, repoName, pullNumber, onePageList) + if err != nil { + log.Fatalf("Error listing files: %v", err) + } + for _, file := range files { + fmt.Println(*file.Filename) + } +} + +func main() { + listChangedFilesFlag := flag.Bool("list-changed-files", false, "List the files changed by the current pull request") + verboseFlag := flag.Bool("verbose", false, "Whether to dump extra info on output or not; intended for debugging") + flag.Parse() + + verbose = *verboseFlag + anonymousGitHubClient = github.NewClient(nil) + + if *listChangedFilesFlag { + listChangedFiles() + } +} + diff --git a/vendor/github.com/knative/test-infra/tools/testgrid/testgrid.go b/vendor/github.com/knative/test-infra/tools/testgrid/testgrid.go new file mode 100644 index 00000000000..30d7ff2c13c --- /dev/null +++ b/vendor/github.com/knative/test-infra/tools/testgrid/testgrid.go @@ -0,0 +1,69 @@ +/* +Copyright 2018 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// testgrid.go provides methods to perform action on testgrid. + +package testgrid + +import ( + "encoding/xml" + "os" +) + +// TestProperty defines a property of the test +type TestProperty struct { + Name string `xml:"name,attr"` + Value float32 `xml:"value,attr"` +} + +// TestProperties is an array of test properties +type TestProperties struct { + Property []TestProperty `xml:"property"` +} + +// TestCase defines a test case that was executed +type TestCase struct { + ClassName string `xml:"class_name,attr"` + Name string `xml:"name,attr"` + Time int `xml:"time,attr"` + Properties TestProperties `xml:"properties"` + Fail bool `xml:"failure,omitempty"` +} + +// TestSuite defines the set of relevant test cases +type TestSuite struct { + XMLName xml.Name `xml:"testsuite"` + TestCases []TestCase `xml:"testcase"` +} + +// CreateXMLOutput creates the junit xml file in the provided artifacts directory +func CreateXMLOutput(ts TestSuite, artifactsDir string) error { + op, err := xml.MarshalIndent(ts, "", " ") + if err != nil { + return err + } + + outputFile := artifactsDir + "/junit_bazel.xml" + f, err := os.Create(outputFile) + if err != nil { + return err + } + defer f.Close() + if _, err := f.WriteString(string(op) + "\n"); err != nil { + return err + } + return nil +} From e24e06479482946250b6f541d0132477471ee111 Mon Sep 17 00:00:00 2001 From: Martin Gencur Date: Tue, 13 Nov 2018 17:13:47 +0100 Subject: [PATCH 42/54] Simplify adding new test images (#600) * Simplify adding new test images * unify location of test images with knative/serving * remove code duplication * Call upload-test-images.sh in the right place * Fix path to script * Use bash and check which shell is used * Do not pass Docker repo explicitly * Do not call bash * do not use capital letters for local vars * Detailed instructions for adding new test images --- test/README.md | 5 ++- test/e2e-tests.sh | 20 ++---------- test/e2e/k8s_events_test.go | 2 +- test/e2e_flags.go | 3 +- test/image_paths.txt | 2 -- .../k8sevents/function.go | 0 .../k8sevents/kodata/LICENSE | 0 .../k8sevents/kodata/VENDOR-LICENSE | 0 test/upload-test-images.sh | 31 +++++++++++-------- 9 files changed, 27 insertions(+), 36 deletions(-) delete mode 100644 test/image_paths.txt rename test/{e2e => test_images}/k8sevents/function.go (100%) rename test/{e2e => test_images}/k8sevents/kodata/LICENSE (100%) rename test/{e2e => test_images}/k8sevents/kodata/VENDOR-LICENSE (100%) diff --git a/test/README.md b/test/README.md index 20ca7d73919..5b02898ee2f 100644 --- a/test/README.md +++ b/test/README.md @@ -84,7 +84,10 @@ A docker tag is mandatory to avoid issues with using `latest` tag for images dep ### Adding new test images -New test image paths should be placed in `./test/image_paths.txt`. +New test images should be placed in `./test/test_images`. For each image create a new sub-folder +and include a Go file that will be an entry point to the application. This Go file should use the +package "main" and include the function main(). It is a good practice to include a readme file as well. +When uploading test images, `ko` will build an image from this folder. ## Flags diff --git a/test/e2e-tests.sh b/test/e2e-tests.sh index eaa5f108cd3..af56cb14fec 100755 --- a/test/e2e-tests.sh +++ b/test/e2e-tests.sh @@ -37,7 +37,7 @@ function run_e2e_tests() { header "Running tests in $1" local options="" (( EMIT_METRICS )) && options="-emitmetrics" - report_go_test -v -tags=e2e -count=1 ./test/$1 -dockerrepo $DOCKER_REPO_OVERRIDE ${options} + report_go_test -v -tags=e2e -count=1 ./test/$1 ${options} return $? } @@ -83,22 +83,6 @@ function teardown_events_test_resources() { wait_until_object_does_not_exist namespaces $E2E_TEST_NAMESPACE } -function publish_test_images() { - echo ">> Publishing test images" - local IMAGE_PATHS_FILE="$(dirname $0)/image_paths.txt" - local DOCKER_TAG=e2e - - while read -r IMAGE || [[ -n "$IMAGE" ]]; do - if [ $(echo "$IMAGE" | grep -v -e "^#") ]; then - ko publish -P $IMAGE - local IMAGE=$KO_DOCKER_REPO/$IMAGE - local DIGEST=$(gcloud container images list-tags --filter="tags:latest" --format='get(digest)' $IMAGE) - echo "Tagging $IMAGE@$DIGEST with $DOCKER_TAG" - gcloud -q container images add-tag $IMAGE@$DIGEST $IMAGE:$DOCKER_TAG - fi - done < "$IMAGE_PATHS_FILE" -} - # Script entry point. initialize $@ @@ -130,7 +114,7 @@ ko apply -f config/provisioners/in-memory-channel/in-memory-channel.yaml wait_until_pods_running knative-eventing # Publish test images -publish_test_images +$(dirname $0)/upload-test-images.sh e2e # Handle test failures ourselves, so we can dump useful info. set +o errexit diff --git a/test/e2e/k8s_events_test.go b/test/e2e/k8s_events_test.go index 5cec98c8413..f321f3071f4 100644 --- a/test/e2e/k8s_events_test.go +++ b/test/e2e/k8s_events_test.go @@ -67,7 +67,7 @@ func TestKubernetesEvents(t *testing.T) { logger.Infof("Creating Route and Config") // The receiver of events which is accessible through Route - configImagePath := ImagePath("github.com/knative/eventing/test/e2e/k8sevents") + configImagePath := ImagePath("k8sevents") err = WithRouteReady(clients, logger, cleaner, routeName, configImagePath) if err != nil { t.Fatalf("The Route was not marked as Ready to serve traffic: %v", err) diff --git a/test/e2e_flags.go b/test/e2e_flags.go index 4828e189e8e..8f90995023d 100644 --- a/test/e2e_flags.go +++ b/test/e2e_flags.go @@ -21,6 +21,7 @@ package test import ( "flag" "os" + "path" pkgTest "github.com/knative/pkg/test" "github.com/knative/pkg/test/logging" @@ -38,7 +39,7 @@ type EventingEnvironmentFlags struct { func initializeEventingFlags() *EventingEnvironmentFlags { var f EventingEnvironmentFlags - defaultRepo := os.Getenv("DOCKER_REPO_OVERRIDE") + defaultRepo := path.Join(os.Getenv("DOCKER_REPO_OVERRIDE"), "github.com/knative/eventing/test/test_images") flag.StringVar(&f.DockerRepo, "dockerrepo", defaultRepo, "Provide the uri of the docker repo you have uploaded the test image to using `uploadtestimage.sh`. Defaults to $DOCKER_REPO_OVERRIDE") diff --git a/test/image_paths.txt b/test/image_paths.txt deleted file mode 100644 index 30e6b3e4451..00000000000 --- a/test/image_paths.txt +++ /dev/null @@ -1,2 +0,0 @@ -# The receiver of events which is accessible through Route -github.com/knative/eventing/test/e2e/k8sevents diff --git a/test/e2e/k8sevents/function.go b/test/test_images/k8sevents/function.go similarity index 100% rename from test/e2e/k8sevents/function.go rename to test/test_images/k8sevents/function.go diff --git a/test/e2e/k8sevents/kodata/LICENSE b/test/test_images/k8sevents/kodata/LICENSE similarity index 100% rename from test/e2e/k8sevents/kodata/LICENSE rename to test/test_images/k8sevents/kodata/LICENSE diff --git a/test/e2e/k8sevents/kodata/VENDOR-LICENSE b/test/test_images/k8sevents/kodata/VENDOR-LICENSE similarity index 100% rename from test/e2e/k8sevents/kodata/VENDOR-LICENSE rename to test/test_images/k8sevents/kodata/VENDOR-LICENSE diff --git a/test/upload-test-images.sh b/test/upload-test-images.sh index 9f7441fc7f0..f9c2edb45bb 100755 --- a/test/upload-test-images.sh +++ b/test/upload-test-images.sh @@ -16,20 +16,25 @@ set -o errexit +function upload_test_images() { + echo ">> Publishing test images" + local image_dirs="$(find $(dirname $0)/test_images -mindepth 1 -maxdepth 1 -type d)" + local docker_tag=$1 + + for image_dir in ${image_dirs}; do + local image="github.com/knative/eventing/test/test_images/$(basename ${image_dir})" + ko publish -P ${image} + if [ -n "$docker_tag" ]; then + image=$KO_DOCKER_REPO/${image} + local digest=$(gcloud container images list-tags --filter="tags:latest" --format='get(digest)' ${image}) + echo "Tagging ${image}@${digest} with $docker_tag" + gcloud -q container images add-tag ${image}@${digest} ${image}:$docker_tag + fi + done +} + : ${DOCKER_REPO_OVERRIDE:?"You must set 'DOCKER_REPO_OVERRIDE', see DEVELOPMENT.md"} export KO_DOCKER_REPO=${DOCKER_REPO_OVERRIDE} -IMAGE_PATHS_FILE="$(dirname $0)/image_paths.txt" -DOCKER_TAG=$1 -while read -r IMAGE || [[ -n "$IMAGE" ]]; do - if [ $(echo "$IMAGE" | grep -v -e "^#") ]; then - ko publish -P $IMAGE - if [ -n "$DOCKER_TAG" ]; then - IMAGE=$KO_DOCKER_REPO/$IMAGE - DIGEST=$(gcloud container images list-tags --filter="tags:latest" --format='get(digest)' $IMAGE) - echo "Tagging $IMAGE@$DIGEST with $DOCKER_TAG" - gcloud -q container images add-tag $IMAGE@$DIGEST $IMAGE:$DOCKER_TAG - fi - fi -done < "$IMAGE_PATHS_FILE" \ No newline at end of file +upload_test_images $@ \ No newline at end of file From 751b0f720a4abe888a5f8f05b9141891f0cfcf7e Mon Sep 17 00:00:00 2001 From: Adriano Cunha <35786489+adrcunha@users.noreply.github.com> Date: Tue, 13 Nov 2018 13:40:45 -0800 Subject: [PATCH 43/54] Fix passing yamls to hub tool (#611) Expand the array of yaml files into a single string, not separate strings. --- hack/release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/release.sh b/hack/release.sh index 7b9c1fe7e75..1cb9f8e19b1 100755 --- a/hack/release.sh +++ b/hack/release.sh @@ -93,6 +93,6 @@ for yaml in ${all_yamls[@]}; do publish_yaml ${yaml} ${EVENTING_RELEASE_GCS} ${TAG} done -branch_release "Knative Eventing" "${all_yamls[@]}" +branch_release "Knative Eventing" "${all_yamls[*]}" echo "New release published successfully" From 105210bdc736c1fa034d254f066d2f82cfcbadb0 Mon Sep 17 00:00:00 2001 From: Grant Rodgers Date: Tue, 13 Nov 2018 14:16:45 -0800 Subject: [PATCH 44/54] Clear release yaml before appending to it (#613) Without this change, running the release multiple times causes the content to be duplicated in the release yaml. --- hack/release.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hack/release.sh b/hack/release.sh index 1cb9f8e19b1..eb299ce2686 100755 --- a/hack/release.sh +++ b/hack/release.sh @@ -70,7 +70,7 @@ done # Assemble the release for yaml in "${!RELEASES[@]}"; do echo "Assembling Knative Eventing - ${yaml}" - touch ${yaml} + echo "" > ${yaml} for component in ${RELEASES[${yaml}]}; do echo "---" >> ${yaml} echo "# ${component}" >> ${yaml} From e4d44ed5f9b404d9228ca507b65cfaf2e0d88f5c Mon Sep 17 00:00:00 2001 From: Grant Rodgers Date: Tue, 13 Nov 2018 15:45:45 -0800 Subject: [PATCH 45/54] Vendor only parts of knative/test-infra (#614) All we need is scripts/ and tools/dep-collector. The additional deps are dependencies of dep-collector. --- Gopkg.lock | 35 +- Gopkg.toml | 4 +- .../google/licenseclassifier/LICENSE | 202 ++ .../google/licenseclassifier/classifier.go | 429 ++++ .../file_system_resources.go | 65 + .../google/licenseclassifier/forbidden.go | 48 + .../licenseclassifier/internal/sets/sets.go | 20 + .../internal/sets/stringset.go | 228 ++ .../google/licenseclassifier/license_type.go | 375 +++ .../licenseclassifier/licenses/Unlicense.txt | 24 + .../licenseclassifier/licenses/licenses.db | Bin 0 -> 5477296 bytes .../stringclassifier/LICENSE | 202 ++ .../stringclassifier/classifier.go | 560 ++++ .../stringclassifier/internal/pq/priority.go | 111 + .../stringclassifier/searchset/searchset.go | 491 ++++ .../searchset/tokenizer/tokenizer.go | 175 ++ .../.github/pull-request-template.md | 7 - .../knative/test-infra/CONTRIBUTING.md | 3 - .../github.com/knative/test-infra/Gopkg.lock | 28 - .../github.com/knative/test-infra/Gopkg.toml | 14 - vendor/github.com/knative/test-infra/OWNERS | 9 - .../github.com/knative/test-infra/README.md | 17 - .../github.com/knative/test-infra/WORKSPACE | 52 - .../knative/test-infra/ci/README.md | 3 - .../knative/test-infra/ci/gubernator/Makefile | 33 - .../test-infra/ci/gubernator/README.md | 7 - .../test-infra/ci/gubernator/config.yaml | 71 - .../test-infra/ci/gubernator/redir_github.py | 25 - .../knative/test-infra/ci/prow/Makefile | 42 - .../knative/test-infra/ci/prow/README.md | 10 - .../test-infra/ci/prow/boskos/README.md | 6 - .../test-infra/ci/prow/boskos/config.yaml | 152 -- .../ci/prow/boskos/config_start.yaml | 23 - .../test-infra/ci/prow/boskos/resources.yaml | 38 - .../knative/test-infra/ci/prow/cluster.yaml | 350 --- .../knative/test-infra/ci/prow/config.yaml | 2250 ----------------- .../test-infra/ci/prow/config_start.yaml | 339 --- .../knative/test-infra/ci/prow/plugins.yaml | 41 - .../knative/test-infra/ci/prow/prow_setup.md | 71 - .../knative/test-infra/ci/testgrid/Makefile | 29 - .../knative/test-infra/ci/testgrid/README.md | 6 - .../test-infra/ci/testgrid/config.yaml | 216 -- vendor/github.com/knative/test-infra/dummy.go | 27 - .../knative/test-infra/images/README.md | 3 - .../test-infra/images/apicoverage/Dockerfile | 20 - .../test-infra/images/apicoverage/Makefile | 23 - .../test-infra/images/apicoverage/README.md | 3 - .../test-infra/images/prow-tests/Dockerfile | 56 - .../test-infra/images/prow-tests/Makefile | 34 - .../test-infra/images/prow-tests/README.md | 13 - .../knative/test-infra/test/e2e-tests.sh | 50 - .../test-infra/test/presubmit-tests.sh | 50 - .../test/unit/e2e-custom-flag-tests.sh | 38 - .../test-infra/test/unit/library-tests.sh | 50 - ...presubmit-full-custom-integration-tests.sh | 28 - .../presubmit-integration-tests-common.sh | 48 - ...submit-partial-custom-integration-tests.sh | 33 - .../test-infra/test/unit/release-tests.sh | 108 - .../knative/test-infra/tools/README.md | 3 - .../test-infra/tools/apicoverage/README.md | 14 - .../tools/apicoverage/apicoverage.go | 241 -- .../knative/test-infra/tools/gcs/gcs.go | 112 - .../test-infra/tools/githubhelper/Makefile | 17 - .../test-infra/tools/githubhelper/README.md | 10 - .../tools/githubhelper/githubhelper.go | 85 - .../test-infra/tools/testgrid/testgrid.go | 69 - vendor/github.com/sergi/go-diff/AUTHORS | 25 + vendor/github.com/sergi/go-diff/CONTRIBUTORS | 32 + vendor/github.com/sergi/go-diff/LICENSE | 20 + .../sergi/go-diff/diffmatchpatch/diff.go | 1344 ++++++++++ .../go-diff/diffmatchpatch/diffmatchpatch.go | 46 + .../sergi/go-diff/diffmatchpatch/match.go | 160 ++ .../sergi/go-diff/diffmatchpatch/mathutil.go | 23 + .../sergi/go-diff/diffmatchpatch/patch.go | 556 ++++ .../go-diff/diffmatchpatch/stringutil.go | 88 + 75 files changed, 5257 insertions(+), 4983 deletions(-) create mode 100644 vendor/github.com/google/licenseclassifier/LICENSE create mode 100644 vendor/github.com/google/licenseclassifier/classifier.go create mode 100644 vendor/github.com/google/licenseclassifier/file_system_resources.go create mode 100644 vendor/github.com/google/licenseclassifier/forbidden.go create mode 100644 vendor/github.com/google/licenseclassifier/internal/sets/sets.go create mode 100644 vendor/github.com/google/licenseclassifier/internal/sets/stringset.go create mode 100644 vendor/github.com/google/licenseclassifier/license_type.go create mode 100644 vendor/github.com/google/licenseclassifier/licenses/Unlicense.txt create mode 100644 vendor/github.com/google/licenseclassifier/licenses/licenses.db create mode 100644 vendor/github.com/google/licenseclassifier/stringclassifier/LICENSE create mode 100644 vendor/github.com/google/licenseclassifier/stringclassifier/classifier.go create mode 100644 vendor/github.com/google/licenseclassifier/stringclassifier/internal/pq/priority.go create mode 100644 vendor/github.com/google/licenseclassifier/stringclassifier/searchset/searchset.go create mode 100644 vendor/github.com/google/licenseclassifier/stringclassifier/searchset/tokenizer/tokenizer.go delete mode 100644 vendor/github.com/knative/test-infra/.github/pull-request-template.md delete mode 100644 vendor/github.com/knative/test-infra/CONTRIBUTING.md delete mode 100644 vendor/github.com/knative/test-infra/Gopkg.lock delete mode 100644 vendor/github.com/knative/test-infra/Gopkg.toml delete mode 100644 vendor/github.com/knative/test-infra/OWNERS delete mode 100644 vendor/github.com/knative/test-infra/README.md delete mode 100644 vendor/github.com/knative/test-infra/WORKSPACE delete mode 100644 vendor/github.com/knative/test-infra/ci/README.md delete mode 100644 vendor/github.com/knative/test-infra/ci/gubernator/Makefile delete mode 100644 vendor/github.com/knative/test-infra/ci/gubernator/README.md delete mode 100644 vendor/github.com/knative/test-infra/ci/gubernator/config.yaml delete mode 100644 vendor/github.com/knative/test-infra/ci/gubernator/redir_github.py delete mode 100644 vendor/github.com/knative/test-infra/ci/prow/Makefile delete mode 100644 vendor/github.com/knative/test-infra/ci/prow/README.md delete mode 100644 vendor/github.com/knative/test-infra/ci/prow/boskos/README.md delete mode 100644 vendor/github.com/knative/test-infra/ci/prow/boskos/config.yaml delete mode 100644 vendor/github.com/knative/test-infra/ci/prow/boskos/config_start.yaml delete mode 100644 vendor/github.com/knative/test-infra/ci/prow/boskos/resources.yaml delete mode 100644 vendor/github.com/knative/test-infra/ci/prow/cluster.yaml delete mode 100644 vendor/github.com/knative/test-infra/ci/prow/config.yaml delete mode 100644 vendor/github.com/knative/test-infra/ci/prow/config_start.yaml delete mode 100644 vendor/github.com/knative/test-infra/ci/prow/plugins.yaml delete mode 100644 vendor/github.com/knative/test-infra/ci/prow/prow_setup.md delete mode 100644 vendor/github.com/knative/test-infra/ci/testgrid/Makefile delete mode 100644 vendor/github.com/knative/test-infra/ci/testgrid/README.md delete mode 100644 vendor/github.com/knative/test-infra/ci/testgrid/config.yaml delete mode 100644 vendor/github.com/knative/test-infra/dummy.go delete mode 100644 vendor/github.com/knative/test-infra/images/README.md delete mode 100644 vendor/github.com/knative/test-infra/images/apicoverage/Dockerfile delete mode 100644 vendor/github.com/knative/test-infra/images/apicoverage/Makefile delete mode 100644 vendor/github.com/knative/test-infra/images/apicoverage/README.md delete mode 100644 vendor/github.com/knative/test-infra/images/prow-tests/Dockerfile delete mode 100644 vendor/github.com/knative/test-infra/images/prow-tests/Makefile delete mode 100644 vendor/github.com/knative/test-infra/images/prow-tests/README.md delete mode 100755 vendor/github.com/knative/test-infra/test/e2e-tests.sh delete mode 100755 vendor/github.com/knative/test-infra/test/presubmit-tests.sh delete mode 100755 vendor/github.com/knative/test-infra/test/unit/e2e-custom-flag-tests.sh delete mode 100755 vendor/github.com/knative/test-infra/test/unit/library-tests.sh delete mode 100755 vendor/github.com/knative/test-infra/test/unit/presubmit-full-custom-integration-tests.sh delete mode 100755 vendor/github.com/knative/test-infra/test/unit/presubmit-integration-tests-common.sh delete mode 100755 vendor/github.com/knative/test-infra/test/unit/presubmit-partial-custom-integration-tests.sh delete mode 100755 vendor/github.com/knative/test-infra/test/unit/release-tests.sh delete mode 100644 vendor/github.com/knative/test-infra/tools/README.md delete mode 100644 vendor/github.com/knative/test-infra/tools/apicoverage/README.md delete mode 100644 vendor/github.com/knative/test-infra/tools/apicoverage/apicoverage.go delete mode 100644 vendor/github.com/knative/test-infra/tools/gcs/gcs.go delete mode 100644 vendor/github.com/knative/test-infra/tools/githubhelper/Makefile delete mode 100644 vendor/github.com/knative/test-infra/tools/githubhelper/README.md delete mode 100644 vendor/github.com/knative/test-infra/tools/githubhelper/githubhelper.go delete mode 100644 vendor/github.com/knative/test-infra/tools/testgrid/testgrid.go create mode 100644 vendor/github.com/sergi/go-diff/AUTHORS create mode 100644 vendor/github.com/sergi/go-diff/CONTRIBUTORS create mode 100644 vendor/github.com/sergi/go-diff/LICENSE create mode 100644 vendor/github.com/sergi/go-diff/diffmatchpatch/diff.go create mode 100644 vendor/github.com/sergi/go-diff/diffmatchpatch/diffmatchpatch.go create mode 100644 vendor/github.com/sergi/go-diff/diffmatchpatch/match.go create mode 100644 vendor/github.com/sergi/go-diff/diffmatchpatch/mathutil.go create mode 100644 vendor/github.com/sergi/go-diff/diffmatchpatch/patch.go create mode 100644 vendor/github.com/sergi/go-diff/diffmatchpatch/stringutil.go diff --git a/Gopkg.lock b/Gopkg.lock index 2832e370dd1..f69177a62d6 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -183,6 +183,21 @@ pruneopts = "NUT" revision = "24818f796faf91cd76ec7bddd72458fbced7a6c1" +[[projects]] + branch = "master" + digest = "1:0d5e3798bfa2642ac268341c96710b8def1f3cbc3bc803c421d90704d72107d8" + name = "github.com/google/licenseclassifier" + packages = [ + ".", + "internal/sets", + "stringclassifier", + "stringclassifier/internal/pq", + "stringclassifier/searchset", + "stringclassifier/searchset/tokenizer", + ] + pruneopts = "NUT" + revision = "e979a0b10eebe748549c702a25e997c556349da6" + [[projects]] digest = "1:1bb197a3b5db4e06e00b7560f8e89836c486627f2a0338332ed37daa003d259e" name = "github.com/google/uuid" @@ -334,10 +349,13 @@ [[projects]] branch = "master" - digest = "1:4d0724515d22c3964ef7bf671c989c45eefb81d71be09c1d8ab74acbdbb2ca94" + digest = "1:a8f096af01ca3ef74546e412d0ffc5ad526bd17ae88495c04e6042200fc7fabc" name = "github.com/knative/test-infra" - packages = ["."] - pruneopts = "T" + packages = [ + "scripts", + "tools/dep-collector", + ] + pruneopts = "UT" revision = "f710a703baf3ac7e5e9005693fb1c8d61c4eccbb" [[projects]] @@ -467,6 +485,14 @@ pruneopts = "NUT" revision = "3113b8401b8a98917cde58f8bbd42a1b1c03b1fd" +[[projects]] + digest = "1:d917313f309bda80d27274d53985bc65651f81a5b66b820749ac7f8ef061fd04" + name = "github.com/sergi/go-diff" + packages = ["diffmatchpatch"] + pruneopts = "NUT" + revision = "1744e2970ca51c86172c8190fadad617561ed6e7" + version = "v1.0.0" + [[projects]] digest = "1:15e5c398fbd9d2c439b635a08ac161b13d04f0c2aa587fe256b65dc0c3efe8b7" name = "github.com/spf13/pflag" @@ -1009,7 +1035,8 @@ "github.com/knative/serving/pkg/apis/serving/v1alpha1", "github.com/knative/serving/pkg/client/clientset/versioned", "github.com/knative/serving/pkg/client/clientset/versioned/typed/serving/v1alpha1", - "github.com/knative/test-infra", + "github.com/knative/test-infra/scripts", + "github.com/knative/test-infra/tools/dep-collector", "github.com/prometheus/client_golang/prometheus/promhttp", "go.opencensus.io/trace", "go.uber.org/atomic", diff --git a/Gopkg.toml b/Gopkg.toml index 12934b0b2ce..c0c91e568a2 100644 --- a/Gopkg.toml +++ b/Gopkg.toml @@ -8,7 +8,8 @@ required = [ "k8s.io/code-generator/cmd/client-gen", "k8s.io/code-generator/cmd/lister-gen", "k8s.io/code-generator/cmd/informer-gen", - "github.com/knative/test-infra", + "github.com/knative/test-infra/scripts", + "github.com/knative/test-infra/tools/dep-collector", ] [prune] @@ -23,7 +24,6 @@ required = [ [[prune.project]] name = "github.com/knative/test-infra" - unused-packages = false non-go = false # Use HEAD (2018-04-21) to pick up: diff --git a/vendor/github.com/google/licenseclassifier/LICENSE b/vendor/github.com/google/licenseclassifier/LICENSE new file mode 100644 index 00000000000..d6456956733 --- /dev/null +++ b/vendor/github.com/google/licenseclassifier/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + 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. diff --git a/vendor/github.com/google/licenseclassifier/classifier.go b/vendor/github.com/google/licenseclassifier/classifier.go new file mode 100644 index 00000000000..8d39cafbf73 --- /dev/null +++ b/vendor/github.com/google/licenseclassifier/classifier.go @@ -0,0 +1,429 @@ +// Copyright 2017 Google Inc. +// +// 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 licenseclassifier provides methods to identify the open source +// license that most closely matches an unknown license. +package licenseclassifier + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "fmt" + "html" + "io" + "math" + "regexp" + "sort" + "strings" + "sync" + "unicode" + + "github.com/google/licenseclassifier/stringclassifier" + "github.com/google/licenseclassifier/stringclassifier/searchset" +) + +// DefaultConfidenceThreshold is the minimum confidence percentage we're willing to accept in order +// to say that a match is good. +const DefaultConfidenceThreshold = 0.80 + +var ( + // Normalizers is a list of functions that get applied to the strings + // before they are registered with the string classifier. + Normalizers = []stringclassifier.NormalizeFunc{ + html.UnescapeString, + removeShebangLine, + RemoveNonWords, + NormalizeEquivalentWords, + NormalizePunctuation, + strings.ToLower, + removeIgnorableTexts, + stringclassifier.FlattenWhitespace, + strings.TrimSpace, + } + + // commonLicenseWords are words that are common to all known licenses. + // If an unknown text doesn't have at least one of these, then we can + // ignore it. + commonLicenseWords = []*regexp.Regexp{ + regexp.MustCompile(`(?i)\bcode\b`), + regexp.MustCompile(`(?i)\blicense\b`), + regexp.MustCompile(`(?i)\boriginal\b`), + regexp.MustCompile(`(?i)\brights\b`), + regexp.MustCompile(`(?i)\bsoftware\b`), + regexp.MustCompile(`(?i)\bterms\b`), + regexp.MustCompile(`(?i)\bversion\b`), + regexp.MustCompile(`(?i)\bwork\b`), + } +) + +// License is a classifier pre-loaded with known open source licenses. +type License struct { + c *stringclassifier.Classifier + + // Threshold is the lowest confidence percentage acceptable for the + // classifier. + Threshold float64 +} + +// New creates a license classifier and pre-loads it with known open source licenses. +func New(threshold float64) (*License, error) { + classifier := &License{ + c: stringclassifier.New(threshold, Normalizers...), + Threshold: threshold, + } + if err := classifier.registerLicenses(LicenseArchive); err != nil { + return nil, fmt.Errorf("cannot register licenses: %v", err) + } + return classifier, nil +} + +// NewWithForbiddenLicenses creates a license classifier and pre-loads it with +// known open source licenses which are forbidden. +func NewWithForbiddenLicenses(threshold float64) (*License, error) { + classifier := &License{ + c: stringclassifier.New(threshold, Normalizers...), + Threshold: threshold, + } + if err := classifier.registerLicenses(ForbiddenLicenseArchive); err != nil { + return nil, fmt.Errorf("cannot register licenses: %v", err) + } + return classifier, nil +} + +// WithinConfidenceThreshold returns true if the confidence value is above or +// equal to the confidence threshold. +func (c *License) WithinConfidenceThreshold(conf float64) bool { + return conf > c.Threshold || math.Abs(conf-c.Threshold) < math.SmallestNonzeroFloat64 +} + +// NearestMatch returns the "nearest" match to the given set of known licenses. +// Returned are the name of the license, and a confidence percentage indicating +// how confident the classifier is in the result. +func (c *License) NearestMatch(contents string) *stringclassifier.Match { + if !c.hasCommonLicenseWords(contents) { + return nil + } + m := c.c.NearestMatch(contents) + m.Name = strings.TrimSuffix(m.Name, ".header") + return m +} + +// MultipleMatch matches all licenses within an unknown text. +func (c *License) MultipleMatch(contents string, includeHeaders bool) stringclassifier.Matches { + norm := normalizeText(contents) + if !c.hasCommonLicenseWords(norm) { + return nil + } + + m := make(map[stringclassifier.Match]bool) + var matches stringclassifier.Matches + for _, v := range c.c.MultipleMatch(norm) { + if !c.WithinConfidenceThreshold(v.Confidence) { + continue + } + + if !includeHeaders && strings.HasSuffix(v.Name, ".header") { + continue + } + + v.Name = strings.TrimSuffix(v.Name, ".header") + if re, ok := forbiddenRegexps[v.Name]; ok && !re.MatchString(norm) { + continue + } + if _, ok := m[*v]; !ok { + m[*v] = true + matches = append(matches, v) + } + } + sort.Sort(matches) + return matches +} + +func normalizeText(s string) string { + for _, n := range Normalizers { + s = n(s) + } + return s +} + +// hasCommonLicenseWords returns true if the unknown text has at least one word +// that's common to all licenses. +func (c *License) hasCommonLicenseWords(s string) bool { + for _, re := range commonLicenseWords { + if re.MatchString(s) { + return true + } + } + return false +} + +type archivedValue struct { + name string + normalized string + set *searchset.SearchSet +} + +// registerLicenses loads all known licenses and adds them to c as known values +// for comparison. The allocated space after ingesting the 'licenses.db' +// archive is ~167M. +func (c *License) registerLicenses(archive string) error { + contents, err := ReadLicenseFile(archive) + if err != nil { + return err + } + + reader := bytes.NewReader(contents) + gr, err := gzip.NewReader(reader) + if err != nil { + return err + } + defer gr.Close() + + tr := tar.NewReader(gr) + + var muVals sync.Mutex + var vals []archivedValue + for i := 0; ; i++ { + hdr, err := tr.Next() + if err == io.EOF { + break + } + if err != nil { + return err + } + + name := strings.TrimSuffix(hdr.Name, ".txt") + + // Read normalized value. + var b bytes.Buffer + if _, err := io.Copy(&b, tr); err != nil { + return err + } + normalized := b.String() + b.Reset() + + // Read precomputed hashes. + hdr, err = tr.Next() + if err != nil { + return err + } + + if _, err := io.Copy(&b, tr); err != nil { + return err + } + + var set searchset.SearchSet + searchset.Deserialize(&b, &set) + + muVals.Lock() + vals = append(vals, archivedValue{name, normalized, &set}) + muVals.Unlock() + } + + for _, v := range vals { + if err = c.c.AddPrecomputedValue(v.name, v.normalized, v.set); err != nil { + return err + } + } + return nil +} + +// endOfLicenseText is text commonly associated with the end of a license. We +// can remove text that occurs after it. +var endOfLicenseText = []string{ + "END OF TERMS AND CONDITIONS", +} + +// TrimExtraneousTrailingText removes text after an obvious end of the license +// and does not include substantive text of the license. +func TrimExtraneousTrailingText(s string) string { + for _, e := range endOfLicenseText { + if i := strings.LastIndex(s, e); i != -1 { + return s[:i+len(e)] + } + } + return s +} + +var copyrightRE = regexp.MustCompile(`(?m)(?i:Copyright)\s+(?i:©\s+|\(c\)\s+)?(?:\d{2,4})(?:[-,]\s*\d{2,4})*,?\s*(?i:by)?\s*(.*?(?i:\s+Inc\.)?)[.,]?\s*(?i:All rights reserved\.?)?\s*$`) + +// CopyrightHolder finds a copyright notification, if it exists, and returns +// the copyright holder. +func CopyrightHolder(contents string) string { + matches := copyrightRE.FindStringSubmatch(contents) + if len(matches) == 2 { + return matches[1] + } + return "" +} + +var publicDomainRE = regexp.MustCompile("(?i)(this file )?is( in the)? public domain") + +// HasPublicDomainNotice performs a simple regex over the contents to see if a +// public domain notice is in there. As you can imagine, this isn't 100% +// definitive, but can be useful if a license match isn't found. +func (c *License) HasPublicDomainNotice(contents string) bool { + return publicDomainRE.FindString(contents) != "" +} + +// ignorableTexts is a list of lines at the start of the string we can remove +// to get a cleaner match. +var ignorableTexts = []*regexp.Regexp{ + regexp.MustCompile(`(?i)^(?:the )?mit license(?: \(mit\))?$`), + regexp.MustCompile(`(?i)^(?:new )?bsd license$`), + regexp.MustCompile(`(?i)^copyright and permission notice$`), + regexp.MustCompile(`(?i)^copyright (\(c\) )?(\[yyyy\]|\d{4})[,.]? .*$`), + regexp.MustCompile(`(?i)^(all|some) rights reserved\.?$`), + regexp.MustCompile(`(?i)^@license$`), + regexp.MustCompile(`^\s*$`), +} + +// removeIgnorableTexts removes common text, which is not important for +// classification, that shows up before the body of the license. +func removeIgnorableTexts(s string) string { + lines := strings.Split(strings.TrimRight(s, "\n"), "\n") + var start int + for ; start < len(lines); start++ { + line := strings.TrimSpace(lines[start]) + var matches bool + for _, re := range ignorableTexts { + if re.MatchString(line) { + matches = true + break + } + } + if !matches { + break + } + } + end := len(lines) + if start > end { + return "\n" + } + return strings.Join(lines[start:end], "\n") + "\n" +} + +// removeShebangLine removes the '#!...' line if it's the first line in the +// file. Note that if it's the only line in a comment, it won't be removed. +func removeShebangLine(s string) string { + lines := strings.Split(s, "\n") + if len(lines) <= 1 || !strings.HasPrefix(lines[0], "#!") { + return s + } + + return strings.Join(lines[1:], "\n") +} + +// isDecorative returns true if the line is made up purely of non-letter and +// non-digit characters. +func isDecorative(s string) bool { + for _, c := range s { + if unicode.IsLetter(c) || unicode.IsDigit(c) { + return false + } + } + return true +} + +var nonWords = regexp.MustCompile("[[:punct:]]+") + +// RemoveNonWords removes non-words from the string. +func RemoveNonWords(s string) string { + return nonWords.ReplaceAllString(s, " ") +} + +// interchangeablePunctutation is punctuation that can be normalized. +var interchangeablePunctuation = []struct { + interchangeable *regexp.Regexp + substitute string +}{ + // Hyphen, Dash, En Dash, and Em Dash. + {regexp.MustCompile(`[-‒–—]`), "-"}, + // Single, Double, Curly Single, and Curly Double. + {regexp.MustCompile("['\"`‘’“”]"), "'"}, + // Copyright. + {regexp.MustCompile("©"), "(c)"}, + // Hyphen-separated words. + {regexp.MustCompile(`(\S)-\s+(\S)`), "${1}-${2}"}, + // Currency and Section. (Different copies of the CDDL use each marker.) + {regexp.MustCompile("[§¤]"), "(s)"}, + // Middle Dot + {regexp.MustCompile("·"), "*"}, +} + +// NormalizePunctuation takes all hyphens and quotes and normalizes them. +func NormalizePunctuation(s string) string { + for _, iw := range interchangeablePunctuation { + s = iw.interchangeable.ReplaceAllString(s, iw.substitute) + } + return s +} + +// interchangeableWords are words we can substitute for a normalized form +// without changing the meaning of the license. See +// https://spdx.org/spdx-license-list/matching-guidelines for the list. +var interchangeableWords = []struct { + interchangeable *regexp.Regexp + substitute string +}{ + {regexp.MustCompile("(?i)Acknowledgment"), "Acknowledgement"}, + {regexp.MustCompile("(?i)Analogue"), "Analog"}, + {regexp.MustCompile("(?i)Analyse"), "Analyze"}, + {regexp.MustCompile("(?i)Artefact"), "Artifact"}, + {regexp.MustCompile("(?i)Authorisation"), "Authorization"}, + {regexp.MustCompile("(?i)Authorised"), "Authorized"}, + {regexp.MustCompile("(?i)Calibre"), "Caliber"}, + {regexp.MustCompile("(?i)Cancelled"), "Canceled"}, + {regexp.MustCompile("(?i)Capitalisations"), "Capitalizations"}, + {regexp.MustCompile("(?i)Catalogue"), "Catalog"}, + {regexp.MustCompile("(?i)Categorise"), "Categorize"}, + {regexp.MustCompile("(?i)Centre"), "Center"}, + {regexp.MustCompile("(?i)Emphasised"), "Emphasized"}, + {regexp.MustCompile("(?i)Favour"), "Favor"}, + {regexp.MustCompile("(?i)Favourite"), "Favorite"}, + {regexp.MustCompile("(?i)Fulfil"), "Fulfill"}, + {regexp.MustCompile("(?i)Fulfilment"), "Fulfillment"}, + {regexp.MustCompile("(?i)Initialise"), "Initialize"}, + {regexp.MustCompile("(?i)Judgment"), "Judgement"}, + {regexp.MustCompile("(?i)Labelling"), "Labeling"}, + {regexp.MustCompile("(?i)Labour"), "Labor"}, + {regexp.MustCompile("(?i)Licence"), "License"}, + {regexp.MustCompile("(?i)Maximise"), "Maximize"}, + {regexp.MustCompile("(?i)Modelled"), "Modeled"}, + {regexp.MustCompile("(?i)Modelling"), "Modeling"}, + {regexp.MustCompile("(?i)Offence"), "Offense"}, + {regexp.MustCompile("(?i)Optimise"), "Optimize"}, + {regexp.MustCompile("(?i)Organisation"), "Organization"}, + {regexp.MustCompile("(?i)Organise"), "Organize"}, + {regexp.MustCompile("(?i)Practise"), "Practice"}, + {regexp.MustCompile("(?i)Programme"), "Program"}, + {regexp.MustCompile("(?i)Realise"), "Realize"}, + {regexp.MustCompile("(?i)Recognise"), "Recognize"}, + {regexp.MustCompile("(?i)Signalling"), "Signaling"}, + {regexp.MustCompile("(?i)Sub[- ]license"), "Sublicense"}, + {regexp.MustCompile("(?i)Utilisation"), "Utilization"}, + {regexp.MustCompile("(?i)Whilst"), "While"}, + {regexp.MustCompile("(?i)Wilful"), "Wilfull"}, + {regexp.MustCompile("(?i)Non-commercial"), "Noncommercial"}, + {regexp.MustCompile("(?i)Per cent"), "Percent"}, +} + +// NormalizeEquivalentWords normalizes equivalent words that are interchangeable. +func NormalizeEquivalentWords(s string) string { + for _, iw := range interchangeableWords { + s = iw.interchangeable.ReplaceAllString(s, iw.substitute) + } + return s +} diff --git a/vendor/github.com/google/licenseclassifier/file_system_resources.go b/vendor/github.com/google/licenseclassifier/file_system_resources.go new file mode 100644 index 00000000000..0d92e3aec8d --- /dev/null +++ b/vendor/github.com/google/licenseclassifier/file_system_resources.go @@ -0,0 +1,65 @@ +// Copyright 2017 Google Inc. +// +// 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 licenseclassifier + +import ( + "go/build" + "io/ioutil" + "os" + "path/filepath" +) + +const ( + // LicenseDirectory is the directory where the prototype licenses are kept. + LicenseDirectory = "src/github.com/google/licenseclassifier/licenses" + // LicenseArchive is the name of the archive containing preprocessed + // license texts. + LicenseArchive = "licenses.db" + // ForbiddenLicenseArchive is the name of the archive containing preprocessed + // forbidden license texts only. + ForbiddenLicenseArchive = "forbidden_licenses.db" +) + +func findInGOPATH(rel string) (fullPath string, err error) { + for _, path := range filepath.SplitList(build.Default.GOPATH) { + fullPath := filepath.Join(path, rel) + if _, err := os.Stat(fullPath); err != nil { + if os.IsNotExist(err) { + continue + } + return "", err + } + return fullPath, nil + } + return "", nil +} + +// ReadLicenseFile locates and reads the license file. +func ReadLicenseFile(filename string) ([]byte, error) { + archive, err := findInGOPATH(filepath.Join(LicenseDirectory, filename)) + if err != nil || archive == "" { + return nil, err + } + return ioutil.ReadFile(archive) +} + +// ReadLicenseDir reads directory containing the license files. +func ReadLicenseDir() ([]os.FileInfo, error) { + filename, err := findInGOPATH(filepath.Join(LicenseDirectory, LicenseArchive)) + if err != nil || filename == "" { + return nil, err + } + return ioutil.ReadDir(filepath.Dir(filename)) +} diff --git a/vendor/github.com/google/licenseclassifier/forbidden.go b/vendor/github.com/google/licenseclassifier/forbidden.go new file mode 100644 index 00000000000..6c02ee5d6da --- /dev/null +++ b/vendor/github.com/google/licenseclassifier/forbidden.go @@ -0,0 +1,48 @@ +// Copyright 2017 Google Inc. +// +// 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 licenseclassifier + +import "regexp" + +var ( + reCCBYNC = regexp.MustCompile(`(?i).*\bAttribution NonCommercial\b.*`) + reCCBYNCND = regexp.MustCompile(`(?i).*\bAttribution NonCommercial NoDerivs\b.*`) + reCCBYNCSA = regexp.MustCompile(`(?i).*\bAttribution NonCommercial ShareAlike\b.*`) + + // forbiddenRegexps are regular expressions we expect to find in + // forbidden licenses. If we think we have a forbidden license but + // don't find the equivalent phrase, then it's probably just a + // misclassification. + forbiddenRegexps = map[string]*regexp.Regexp{ + AGPL10: regexp.MustCompile(`(?i).*\bAFFERO GENERAL PUBLIC LICENSE\b.*`), + AGPL30: regexp.MustCompile(`(?i).*\bGNU AFFERO GENERAL PUBLIC LICENSE\b.*`), + CCBYNC10: reCCBYNC, + CCBYNC20: reCCBYNC, + CCBYNC25: reCCBYNC, + CCBYNC30: reCCBYNC, + CCBYNC40: reCCBYNC, + CCBYNCND10: regexp.MustCompile(`(?i).*\bAttribution NoDerivs NonCommercial\b.*`), + CCBYNCND20: reCCBYNCND, + CCBYNCND25: reCCBYNCND, + CCBYNCND30: reCCBYNCND, + CCBYNCND40: regexp.MustCompile(`(?i).*\bAttribution NonCommercial NoDerivatives\b.*`), + CCBYNCSA10: reCCBYNCSA, + CCBYNCSA20: reCCBYNCSA, + CCBYNCSA25: reCCBYNCSA, + CCBYNCSA30: reCCBYNCSA, + CCBYNCSA40: reCCBYNCSA, + WTFPL: regexp.MustCompile(`(?i).*\bDO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE\b.*`), + } +) diff --git a/vendor/github.com/google/licenseclassifier/internal/sets/sets.go b/vendor/github.com/google/licenseclassifier/internal/sets/sets.go new file mode 100644 index 00000000000..f34ae5bf862 --- /dev/null +++ b/vendor/github.com/google/licenseclassifier/internal/sets/sets.go @@ -0,0 +1,20 @@ +// Copyright 2017 Google Inc. +// +// 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 sets provides sets for storing collections of unique elements. +package sets + +// present is an empty struct used as the "value" in the map[int], since +// empty structs consume zero bytes (unlike 1 unnecessary byte per bool). +type present struct{} diff --git a/vendor/github.com/google/licenseclassifier/internal/sets/stringset.go b/vendor/github.com/google/licenseclassifier/internal/sets/stringset.go new file mode 100644 index 00000000000..54edbd4bfde --- /dev/null +++ b/vendor/github.com/google/licenseclassifier/internal/sets/stringset.go @@ -0,0 +1,228 @@ +// Copyright 2017 Google Inc. +// +// 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 sets + +import ( + "fmt" + "sort" + "strings" +) + +// StringSet stores a set of unique string elements. +type StringSet struct { + set map[string]present +} + +// NewStringSet creates a StringSet containing the supplied initial string elements. +func NewStringSet(elements ...string) *StringSet { + s := &StringSet{} + s.set = make(map[string]present) + s.Insert(elements...) + return s +} + +// Copy returns a newly allocated copy of the supplied StringSet. +func (s *StringSet) Copy() *StringSet { + c := NewStringSet() + if s != nil { + for e := range s.set { + c.set[e] = present{} + } + } + return c +} + +// Insert zero or more string elements into the StringSet. +// As expected for a Set, elements already present in the StringSet are +// simply ignored. +func (s *StringSet) Insert(elements ...string) { + for _, e := range elements { + s.set[e] = present{} + } +} + +// Delete zero or more string elements from the StringSet. +// Any elements not present in the StringSet are simply ignored. +func (s *StringSet) Delete(elements ...string) { + for _, e := range elements { + delete(s.set, e) + } +} + +// Intersect returns a new StringSet containing the intersection of the +// receiver and argument StringSets. Returns an empty set if the argument is nil. +func (s *StringSet) Intersect(other *StringSet) *StringSet { + if other == nil { + return NewStringSet() + } + + // Point a and b to the maps, setting a to the smaller of the two. + a, b := s.set, other.set + if len(b) < len(a) { + a, b = b, a + } + + // Perform the intersection. + intersect := NewStringSet() + for e := range a { + if _, ok := b[e]; ok { + intersect.set[e] = present{} + } + } + return intersect +} + +// Disjoint returns true if the intersection of the receiver and the argument +// StringSets is the empty set. Returns true if the argument is nil or either +// StringSet is the empty set. +func (s *StringSet) Disjoint(other *StringSet) bool { + if other == nil || len(other.set) == 0 || len(s.set) == 0 { + return true + } + + // Point a and b to the maps, setting a to the smaller of the two. + a, b := s.set, other.set + if len(b) < len(a) { + a, b = b, a + } + + // Check for non-empty intersection. + for e := range a { + if _, ok := b[e]; ok { + return false // Early-exit because intersecting. + } + } + return true +} + +// Difference returns a new StringSet containing the elements in the receiver +// that are not present in the argument StringSet. Returns a copy of the +// receiver if the argument is nil. +func (s *StringSet) Difference(other *StringSet) *StringSet { + if other == nil { + return s.Copy() + } + + // Insert only the elements in the receiver that are not present in the + // argument StringSet. + diff := NewStringSet() + for e := range s.set { + if _, ok := other.set[e]; !ok { + diff.set[e] = present{} + } + } + return diff +} + +// Unique returns a new StringSet containing the elements in the receiver +// that are not present in the argument StringSet *and* the elements in the +// argument StringSet that are not in the receiver (which is the union of two +// disjoint sets). Returns a copy of the +// receiver if the argument is nil. +func (s *StringSet) Unique(other *StringSet) *StringSet { + if other == nil { + return s.Copy() + } + + sNotInOther := s.Difference(other) + otherNotInS := other.Difference(s) + + // Duplicate Union implementation here to avoid extra Copy, since both + // sNotInOther and otherNotInS are already copies. + unique := sNotInOther + for e := range otherNotInS.set { + unique.set[e] = present{} + } + return unique +} + +// Equal returns true if the receiver and the argument StringSet contain +// exactly the same elements. +func (s *StringSet) Equal(other *StringSet) bool { + if s == nil || other == nil { + return s == nil && other == nil + } + + // Two sets of different length cannot have the exact same unique elements. + if len(s.set) != len(other.set) { + return false + } + + // Only one loop is needed. If the two sets are known to be of equal + // length, then the two sets are equal only if exactly all of the elements + // in the first set are found in the second. + for e := range s.set { + if _, ok := other.set[e]; !ok { + return false + } + } + + return true +} + +// Union returns a new StringSet containing the union of the receiver and +// argument StringSets. Returns a copy of the receiver if the argument is nil. +func (s *StringSet) Union(other *StringSet) *StringSet { + union := s.Copy() + if other != nil { + for e := range other.set { + union.set[e] = present{} + } + } + return union +} + +// Contains returns true if element is in the StringSet. +func (s *StringSet) Contains(element string) bool { + _, in := s.set[element] + return in +} + +// Len returns the number of unique elements in the StringSet. +func (s *StringSet) Len() int { + return len(s.set) +} + +// Empty returns true if the receiver is the empty set. +func (s *StringSet) Empty() bool { + return len(s.set) == 0 +} + +// Elements returns a []string of the elements in the StringSet, in no +// particular (or consistent) order. +func (s *StringSet) Elements() []string { + elements := []string{} // Return at least an empty slice rather than nil. + for e := range s.set { + elements = append(elements, e) + } + return elements +} + +// Sorted returns a sorted []string of the elements in the StringSet. +func (s *StringSet) Sorted() []string { + elements := s.Elements() + sort.Strings(elements) + return elements +} + +// String formats the StringSet elements as sorted strings, representing them +// in "array initializer" syntax. +func (s *StringSet) String() string { + elements := s.Sorted() + var quoted []string + for _, e := range elements { + quoted = append(quoted, fmt.Sprintf("%q", e)) + } + return fmt.Sprintf("{%s}", strings.Join(quoted, ", ")) +} diff --git a/vendor/github.com/google/licenseclassifier/license_type.go b/vendor/github.com/google/licenseclassifier/license_type.go new file mode 100644 index 00000000000..431e61a2275 --- /dev/null +++ b/vendor/github.com/google/licenseclassifier/license_type.go @@ -0,0 +1,375 @@ +// Copyright 2017 Google Inc. +// +// 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 licenseclassifier + +// *** NOTE: Update this file when adding a new license. You need to: +// +// 1. Add the canonical name to the list, and +// 2. Categorize the license. + +import "github.com/google/licenseclassifier/internal/sets" + +// Canonical names of the licenses. +const ( + // The names come from the https://spdx.org/licenses website, and are + // also the filenames of the licenses in licenseclassifier/licenses. + AFL11 = "AFL-1.1" + AFL12 = "AFL-1.2" + AFL20 = "AFL-2.0" + AFL21 = "AFL-2.1" + AFL30 = "AFL-3.0" + AGPL10 = "AGPL-1.0" + AGPL30 = "AGPL-3.0" + Apache10 = "Apache-1.0" + Apache11 = "Apache-1.1" + Apache20 = "Apache-2.0" + APSL10 = "APSL-1.0" + APSL11 = "APSL-1.1" + APSL12 = "APSL-1.2" + APSL20 = "APSL-2.0" + Artistic10cl8 = "Artistic-1.0-cl8" + Artistic10Perl = "Artistic-1.0-Perl" + Artistic10 = "Artistic-1.0" + Artistic20 = "Artistic-2.0" + BCL = "BCL" + Beerware = "Beerware" + BSD2ClauseFreeBSD = "BSD-2-Clause-FreeBSD" + BSD2ClauseNetBSD = "BSD-2-Clause-NetBSD" + BSD2Clause = "BSD-2-Clause" + BSD3ClauseAttribution = "BSD-3-Clause-Attribution" + BSD3ClauseClear = "BSD-3-Clause-Clear" + BSD3ClauseLBNL = "BSD-3-Clause-LBNL" + BSD3Clause = "BSD-3-Clause" + BSD4Clause = "BSD-4-Clause" + BSD4ClauseUC = "BSD-4-Clause-UC" + BSDProtection = "BSD-Protection" + BSL10 = "BSL-1.0" + CC010 = "CC0-1.0" + CCBY10 = "CC-BY-1.0" + CCBY20 = "CC-BY-2.0" + CCBY25 = "CC-BY-2.5" + CCBY30 = "CC-BY-3.0" + CCBY40 = "CC-BY-4.0" + CCBYNC10 = "CC-BY-NC-1.0" + CCBYNC20 = "CC-BY-NC-2.0" + CCBYNC25 = "CC-BY-NC-2.5" + CCBYNC30 = "CC-BY-NC-3.0" + CCBYNC40 = "CC-BY-NC-4.0" + CCBYNCND10 = "CC-BY-NC-ND-1.0" + CCBYNCND20 = "CC-BY-NC-ND-2.0" + CCBYNCND25 = "CC-BY-NC-ND-2.5" + CCBYNCND30 = "CC-BY-NC-ND-3.0" + CCBYNCND40 = "CC-BY-NC-ND-4.0" + CCBYNCSA10 = "CC-BY-NC-SA-1.0" + CCBYNCSA20 = "CC-BY-NC-SA-2.0" + CCBYNCSA25 = "CC-BY-NC-SA-2.5" + CCBYNCSA30 = "CC-BY-NC-SA-3.0" + CCBYNCSA40 = "CC-BY-NC-SA-4.0" + CCBYND10 = "CC-BY-ND-1.0" + CCBYND20 = "CC-BY-ND-2.0" + CCBYND25 = "CC-BY-ND-2.5" + CCBYND30 = "CC-BY-ND-3.0" + CCBYND40 = "CC-BY-ND-4.0" + CCBYSA10 = "CC-BY-SA-1.0" + CCBYSA20 = "CC-BY-SA-2.0" + CCBYSA25 = "CC-BY-SA-2.5" + CCBYSA30 = "CC-BY-SA-3.0" + CCBYSA40 = "CC-BY-SA-4.0" + CDDL10 = "CDDL-1.0" + CDDL11 = "CDDL-1.1" + CommonsClause = "Commons-Clause" + CPAL10 = "CPAL-1.0" + CPL10 = "CPL-1.0" + eGenix = "eGenix" + EPL10 = "EPL-1.0" + EUPL10 = "EUPL-1.0" + EUPL11 = "EUPL-1.1" + Facebook2Clause = "Facebook-2-Clause" + Facebook3Clause = "Facebook-3-Clause" + FacebookExamples = "Facebook-Examples" + FreeImage = "FreeImage" + FTL = "FTL" + GPL10 = "GPL-1.0" + GPL20 = "GPL-2.0" + GPL20withautoconfexception = "GPL-2.0-with-autoconf-exception" + GPL20withbisonexception = "GPL-2.0-with-bison-exception" + GPL20withclasspathexception = "GPL-2.0-with-classpath-exception" + GPL20withfontexception = "GPL-2.0-with-font-exception" + GPL20withGCCexception = "GPL-2.0-with-GCC-exception" + GPL30 = "GPL-3.0" + GPL30withautoconfexception = "GPL-3.0-with-autoconf-exception" + GPL30withGCCexception = "GPL-3.0-with-GCC-exception" + GUSTFont = "GUST-Font-License" + ImageMagick = "ImageMagick" + IPL10 = "IPL-1.0" + ISC = "ISC" + LGPL20 = "LGPL-2.0" + LGPL21 = "LGPL-2.1" + LGPL30 = "LGPL-3.0" + LGPLLR = "LGPLLR" + Libpng = "Libpng" + Lil10 = "Lil-1.0" + LPL102 = "LPL-1.02" + LPL10 = "LPL-1.0" + LPPL13c = "LPPL-1.3c" + MIT = "MIT" + MPL10 = "MPL-1.0" + MPL11 = "MPL-1.1" + MPL20 = "MPL-2.0" + MSPL = "MS-PL" + NCSA = "NCSA" + NPL10 = "NPL-1.0" + NPL11 = "NPL-1.1" + OFL = "OFL" + OpenSSL = "OpenSSL" + OSL10 = "OSL-1.0" + OSL11 = "OSL-1.1" + OSL20 = "OSL-2.0" + OSL21 = "OSL-2.1" + OSL30 = "OSL-3.0" + PHP301 = "PHP-3.01" + PHP30 = "PHP-3.0" + PIL = "PIL" + Python20complete = "Python-2.0-complete" + Python20 = "Python-2.0" + QPL10 = "QPL-1.0" + Ruby = "Ruby" + SGIB10 = "SGI-B-1.0" + SGIB11 = "SGI-B-1.1" + SGIB20 = "SGI-B-2.0" + SISSL12 = "SISSL-1.2" + SISSL = "SISSL" + Sleepycat = "Sleepycat" + UnicodeTOU = "Unicode-TOU" + Unlicense = "Unlicense" + W3C19980720 = "W3C-19980720" + W3C = "W3C" + WTFPL = "WTFPL" + X11 = "X11" + Xnet = "Xnet" + Zend20 = "Zend-2.0" + ZlibAcknowledgement = "zlib-acknowledgement" + Zlib = "Zlib" + ZPL11 = "ZPL-1.1" + ZPL20 = "ZPL-2.0" + ZPL21 = "ZPL-2.1" +) + +var ( + // Licenses Categorized by Type + + // restricted - Licenses in this category require mandatory source + // distribution if we ships a product that includes third-party code + // protected by such a license. + restrictedType = sets.NewStringSet( + BCL, + CCBYND10, + CCBYND20, + CCBYND25, + CCBYND30, + CCBYND40, + CCBYSA10, + CCBYSA20, + CCBYSA25, + CCBYSA30, + CCBYSA40, + GPL10, + GPL20, + GPL20withautoconfexception, + GPL20withbisonexception, + GPL20withclasspathexception, + GPL20withfontexception, + GPL20withGCCexception, + GPL30, + GPL30withautoconfexception, + GPL30withGCCexception, + LGPL20, + LGPL21, + LGPL30, + NPL10, + NPL11, + OSL10, + OSL11, + OSL20, + OSL21, + OSL30, + QPL10, + Sleepycat, + ) + + // reciprocal - These licenses allow usage of software made available + // under such licenses freely in *unmodified* form. If the third-party + // source code is modified in any way these modifications to the + // original third-party source code must be made available. + reciprocalType = sets.NewStringSet( + APSL10, + APSL11, + APSL12, + APSL20, + CDDL10, + CDDL11, + CPL10, + EPL10, + FreeImage, + IPL10, + MPL10, + MPL11, + MPL20, + Ruby, + ) + + // notice - These licenses contain few restrictions, allowing original + // or modified third-party software to be shipped in any product + // without endangering or encumbering our source code. All of the + // licenses in this category do, however, have an "original Copyright + // notice" or "advertising clause", wherein any external distributions + // must include the notice or clause specified in the license. + noticeType = sets.NewStringSet( + AFL11, + AFL12, + AFL20, + AFL21, + AFL30, + Apache10, + Apache11, + Apache20, + Artistic10cl8, + Artistic10Perl, + Artistic10, + Artistic20, + BSL10, + BSD2ClauseFreeBSD, + BSD2ClauseNetBSD, + BSD2Clause, + BSD3ClauseAttribution, + BSD3ClauseClear, + BSD3ClauseLBNL, + BSD3Clause, + BSD4Clause, + BSD4ClauseUC, + BSDProtection, + CCBY10, + CCBY20, + CCBY25, + CCBY30, + CCBY40, + FTL, + ISC, + ImageMagick, + Libpng, + Lil10, + LPL102, + LPL10, + MSPL, + MIT, + NCSA, + OpenSSL, + PHP301, + PHP30, + PIL, + Python20, + Python20complete, + SGIB10, + SGIB11, + SGIB20, + UnicodeTOU, + W3C19980720, + W3C, + X11, + Xnet, + Zend20, + ZlibAcknowledgement, + Zlib, + ZPL11, + ZPL20, + ZPL21, + ) + + // permissive - These licenses can be used in (relatively rare) cases + // where third-party software is under a license (not "Public Domain" + // or "free for any use" like 'unencumbered') that is even more lenient + // than a 'notice' license. Use the 'permissive' license type when even + // a copyright notice is not required for license compliance. + permissiveType = sets.NewStringSet() + + // unencumbered - Licenses that basically declare that the code is "free for any use". + unencumberedType = sets.NewStringSet( + CC010, + Unlicense, + ) + + // byexceptiononly - Licenses that are incompatible with all (or most) + // uses in combination with our source code. Commercial third-party + // packages that are purchased and licensed only for a specific use + // fall into this category. + byExceptionOnlyType = sets.NewStringSet( + Beerware, + ) + + // forbidden - Licenses that are forbidden to be used. + forbiddenType = sets.NewStringSet( + AGPL10, + AGPL30, + CCBYNC10, + CCBYNC20, + CCBYNC25, + CCBYNC30, + CCBYNC40, + CCBYNCND10, + CCBYNCND20, + CCBYNCND25, + CCBYNCND30, + CCBYNCND40, + CCBYNCSA10, + CCBYNCSA20, + CCBYNCSA25, + CCBYNCSA30, + CCBYNCSA40, + CommonsClause, + Facebook2Clause, + Facebook3Clause, + FacebookExamples, + WTFPL, + ) + + // LicenseTypes is a set of the types of licenses Google recognizes. + LicenseTypes = sets.NewStringSet( + "restricted", + "reciprocal", + "notice", + "permissive", + "unencumbered", + "by_exception_only", + ) +) + +// LicenseType returns the type the license has. +func LicenseType(name string) string { + switch { + case restrictedType.Contains(name): + return "restricted" + case reciprocalType.Contains(name): + return "reciprocal" + case noticeType.Contains(name): + return "notice" + case permissiveType.Contains(name): + return "permissive" + case unencumberedType.Contains(name): + return "unencumbered" + case forbiddenType.Contains(name): + return "FORBIDDEN" + } + return "" +} diff --git a/vendor/github.com/google/licenseclassifier/licenses/Unlicense.txt b/vendor/github.com/google/licenseclassifier/licenses/Unlicense.txt new file mode 100644 index 00000000000..ac8f5f5f15b --- /dev/null +++ b/vendor/github.com/google/licenseclassifier/licenses/Unlicense.txt @@ -0,0 +1,24 @@ +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or distribute +this software, either in source code form or as a compiled binary, for any +purpose, commercial or non-commercial, and by any means. + +In jurisdictions that recognize copyright laws, the author or authors of this +software dedicate any and all copyright interest in the software to the public +domain. We make this dedication for the benefit of the public at large and to +the detriment of our heirs and + +successors. We intend this dedication to be an overt act of relinquishment in +perpetuity of all present and future rights to this software under copyright +law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN +ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to + diff --git a/vendor/github.com/google/licenseclassifier/licenses/licenses.db b/vendor/github.com/google/licenseclassifier/licenses/licenses.db new file mode 100644 index 0000000000000000000000000000000000000000..dc66125cd98a04916852955d0f1c94818ea6258b GIT binary patch literal 5477296 zcmW(*1yCH%)5Q-xEVz3hxI2eKumci;yB@(p4h`-E35WaP7YMGwT@swbga3eVSO^yU zQ3oZO!y__v?ADnLgv9{r~lJ)J2f>=3TvlX>;Ip=f*f8rna>*2`MQTxzZPA zR!q~;t^blENt86$WAQ$asi>#`S;l@jqDhTY^`A6fZcWs$$+6Pb3mLz2r}i8O&G1BU z=TU#j$q(o;=++EHZS>!LiW<9o@ih+H~Z1k^1TYi!1+61t` z6Q#(vMf&U6%c(H;5`Y|L{hHbWnBz|xT5Vox4P9b!2LN=f&XKdCCDGd-255-a8+JBE z1P7{Uy0<52rU$B_lq=-@jfTcpS?8w{6JwtEZo$XFbTj3XZB_K{)sjJlQAPpW)&Y2+ z>x(YrC>ey^Pn{WpC~B;7WrRY9~i51Ymv$2w>T>E8sdri{L%3 z!w~!e!L#%2mDNMpFoAN}nSeOsVzryNz2AL@SRwQSPLX~-YX%^LTOEdP+Ze31o0McwIzBf z|HA4SuRS9;dFNeCziHVP>3A%~P>A)%tM<@s{;UEO??!AIZ+Z9WoH*J{=hM8;J89puZ+i`rP8lN|P*g z7X9WWS;<+H-$!Jx3_z4?{Uq}DqI0UqtJxPmWM@)E&X6RT9AnwNNGA!k)zp-18%P^p z9)wu+=5*`|N2CC(EGqJc&YAjHGm}-MEh0luGFd;jCmq0Dm4<8 zo4MDQ)@hTR@4Raq{9FB&VIk+9)$ zrNbW>i^Uw1^v2n}wjPVbL`Ph#uUv{3`s>U6!r9-3<4)7+MKSo68E9tO^y{3+8t|pq ztJ6?vHV>{E>iYSZvF5Skt8L>s8L`4%N7*%Do3V|RCi@6anG|8^+a>+XNMZLV@?^b9 z@?c-%OXYiDJt4D9eL?EM=3`Igacyy)o{+XlwaejK7?YP>`)8J=@~0%!KK+qU;hES9(dAA_BtYU}^P& zW`@ZrrE?^&)nE;hO0EO`>$XGWx~1trN!mAdP5FS$t`~_-YUA!jMH+Nxju}5wjJ;XO zaw*H@hFopBdn((2O|m;Bn-jS%pU@`nVF$;Yiu4dKF5`y5$yv!nde~R{6IE(7z6s5$z{DU)-S+<)kE7FciWa6t3+Y|)X28zM z9ITo~v@qRK&B)Z_8DH2h4JGN_$H@O4L}U*ywM&wRQ)MGv&iv-SX&^fbw1-w#l;&cU zs@;x75qn_vm4eMAGv%4RE8~(5rIULX(pcw{q8Vxr4#&xy(p5vrgGi@nJKad#`C-cB z1pXm2C6NJLWyx{6mj%uoL4EmVX0q7{)Z($Z@47OGOll@X+z0CY;+yBCngX(kU2b)@ zz?D&DGRcvd?*jsDlZs?WzwBnotboH;F1)`if81LBc3AiN*KZ!XA`|u$Q&yssH?u)& z5~KE=Vn^B@dMYrT6t#C!&hZ^P?7RI?FFEc(aa*?_NxyU|J&eDYFB)xZ>%vOBikp7H>AGiwQ zWm3{S!Oo5^$sF(6aL`6B($-J@4+IC-TlpHWuV$;M5(JP=>Xo7SODhJMpB({x;!u1K3%HBqq&wVLHr) zLcDXbsQT?GM_hUni=2@Zc`5?FBEnt&KCz!^ink!D`DN^ZhK*Kqd-m6qE3dJ`ykT{uHvKh^MqYGJ719P*Grjb;0~akO}p zEB3Zr140=R(cc{R#ct;RY6$;q_l=ue{qcL3X7BRyD8DtBCG&wu?~i!oL}r4%s>k>6 zjkk)mZrsyevx+5;s}Bm>PfzNSj}udc*H3Ha62HrHrEj0y87~B$o)mWXLn5B6T935^ zRNNkfcXqEo-BZXjg;YN0h zB6q-U>^zA~TY=Dd&OiJhG1RD&W^NUYK$y```&4wToY=9;?~SD_fZASwr+fpAuz|U^ zNxasD7fs+lQa$*dwYt{JINCtWy*5T8q^RNf154=52if9b(om_B`dHdiEZ&R!5=8yi zfyI4I<)7&1s-U9bw~x`{m^eB9ppO5dD(*#)e!aQH!{lG^R^h{qWbHG4LxF1z4+JIE ztR5~+^J}e=E2u*=Yd503q4@>p^Hb_x=Fk4f1xa6>CNZh1uxQ?RQ@mdK>rWszK8$g% zpk72Z#mTm-Yqd#`n;Ua+V_ewE3PzSdw+(W^9Yk2v0z_X?kwPwzdf;+76gV}E+IR8T zH#pDi53cG1{{G6qa2TXNQ)$A3!eQOiF8C!t$zKWWaYE;{my-RC$`wK21h|HdWKTh*^T){=j1pU>YyJgLx#>p$|Sz8u? zB0`vll8EDcQQ?J;;MKcv-ExU!zd1>4<1c49B`s4Jri0Lyzr##T4SpiVXwIoVk zv`W->8S3Nej1%OLIs)iBZcyu6W1I=sj^1-*HTnbR5(*zVsA92g+;llOnm=N(g0bxX z=QpvN1~qQW%Ng(ACHx~`K1tGRzH_Rc@7+NYFv(uqllOJ`gFwZQ1wA1cb1V-yKAK zSt0JH?_Kg%nFJSAD8Tzb3E3|)lZOJ=liwacRA_{JcZ&vm*mpb!onQ=Let%V#K5ruX zr2{za9$R4w-gm!l|HWn%EZy;QU)Z7mYusyx0^vR7PmmM1?5Z4yAwGq}JNcIyKKbge z<>;a7m4PLL7bTwrs#7uahHL|ruP)r{%~~?qk-E#7C{;T|2eHJc8|`sMobTm`{Y+in zub#{SuS;ix3ncD3h%sy;3epzenp zJN~OpR};6_XASubW(}$R=nqTu!}oImS#1NO)# zFx8g~CsqXn!QPJ_FL)iY>55Ljjpso7XtSYxM!n{CheiBLst+9tjHqzTP{kzDB7RBT z4ew(GB(@Vefv^!XD*R_OI7*eVjNny+QB&P3>w^7Bb&I8qRs5)d=WQ3_Kt`1`Y~;cG zQ{~-KOG-V9KDO|hRlcYZeQ(jGf4giOYu={3zLKw+(O(h88>M~_ggSn+hsJx6^u1rA zH0qU!G9N~xtty4^Xff^SPt~r5_E#vbH3}lJHHyR!aa*dbV5&r+d`d9~UokoFV5$l4 z=5NIu&+`l_;y1;4Ua~dfv(b&O%Z!>0tTzNEA8LbYc012wMOCaod>kINk~WI6jkl zg`#$pEOV$z7>j3}-K@wjrjTqX(525gEymg4;-|hy$2PfTM+JHWeTlNgr^z0RcmapA zk8wJ0g#Xr*uJok84xk%;NrRi^y6uP18XZm<#YMqZA0{%j(RoMzjp%0%%6>@W{c+-- zy&TNpD(lu}FK3;ia1zsQ-`Gm^`U;)W-Zm7W1;p>k$V<`Xx1Z_!uVK1VbZWZ1>DgbI zxKu1m?aEC}_2LcKquM{#RS2g8eWjW|it=@;km_e9+05tI#UZrlI)&0;`a8oE`k63K zEftt(IEQ2QYdN=?kLjI_IM30?pr7e3*uuPwvn}q>L&m z@pWw*syoFL@soE>$3$mgHVq2ky-*d<&zz?*dPS#mH*(HW%+Ge25gXqAt^?cshPaKa=n0ZKHZp1JuNuclG7-?XYKjC2C!B#$L=XgO(*gs*iLS2@32!QgReY z=cY06w*>H{z58gr{I}bYb$0;m{MonKKiQWL{f%?v-_;2nGMRaU+?|uF;j=ml+*LeH zpMQ3QhGKpzf~=ikQ~gYj@~;2X%=I%*ixo%WT6O^q5*9b4z2VweM9>?lUReRM&+X=P z(KRpGM3z7QD6T=^5H?6KKvflzzw_WYj2*<=>~+>s1D^l&UjN-MmyEB)V{>9W<#)V) z<0D(`cqm7yGSK$gw#<rcL;YEe&7$4m}Wwy0ms?bm0mQ;>~m-^Su5 zasL8@4pB5(=l|_Yl1ZN)+f2qDllmYg&=G#KSyC0?kD$Ub{sCX6|%Jx-T^xwH(=^_4d*A5K~`q3Y% zS@;u91E<(IYGBB0T%Dvr(}u2kZ0N276SD7ZztouyEf_3N2AWvi8lkpJF_M7^z~^fhj(++AO#YuMGCcigbHcq<>( zj*{wS9!b@I#~>6~gL)J5ZYn^_3Q8e3O6rH4&nU7ItL~yoRJX9u~qvfQ&pSWqK58j@5*m6^0gJ_m%dW7#xIViQ&F7@)BcBn$^i^v)S5Us8#kJC zckyWf7mmnM-kj%+Oo^$Zdz&XUzow}bTCgevA zmYhot>IWA6L>nZHsitBA5(J@FA8d+2-q&e%u` z*FlzF&4oq5i(%`V?ZC6@_lu+r4Hk0mg5I4?L-TXQl56;clWV;FordDfEvVx9m!VQ) zze(ISYN|vcj8B_8RZZ6m*8ZMo|D*}(KCI`ph05hS*l_CmcBNfLxvOg9P+f<$oB7tZ zx(;V;MHnG5|xaqyFtUW?D}KObCY z+#D86Kbi^H|85!hQnYGGvn0P^`cdYk>&$VakYY!^7_D#XyD^LjW>K(jAZ8tN#2m=N ztT*Hjm`Ank}97~bylB|Gq#`nv9YS&*SjK`x~zK5FGFW5nX5Pf!C+_VfEN4>qLy zdtAeg^;Eg$+)f7bjtS>?%^k+CSYY!fmybTouDcN8dTftBya-plK+LXREaLjFf^QFb z8jOFp*Yqnb#>MQlD>{ZQ60ue3{1HIZ(s3Vav*u3^^~1X-MXYMHaG5M6I@peD&6dKh z7OTYxeAbLIxocLp2<9zXt4i)R+aH#@PY-p+Joz1DL~0DXquVpb;O)NGQ5w+e?r7^Z zKp+v6DMPjJq$E;)O|zxr3mI~a?m0>HK98%N97o-QR2B!_dGFRgIVHZ&Jjs2i(79%y zaVWo&@BO5B2tNs?u9GBajA*|z@u&_kW+c9R`6sE9kz&{gHIpvZwiNbI zY5%w&1JadaxpCC>lIm$&EDKWZihA$27g#L-OK_B$6)C6tTp(*akCS7x*KMevje2y< zGkdINLzVk2mISa&>Ga5|Mg{ctwlvOAZ>0?$umGWipCaUT~Dfv~i8E=@$zXG8t)5Gi^oe~0{J>{r=Vg)*WXAP_o zu|N9G7dUeD3okYi5hxFEUR<1?FFEm#ed#7>hzMi^sN7z^mCoGOH0>;)lFKiv_*?&8 zK*qaVtmx$JOkdDiRV$5ZEsOly;zdt|l51Mmy;awEdbO^c&Y;Ct3yOnRKd6Pe8e~Pg z7_ZFAy2Q~aZ_`39$vn@3mW-~PM=K672aOzTT?zpsOpx8 zCU!jXbQKg}9<^eMi5f2-37GZF6t)f(`NSttajlz2$aA(n&qrB4Wqm6PAMQhVQ6qwq z@2^EsHlz@9e4hKOBne(*7zWiv5hef(0EV?IcskP)J;*9^q@|yK7nnBV(%+#0*XDIp zMKeyphtJ^!PiL-n&gSG`vs4g+UjwgBvMbo^P%$xgK09M-C1xc6_-5cPBP8F{a_k>a zY_miFsH$Dtd%**~_W1X&1F=?vm_eWSdHjbPaP*lL5ZZ3Db-j8}&jk5~Iq&;`AZhN% z*b;(h2z13}utrN~VKjd|UkPBHbU|K8fid%~(x)qwscYc{U8T1MhUDNKc5va%Q1wRf zh&WP`99fa`bexN9h+6r9IWH}E?@xPXiwtvsm-7W47+(0LE3*CIrmYW?hOiE!H`}7> z%VG~Ov*fW**!znyh_zkBnGLwd@Aua(PZ_KTIibr5ka+tr;bG}G3}%DZc|Q?iLV*x79v)jJ$5rOuhzm|wlhFI=@ErZpK>xLx?hvw zBj-DkJX$@aTT(>3;-%5x(jnG=#3BD&qD#I?^!STwmITr^L(!1;*Rs&?Tbf}{Zm8?3 zxBo>O3Dg=7L7d)lRWdTi9-%0QtPs}jvM6R$l$*P2T>HAqhwzd?q<178{ZLG-EgqI| zM!R^Tzt!vdtUqT&*qmN$WmMc^G$+sjHVae?^1qI~b9sZQZ>9`Nbf#}hXMDCYAKWtV z+Qjq%Bo-$E?wb^OOpTsNSV;=MwQemtEt;o}k@n+4kl%%L_)kd-qY{5=XDBz+ExrD`gd6+OFK zG{k4(%r#BJ)i~PgP)M6!_$Y;Bw6rJLn|&S39woOEklQKFWiRBFs@tRrLmmL~VJFRv zvRu#Rme*?#G+a{2U%kK=dq9xYjaB(`ukcY~!dejGN{wK+mIcKpq_a){kbE0~Iie)4 z@^(V=>WuQH8WXin-?F9u?EOa*`=A-C+p^J4CDcVb=c?G%`QdiNCE)=KK>ae4;l1We z@?uX9?((`!6)h3^SRAp^s-SpJ@=cwAo)9$@@9sNeN2h~{ToyS19C!(#Hig0DbHpTp zvatv0aQ-~=rO&hhKru#~%f`UuR0F$XmTZj5GmXrN3xq!4Jy^Blw*HG2yu~R3U$!NG zvSm{^diHB_@ZSm#=JIzo0Yr$OfWkX5+W>*`>#%?=fYm*wPC3k85D^?0tXYl22?uIp zT!wAet{^Mb7@8D^n3iuyQQl~HB;eIYnM=6<+2L5j%hX)U@*(^hH3oA6RBII_W62hi zN}@W*g8J!!4f$6d8MY6K>IkZeH+2WOwT4RHt@gjVIN>ok19deYuf98&8$ujAzzt4= zis-srQztQamb&-Z<@SnzX`#~T^5?8_SH*@D$hwd~SnEI)0@EXOYctB%3zdk1g1jks0Z7>SwP zQAI8|fM=@TSr+NLDY|(8F1L?9N$-D~KvcWIPa@uhs5{LHBCj;0mncbGK+uQVMd#Bc zH^gx^yj#SP%r*LuTC!5D^Ew(pI%g)>#ekd`SXccp)85}}HNRhFRptU%5E(bZ2COfB zmX?fJ=?2BDQzQG%;O+9V|50yiz#64S7V1H#syQ@>TQk56#57ai4F657T{5uZin9bL z8k!?@b=^}u&kJJU#e|yO1~^teg9R5T|KBePvTtFHO&W0dFQy--`)x)=WWrbEfj^{e z{?UP*gjPm=EA`q4H6C2{R$cXw|gm#tOM&Ep4Vq6tt%*-vx32u4>j-YW-Fa8wfZsIjLVyxkwJI7AoDP>J!Mu#Sk3XH>;X#Ip!99m=pWwk} z)K69NEwUd7x>kvs-vT^BzMo%SY|(uLhqdo;Jt{OxL0Olr>j z7Jw8F z5<5QraipD~8$5*nXzeOALds8uI`96H%vU`((3J#h$y+@Z4qjpEuPVpJi|DVOgQA!S zUd)(jfmeycE9F)#HIrZIuU07@KaFb(YaBHrj)~y>-JUlCu?G%Wmgta$TBT(D>jzCL z%;tRyxo)jwy`q+@h}-2h>ceR_`_~but`IW3@4Y_AFbnv0gcWnHjOQ1jBUCQ=#x1U` zm2enN(497(Z2(94y9nCVn~fedH~IJ#QvgJ%P=TL0CsCat4;R&%PM7mFg<>!e4tx@? zOP05Ym2Sg&qg%HvEXS|T@QM1(zk<_Q%RW2%ov@Y5*>6+&KU=+pBTO%UX!Vqj2qCJ^ z;Fgf@xjryxMk6k=$m$!{gP3ug^b!SfY%P#;&W;J$_XxMlyNGg+bucD^?EIKNEx*qA z!iZkE`14Lv6KD=%Xv2aGpcY*KIDtjLD{Sb6caNo3({a?w3Bl_gr|C@63TtwNQ}DIT zmBxQ58qerzQqQ@_&^%9vG~?LtOJxl78jcQ_N)-53qWb|1hzG4*pOX$<{9~qp!{y&* zI@W)sMz0+X9+!&FvWz?^gAbc8g-{9wk<6)s9?HFML5F4EpNS(74sPh(+j6!Zzwl7vX70XZ(#atZ4I z5rnT>zTCp_1&i*SX5oOSn=?I%bI!RllD0Pd_F(#%m(-eJM7vCNTGgQGO|+*74fs9x zIc}=D3{kxcZ+ZIn+l5ve2c*|r&`!B9X@n(rAUQ6)X>!&@Pelg0q>^Cl}%@SS2QdJPb$oLeMn2M z>LiPJHIQ_lz-gjo20)I@1Pc=Zo#e`h2I?PNy1I*aK&eKaFJm%aQC~jw*gID zWLBZH^-glDHh21~0L66Kr=mh;;D|olz30QD4#Av^U{~(H>?J;8rUScpm=#}BUW!_{VZ0Rf*~ePs>lmZO6N^pYk%tz_UI9R-|OBpNm69!F%v}n+mIzZ z{^$B7%V4lE0CJVb#89fssFL_fF){EDacTUPyRNO_RJTw11BRhKAV;yQt*nTo9ta4# z9bG)V6AG%269t2J1ct0Qw;4c*F&dhy{c(bo4`Bhq@qYw0KY$0ix4)2*FGWh%Q6Rgc z1QNgzzUX+;&?%K zgpfN~#GetT$@21(=Yoa)6UU%CvyAE|>R@>8z#kym(N zg}XuG7*=t+7sx+B=RadHxWGFu;K@6A6$|5ZM0E{3N!PUZ=#L;nD_s73`<&OJ1*V9P z!VJNMiM{807S(Z}klPz7K4}jyfJehtPThb6IaPS1lA&WQ#jOetmaGXMTOx+m0g)L3 zMzAQ-U(wQSi9>Awog3VV(!%mL4Wuf9Z*x=tOql z38$3Lc7&dAuSCJcJwn1hR&BCR4j3eFEbi(Dn1hBn5z&vI_gIZ=-+^lt%o`bmx)|r| z&rkj_-ieocHk7yjD+{wK@5}U5g9p6&#Kvvo6N2=SLkLvJ4*=nDhzLW_?v*>e_CVDZ z`|Hbbz-ioEhm53&p4^{_8McEpGNca&qUi0l2iMx6tQWP{xiPX256+0oXda)n9(%wC zZ-?dv1BHUH=V*~5(i=VswzoVGEHKrHYkRVEN90O7K58Akhr?S~kQ8c-kMPY8$)8*e zRdO?fXLLR~6AuPF7wCp_S0TCUGGpIUP>&wJ)MS~ymDph*wVir-2dT?J$9{-gUVT=z z7=Fb4&PKOVhlf1GTxHuH@7Nif8EEe1Y^sVl#s|OiHIiUf6GBH3cG9T7AIOHw|JxI8 zpg5X99J|4J3LcMLkryTQvf=H1Q_cuT*x$XZW4r~p|6cM<-su5O}* zTW&NZ4(2p7HaFuTAIrR@EN604W#%tiDk-Ug1H1WZm$l(NCXu~$82=s#Z zBghy=s~*u$ep7wnPS~7};oy<)x7>f>qFcNm8Ji{wE~NJ<3%#X_k@n<5a5*xRQ}X4u za3fA!&51R|7#iT>RJ;#(&61SOa_Gp&%r9xsKyo|^aH09x(x<^NNvyPoRD$ucAbm4T z(A#yRd({B?#1&csNGioWdYAeg7cuf|M0YwfLQAs|&}=)FIeT$HD|`={@cYhRV(!>bk3V$t zqDhH)N#EqHj*u|e6+ProFs>6Df%#c6==RlWG;8^yO#mCh$#p-4GE6m3w`NiW5*u;G z10ev}D4N8owdP_`A!>Un;@kL)!=#NdA^&n;En(jYqBcw5YP_H9L1=TULS3JYLLUp2 z=a!OI*0E9hJL|Um{+Q5Be8ie^jwbiOoIO`Wyw!2PmKR1dlooke7zk5wWe$@Tq(in8 zh9t?Y5zl=^M=6~6Ip9OhwaW1j-oARQyP|CrP)`7&sStRYH11Q3Gu#iyJ#bCZU!I|lGi8V?L|Tg+w+bY{vW}A6i=`Ob;^mmw{?$2x(?qV&;zNvYbs3**kqg4$ zj$tM>+CdIvSQVVC{wpsY=X4w%>cs9TrXK##^<%N|+MicnhP>zbPzN%DR4 z@>bgbfDqL8x*I!j0wMI;arwinQtXcheE{l!DsiPHZkTOt(A!Oq`7IhU`KOVbi%kGk z`So9%qG|vt^FgMO{!>mH1`;JB?p}Vtzdh*M{c7cdM@&IzDu`%8pa7IM&gM;p93W4I zP1+JEyCq^l9d3I&4!mfrOyMngw@A%^U!PZ20H};=0shhhvrqM*rfr8;k%`=STFUfF zrm_#h1%!ZG2S!LB3u1xF7Rk=2o>&2@zCXo;BL6c+E?|Mn@-HWl|1l%Ois0?a4Sr{aUFUi6637~mJ?e#1|9k+k}jQ$n$Zi0Vf8<4#;(sTz&}0@GVD@pk3Ym0N8I z13K!vmEQf!#NUd@tJmPZzf8YoXaa4ldi5o7JjEvg+8#T(^0Qn*K{$fdV{JvvK43eh z+cHKmV7ZlAQF*Iq%NI`No^Pc$(*pbYY}}R?3ZE@oQO=Fi0{Yhl_-@r8w`x{*IuNB~ zHPu6*gS!XPN8#4t1thn9DA$6XlGI;~&bxQzZ+xJdSpk$Go29o)kz&RR!VM?eq1|n?ks;;Pw z2x`rRIKAq;7b{+4wEYO?o64{%n?L%wNQtQB2$s!fY%KwvU|g0A5YUm#jR*#jz1jR6 z6yo$uxRqORzzB=bLvNQ4Jz#Ic8a1D{bT?H1c4Ak(CnJsItYGRm<%j*khTe8ZAUnHK zanMk4RXyb{CNlrZfz3YG#TFO4e4pZ?0$3gE3!Yo~sIlM9-;^8;=4#Sx7~1nHcI^({W)``5*rqwLq)=x-K#cXuz^!aOwfuiroY*1BlRuYIk5_!+Y) z`6vo;%mKGtdX!XGmiXgFh8mC{3Svrh;m`jC2Og~iv&o&XyAi+H+HzWwFw>DCLMAV8 z6c!1o>I2B(+dcV{0p}%%h(pkj?>D=%@03taOaxzGu)4H*U0iK2cw@uh7R~lnU%M6) z%6n_|ad68SS&9JO4iQS9c`-uFaZz9OuD1?+Rk>D;4&vz+ zwJ1`oL08dd!*IT5esB&HhXl$a^xRE*k{{)%n6a;EkJ2HU;&gcp$>&TtRc2 z=@rDEM^8KGkdoY~~wodS;Mj^fO0;3j=YdBp~zxp5Q^GuT%H{ z%#G4V`e3}M`6oA~&=Flr=5=~AA|%O(OkXE>3YARHsQw1|uZOLi$}WHg0kb->Zf=x- z904kMJlNnb3c{{$*(ASw$ z%c?JdM9L>+Swti47&?CmU91f4#hHMH{K?M(HB3m@bN8!{ju zuiUVJN?bub>yh>5K=H+yWeik$`N`!4HAgeao2Sf4&ny%-GVB2^PcJ8nU!T*oef>go zs$5Sz^*6eh`>vWnclZDppdMZs)ZLZOxL*VmUOFjrrLeblChm~?KhwFR_n(gJBAkSG<0iViFzd^Fz2PEa?6FJ7M000b}}i)_Mvt3&y8= z%v?;$37NL=&#SBd4Rfz9{jwKjV$wb7D18uyL@ELaX}0FV@^g+195V4bUP}g?RjBLn zFy19CgLXD?I&zJLg&|RHiokARrQ~Z^GW#=?`XKX}pRyof#qJy9&XlCj?c(^$i%V3+ zzj3FX)oUCE@&r~}S!^~zn#Un)O|`#XSkc%(b(&=!yC;gml%o{~s!EUXkfpB1lr5Q~ zH5)YMkx;4j{&t+g8~XSErN*tv(_imz3hum=irx<1{3ZFIDgLxH^0T+6RiOK?YO9EW z%JS1>0J}`k(%`lqPXvHkxK~|r#CvIFFBT26+qg;SLwqvS2QFwHt%vKw)_61OZk|Lh zh><8}32bT@$l*r0UI&O)4*eF?_$rOG@LPAo<#?=~c$ILY>G#7?{^)5h z`mCXz4jA!^q+1p{+~vXjQP^psp4ii>#IjU>0b|l$y-ezkd_uZ)U}}W$QMAJ%UdIcX zy^$jPq+74FaLi!dNgCt6N3x_!Z>$V2p6&cMNk_dsF#SRp3D28V&@nKy)A?N!r{ z&3)QDWz$OF@005cdY?}_-5(<3#W1gOMWjO~hW{smQ5%ia^QgTMGJ>W2{m449X_S93JBiy#mH!Y@Xc?j|-`)A({`k8T`_$>TU{y7}m}4wI zrt=MnlLGUA>~ifuFo9Zur)R!b$C!DlG*$z9A~NDMph0w%tI0Z#jA{Urqj+J0kER$AU2s29T_6Aq6k;XYcjU&J$$ zJ94(~d1==~{dvUFL)?WXbfCs8iK~7fjV<}e!`2i34q`H5BOn|>xxQz(?02l zUSdm=bx^Ze?KZV5^fAk3=IGug5jL-%RO_7=u?0xDbfm{Ev6jpk`D5dgRu_?xXoNkOM|)Z~bn>_tYeV@)1Kq-MmsEHSYeyf81R_ES&sf;X}B3`!P+;hn~KO^rhl~0i(LJ?Z+W-P zMvv*hP{1ZipYQlaG8aJTZDSTk#P)7+<@(1T_k>qFzj;Ou?*|_Kgpui{TDQ@6R7>42 z=<9VHkGzxcqK?n^BP6$eLt1i2%{E118?E3~fc?BgW2Zl~C6vo@H&i&9(Y$s8G&G}V z47!olm?=S=gvYj`f;-TqqMxH4;*OH6qXk~kr-E;iX7t~aHzwzGozZ`_5oJ`r`-C$5 zpD@@jF27^G>@o3(BmQ0TYFJ+UO3h0!FTaj{va@{hVdQrQm7r<1(CZ_Par%Z_>Duaj zIQPH|O~DAdh&Y^DY-nb=wddDOKywveCl7M?niBObw_RX(E|TaXHClE zUNzQPjBzU-4Q{59MsEo-N21RV@;a7^cGxaO?szK1=+LMR@f$`THfZHU@Fz4lkNCC-IepSRjW8n;mxmp4)7g3mlf9Z-o{xGwdz*EKEwc++A@#w9 zpOs$@KsMjA0Z$sbemLX$1PY3Vg@F?6eIMex>X|$(@ggRu8+zeR!})?_VQdxjS=-JL zhim(U+(~RgAH5}$Frs}$%#l!|q~ZErM*XW0&kQ`}QX1aA0>?=cTXo5pLP?4EGs`1R zn<;DnkEH3;a8Ir`=@6D@R>n?Mp2gm6)^f_Eu7MA6o2MF;xSe1cr*a=!cLMP+oR~1L z7k)zsTt)NOZh?huWh418zlx7~?raMekdgO`c?ptROTY!lH^qgtQ;DtX+AZmO&;Pb> z@kbO2IaBVe`|fn$#5s_x;ZWKCxx_))gx7H@k6H5&jqAXj60Su1m?OHx9`#dy>^Ru? zB{@;~d9W69?vDQh6D3U$A+ufpBE}@7fWt*DUnFtx>}?+`(>4ayZ$AC9oSxc@#ad)5 z0^*tBa=s2v##krUi1I_@P*%1r#E{Gw7d>OUQ5N0y_6?ia9wv7xwr|h`=Kkl1*RoW& z$l4t!k*~-)0UPIfbWwOc9Tgb2VIIB&d$~i5(^m6tY%;|hd(1QK#VgI%Y2Tz2W#d!z zrn|^UDehzceZ{ur@Z_}m_=-Hw)9VUm%J!Q;bC&#D3o{FcICF3SI^O4Z%fzdm^>-M5 zGx9Y0RPdXWJ^RU4CFc%KNwVL z34&)KujA7P(XhN&q{O#PDG7513$Y0}NmMkQQb}^)twCVX)MC~#MzT~2;~dF4?7u8+ zc6fJqKrI7Zj%ZGsPnrtplC{Lxg>f$a*@BmaOgYVSI%(Oluwv_ z6{|oIk#&Y4O_Ky4a`nT$zHki}V z09LMF0y8Ctgfhl8%pcXJ26v=_D1VeGiSqP6~ z@x46gg*EbzVuk%6$TL$qhQ;kGV`(Iaryd;iQ*hXsvu40VnN3z;>U?MPVNsfUwY@=D zv#fEN-S-5kmb@g|V-{&72#_^9)!Wb(7?SfP##r{3dlO9#s;s(Fv>Or)PWP8!BF$a0 zrfBN}b`PDpLku999~|p=cNU{>P>VCNG!;Kw4_pu1<&Cq1WwVZ|jupZFO}DUp)?DqvGVlSuxXpeF^^E!ag#1P}qK7E82QQfLIuESW}fwS%O7N%ETj`Q`Qi!Ac2E zQ}8d6lPK4V2vAHg?GkwvJcVFe1q&i+RRTa3WP@N$BrYxRR=FcY!oHI7A(^X^Z6eoZ z2-aJYwZ)EisVnqr7d!AhvdFWnw6kb1RW{J zHvtVwBDY*}A^2%O+)MyKF8EX+1d?qe$(EAjBIsyAe+zg*pjVPGFNhO?P{>s>0{Id= zuVjHp%9tOS+;JoLAA!Y52CrQFAnBQc;gt(N z1jix?Ap(w;EKbRqmSkChNeOgXfZ77&mxOIe3h|@gTvW1LULepgxgtSe&jMK!~_+H7D5yY+BTqGE8Nv)PN7`Y@v(qjdrD*42M;gc(MB#Fm&MT(@2OEQN6 z_(*22WD!Z0v)pYW*?MwIhg`BEDVlNtf!q!yNy3sRExDGGoFMtN&c+l07`0tWReNa^&j!$ z*8i*@&=L+cg%bl(vr~WnKK^|DPyVn&{m=Tkn{WD8{m*~KPls~8Tw&RiQgoUV;jq-K zHH8zM8=U(8=`sK7=kNE^VgLU_$^ieR{eN@)%^mlDecjFf+W-HIpZ^W@f77q07h-JAFn9tVBcM^BYdS}$8$~71}^K_44BJUYUEfxBdU=j`i!eK_dP!I zz&ARX%=hn9Jl>2P<9E{utQ_y+Mg|1r{so9Ozn=RS@dJPTkGKE-AqT(t`Sbh#h8u3Y zaZtzl--B+d|JVNiXZ-xd!``i2f9nz$l-F$&a6mN*fAxV-O3f-K zy8O4KV=CzKhxLZC?q%nWd(Am7HFuCHW$ZM}$zJ0FPovb&5Ky10uM@hx`J-KSjm z)^Y#P8k*xYFw^r)O;KC=o+)JOcqMEdQ@qyWlt{9pWOlEycg(f*4ws~L%(eAS=E8c9 zt7M*D6-IY6Ntr_Wo_y5t%Vr6(#Wyiitm)cRiFlHvX`bivbeIBGDC9@EVoW0alF)Hb=*p<-X`~-Qf-O2skrKRwN|e^O0Sx#oJ^6Vj3pl9 z-Ib*8adlKCk+)VtU&Dyr+Qk!&sebB6r9OEnQ?ZMOlPOS2D@MnQzn-a+DLcw_+^Fg1 zNGe9X@D6q|^+g?1S(KY~tBE>^NRma4SyWRm^xBdslH@V+#$+P=31;%u`6S{pevE(DJpXs=jGaXYi zlNl$TNcq+&Q?AbO%3~7Mr6i`qC2O*GK||Pai(YonL`2AP1xv>(W$BnAmQJQloH?R1 zwc#976;33VSDMl>MJZYGJ8oINcTnBPJ`()bUh6-{mR`>^`e;1KL-5K%y03esARVs= zq+`lIQpdiMkB*r?A8662OGGDA&M`Adw6Ok<`}?okUr5pbfG#*+#sTC@Jhh5Gm-DbDBc zxqVIkY36mFLG;Wjw#VmvO4eJ-={Z@2^qfoyJ*R~T6!+BSKV>&$G=8`09lc`Ywi9wp zMK#B(rIw}j|7DFV8aMmE35Ob6{*1}MC!Gw)S6?pkN3W(FzwI7gM18k*oH_TUUK|~Y zrbgWV%`Et!c6?RFoJ@VOKdpwjHQXFu^)_CnOvN_Gt<^?@BIaS)m2{XmA>Rz8_8K+n zGi0u<-O~)EmYQqzx%JcBiG@;sY92Jnzrpx)ZJCUT{LWFiWv3ip!6}}q(eB;yQrW`P z2z;+PnUhtQjAzNZHt#^ktvu#`HG{os`uj@>;E_X znq`EkVQ{u`=qRFg*?vid6V2g7*p(6Pai0h$+v2fgg!U;LxO678(_3cyWUpz`$El4@ zeO+C>aS3H?j?Gw*ss(s>B?{zE3zcQS##%8)x)<$2ce`_mX zz61x{TnY@inGdGcrFGOBf>hbH&SsIxx*%iq*d6R;#Z1mVKgGZYIv>JmAy*$^bK z)vI0_*dcQb?D1735A5e+?TKk&7k8a&w00LyoofOYE-E^!1gLF!)j{nVQ|R7y0PSQr z8g-$?Ic`|Pg$^@u?O`5q?U7l;wZ{l??J3f33m0AY+|(|_HptFu7YmzfA_gwT)x5OM zGYJZN^;IQskxqW*)Vc0c!DN3r^8%x zOEPoK?JaDPTjz}G3j_^wQ=8nr2fDtqVdAZ*bJu&5$=%|cOzsYuOzzX(WDd$#k7O-w?g?4k+zW;ubFa|fn16D$`CDF| zM~%D^|8VlEO%Hi}0P`ixg*s2FujdVyS4RUD`T}35^JqJyNO{qDBnmP?Fw}VokBeyK z@|JibS!HVB=dJ0;6?yA@$70@YZ#D7`2v_7C1`Ia3g{AXOcr?j7=ObU zOh3P@12g2;WSAj;fG|V;5U>0Ekv?X~_t-yw5|1R2pf;zvM@&8m0tv*S&Ts2D!~8|Q z8RkFQq0)c;rVf?<^Y?na9Rc^P(roO=ZYfOboWrdUvsRhqe= z)+1cOfGlPz7$nS8FdDER6XHW%Fkbk!pv8Zn6(llRLcv1fEjQwVy5K?SXANMGgMFwA zHW-d8*p?*|3wH7;O1~YD_YV8sJMO)A)?3ShR{@LsVMEjfmkh-UOT0!3E3+n9SSyn( z9N=|XIM{2m&?`P)I2tg>VMf%2V*v{?!A8`Dlgwr>Y$M_51|Crt&ZLvtd}Xm$EW2=p zDUn^c8qjWMy{&MqH_lCfxh6P?x^S!4?p`|C-9RPk!hL{wUbqr@d7Yli?&?BDjVb|G5|tDI204U_x}*#+*93A=m(=<< zZ%KdQosyy6=t@RqldxoL#+WK;0L*d2fT&AiMy8j{V-48^eo>b!B!QfAg;>eMW(7** zyerw%(RM1?M(ibFVbmqW30`m*b&2TwC2wTWuH>rFuC&O|uC&_SE*v^WU0RzVQ0V~K zA*Dn7lqelZ3l>9NI>DSzrIXC;N~e15HUkDZ)Qq|`>akPlZ2x{LU694WrOSz4Mj)21 z^Y67%Z?BiqUN=E$)TR3X^Cbp|y7XvP^XCBzGO;DprDF7z(aDwxS)(qiF}td4fM=kT z4FoLm2eDC?4FxRi5XwefcBdI!*-XG(6V^suHqSdz%a*yER<@EBP6FMi%T}?4!r^Yz zWm{x;WYV~yZ`5V`0PRF1IRkas5kuy(lV)3%i6&ci9x&Gg#!;8OVdhhAd0!~c%VLJ| zGGT`DUS`qC`?IZ=YC&CI?;VciLxl~>M|KcKGsgPui zx}q8|UxN0iEBg5MY{ihwW~dk~L#mk2feIBiq>P{C)vv9!( zs4Feft|c0cy0XGdp|USg+8>!lT{(ah1UEj7y7G3ja+Q<3jw`2$@XXpb>+iebL)kDDmVFAw{p8Twx>N3Rvz%I+seb<>tf4Qp336z z%5%cul~=PDw#xPk!>XDNJX+P)Kkuqx9>uH10@~3yNwBIrP4`t(0rNcQ1nR10>9lG# z9X$p{fx2oT^NtIqKwY(x1fmP6KwY)LOtoqUV4)9Qfx2q9_t6oL>{Z9T9Z+@JGb*dj z)9LE+cGVkfIeQQc)K%I)q3TN0Vs&p~JO*W;uI?wxP(8Q$2*hzIKGeQdc*Z>ODH zeGD*P;2!w@u=h6LRb5s7@Lo5`%_kuQNJ0q7Aqf-$gd~IzAbbUFYm595kusD)ZjzfO zG~}lDCQxccL~LoriWO-^WRO9uh!l~jNEt*#rc$g(DaDqTsgzPmDPpBqad+o^*4lfo zbIygTGw=WJ^FGgi`aI5%$J%?ZkKbB9m2;lekDTkiJ(im&0AiqWbK-V#ZocVO zZiN+}+!{LHv>Bc|Q9zmt4*``sO+Z3hXbGs?25p{cs0gUsg#r>|U5zcEa+k$TT<$9T zTVxS7uu(t{AA|%{E^hWF?8PBFcezJ!?%LWS_@(3Aw==HqklHX z2uL|*#AAR39j{btj(LbBE zPz2|te*vp){f7uh42N)^_OIej^sh55_rKM)+<%(CJDYe*5O{%3g?_Q#{$g%^S9FO4}@kd14h zp+=wz^7L914Dqf-!EoPN6x8uF6j*JwV2Pr=IZy zY*nFkKNcS2hEDqCr|=BVPvI3jwYqdjtnj*kbPqNOR8fju!Xn4xH7^=MD{r2u;q6lN z5%0B9G~Lx!islPQWxyAxqJ;u__+Ve4ik8{6Dq3%4sA#jj+lsasi=${4sZs6spzc+) zM?i{&hJh+NY?Zd6Q)cms*auN`-V?qky6iQ+ilmt{C&PZ($u{!h498RP2IVI#+!GYUjFZ>J7}W zUmlp}9sWSaMDuL)cVGoi)WBOkVl{9^yjTsKZ^dfhVvkr2T;>t0fva^mk<5>Q>&c=p zfH+VCpW;64^EPnE6M`IgoQFB^H5)n{c+qe34}8}wd~wj4I>m0tvAB|uarzTH^7kauJ8gGM@vAF}F6@oM~QXlV>o@mj0h6>sz# zz{OkX46-miP{n(V@>zULyFV;s4^;6nU_m4&7&oKtTtYfZ}(_P zvG3R}DbwCN0{Vd}sk418nQkVkWHzm#f&PIina8?9Nr(O71I9=%SuP;m0|^9G@{pC8 zl8tNulx)?$dHmu|+u6N#8A=ZD2upklZ^>~Z_9bWiCv3?D0niAlL<&f(s;H;}RVr|t z23iQJG)(~D|1UsUh{SNSl{$8{N{4%nn9^!j2WbLTS|8_&FP&w2QM%Bh`jvKg52n&( zX7)-SW96ZAtzCoCjd53E=~fOAmWZ@Y0h4GQ8R{sM7QN<{;sHb5NR<S{^v?^rt** zx??5|8s;Ho`}Xg8hk#265~%@ju_mVP)}@U;xbpf}5jEd1YG-vXm66d6?3`3Ef<32a-_Q0C$S*`m&=vE&iwIv9^bX;Uji;iO&nIh z1)hmr3Al8wJpJCIb-=}6nbn?)XzHSaL~;ULw2z4EfVci*;aPeiP>DPsTiBbZ%WR;gjJ9k_4H-O8dFqL*OYoN)bJ^uPzftEFbveE1;5I6U69&KVBeAke`^-$1^{5-N|%Zw;-rshLB^+ z$G_uY`Tpfs7rqKykBbPffXnt05tWC)_>F)69Pvl$!lc9(2!^PE*gpcE8sfn$P{ABQ zq%i?awPEY*$*-rri-Km#Rx@3#Vc7pmQ7*A=>vF!tw*y>dk&1$Nd8jSXu3?USzE0A3 zPT)+rbfJbue^Je(QywDO14BiB4~4HGgAE16gswm<3x`DkcU&56$}<-~IeP z;F9k|tpr?j&3?D{0C}TSN*lIRule>$=`V zKng@5L&S2xWwhm=zjFOf54G~?=ib5zF%|rXDhDM9pmO&h4;`|l;y0Yk-~8n3^MR{1 z5_2Ok69MrL0vC;EVtvhSKy-w_b>oTe54eIl(~9fb)I=Z!Tz*rYS=EFRnRFOb(osRQ zr(k$$WMc2z@MJU0AH+rpOvyleiXIZjE((5zQ=Uo*-hR1Fi;5 ztZg2mc}s!)0-lRPelO76;h{S2Us|;);4NRRt0~Ka$DhZe)9^eH)uo48I-D>Jbxu=Q zoyg~a3osK?7jU&vnfLn6BK+|H3VT0F}B@q+@aZC{xEf`84rhV5%+~xjR zt$6Sn$`|5K1tt<9b~F!B^!l?u`8aUJeIh#qt~gHgyI|Olm=9VLe-Og|UwQAM?C%5DA|+N|VAdm| zhXtlCAp$+%63E0#hMPPQl~X~qm%#P!iHk4bEk~`(EfbY57}h7^Ck8|MB(vYV7mruN z{X)Z7^4Dp@f2; zqAkE&RRyi-K+hY!CGDoF`qDh8&aA?jXW5)`7I1?HxGcR~^vGQqz{RSG)e?U^i-xZt z3VASmd~)eKmwy7xm`1e2z?>k&=?YxopIABro;vNO+&9sB0+$LWPE7pq8mJ^GHU+eD<@z*I&=tqn{LMC94P)IP+Z4b1B!r<{2G<3MDd z^5{1nyaR}@lSqq!2|9>c7?|>c$b*3?EM#fxg;~Hw$BD!gf4q+7m>}|MU=|u;TLxw+ zkzbIHy$FUZl6cjDiLPWv^UF!VJX1uO4a|>2Aa>Lfq$=a1r&8gfZ_?qdQKRzn0S6L-$J28K727>9xR z9^~$Up|6+o$Ugpe5xebVDmAJuy344ffADDVgs^>FH z@V$eSD<|JCUGXO%rd2uPu|MG{WSH-WJ3HVdQPQL{#C#3R@IvIyz%(C3qYO-DLG;ML z#1q8S3tY*cXxams&DD}V5zjj?sT48m;u}{eL611>ff>DsoE?~{Nlthwmx2tsoWESz zrw8`b&@;*{y=El^G)wYh}hwQd4q_k9GHkj9(-{luB>6mFy*bM14{EN zKX^aj~ z%os#$>Ay{|6$3@}-!{PK5i{|GP*nhXR#^Lb$ASlK=8CDiu~GZGQwt{lI+N@*|%dSrE|t@|I*u&UzKl3N`dt@{yP8aZ4LQKv|UfJdVK-%gWku zcj4?9YC<{ZnR$5n83tkbA1yJw#SFQs{PoX&KMj~HRbH#DJOxZ_DzAL4`s={lk#fiW zw^73}1VdC~KcK7X&w{x^Mu)9$0mFnYcP;uZp$jx~qlD9`i-76Qz$vb*o~%Yl0Rt=;2B`3mxzlXArKUh34Jv2zx>oMmc1B&IwCGN++nh7)FbZz zGhoW|56r&=%$X?1PX8tCdCh?+zy53v-mZpdNf!LNs|$ZTLj@jW*8}S(fuWU`U;h5X zWNK;FaG6zm7g~>oZ&+5}vmdV{L*FR>FhVQOnxIjBC)V>NU}i75ID0vn6Pmh7etCTW zZI7f=_+=rdU4HI77)E{h$6=vg1M_3c3tyOv(i+SL70eUz&bxl~EueY7eJlcjv|S1-`nt%)1uioJJ4 z6QPrsg+~FppMvfkLe4xe{&xV~`=I-v zkPj3+jD0sem-5X(0$%opS5LODzdi?;xLWo(I}nX6Lu)FZo;H^vA)263?%WNZ255qX zLe6-06Ggu@JHLFb`y9ccEa9)=Xp<|F%Ps=5>&o4)eqj$V38SoE@kDt7bnm9}TnZ@M zK2B7T|Rl>BEe}i4YoZ0+dveUb6S4?OPyDN>6~Sx_BSX8 zh7ndKes^CCm~2&6<`w)1pnD^9ZxOO}(h(f9;ffnl_k&%QC{bB4lZO55>U0di2tj{SB^ zO2A8os0r`nSEkmW{$RKW<-*!AXh|EIe)+A-!v+J>KFa4FeE^k0L%1wgy!6FafoYdz z&s;^GI?bLem#-AK9}O+CyzS@L@khgLDzn?JQfOGy|H-3o%()Xsz8(~8a``~zAHRDZ z=hAR<%eS6fL3%<4s6e)m|2`v|E^JM|D4#v@F77MCHYn4JUw<6O(t!rOklAl%;Bjq; zl4bdwFVz8bKMvhb3i;&guf74y?=IV_j^P9w{$?4f9d$b}YqC7~nKw286Vu9GyGD}d zUUO~A-50+44nQ8NRKMBZ{xg8?N1=O-kk7o;x(=AzUaGdQZo$5uq*6&T;a3HCe+M(D z0Fs=$RgA)}uK@+?g#2#%JOVnL1O=ysj8;x3)v5{<-ZtNx_!{*`(}>FNf8yCM8s4I( zCp_^oKt2$Qc*e~bipcXtB17-#M_!uy`Tk@XP{f|3h=xV&kvop!%^w7oy-N2Kd3MPQ zN~SXT_Q}&nu@J(e(CtVdL2{82xP(+r(n|!QBio60Ab`vCX-fwZC>O2UF0<- z4Vz-l`rX^c|4xxDK>;p;VNx`Mlt7Bn5D0;Svt(9&c<=F+Chr(I<|uSd<}LYh$va24 zB^fK^7@#l)LDoneAUFp33qPoSb1b=h=@cdK6#*~Ee?VJ?JQM`FATTuHmndXR9xVzN z5SqtrlgviX{L_rI{`THBa)}WPi-OFAN+IYc;i@TyN%1H`r4km85W0jrB@hP1=_x8s z#}sMI6ze1xBmuQ_H|C7mJ)NRMbF>sxxwN}!{SkS`PjAkY?}4#?OiBa#5b6ulw97x{h(2~VgK0r)HU5m1e)`F{OkMb zzeF)%a>|j9jWkDc>r!xuB83E`Av6OS_GDL+1B7l;vZu&RNl`X3_6d?st}0SJ2?f8lGlcUilkXm!UV-<2~A8sDuT=sZjHcw6tf{j z0ijbUTuW$v3YStqf&74!*P-vbAHVbZHwh9=m@tCM5Nj(fe=6B;2{h-VN?lyO+XNWFOs8;+)3o%B+w0EBMDAG9uGqI zP(+4+3*>Ae-v_y>31v_C3IY^Sh?g8No<%`3gN#fH#}mY#{HKH}B|H$BwR%LmyFT$n z0%(!dMdmkoZ7JqUv1tk`5)6e9c4YIB?Ls&>$~B>k5YGy33nV>DsC3Gkq4+34AP5jZ z9#3*r6PSe{;1u;H=R1MC2_H)Ovu^J*zyHb;6bUDP0C_|RmP08Q6v-fL0h#BNc%U`3 z!>>>7|KXSPwkbz4$%rQ7lt2iC^^Vu>&>*3Z7y%F{P(t}8WRwx)f+DRHxgeJi1r7lyOE0FoY+dXdyXN2%Jm!QgRd$YM+osgrz1cm%KED94B`KfyOCqfdGmW03k3E zCB;y%mF!6J{t{e)^4kaxM)3$TkqH<@IZp)5qGTMx+Yok1 zpnw7ehRAW(br|9&hsL5?GE5TS|POcstq76pWyt5~a-PssGcyJ&lAq zB1kHEYza<77&{8~kw29(1u5pPCydVS($)*J2s%cnL`tn8kTE6Dk#mjk=@hA@I4p(P z39CsEX@WUWu$tiVOO zAhQ8k$oQKizS_;61v!F3X#{a6w+s0Jv;kfK#ex9ZgwoWW-BKtMgd3vZGNIuJ z8$;+p%HSY>GNsiKs7R+{P>_Z^`IMP78EzF6s3$kGb{F3c(*DJMaL`?aUi z!1oN`%VdI>d@q9#Hf|CVL^R12CIn`@GzN}jqQ=IRZ^9-9scGD71}NzF0YJ_ft4{htOeY+&#PV`8$AOm3Nh7?{K;gCI8{3kDJp z(Aa=Q4AR+TXBk|Vi7~V5UuR`#Fp~*=892B_C1^aj3HBKbs>zi!phFX_F>p$gF<`OO zHW2KIGFT6)Nm1gwC&ux!x1EtL476c5Jcq3rr^V_i4_agI3S9vFPXVSWyBa_$ai zoOu%QIS6R*ckDps93zHYa+)0HdN9m_L-?G+%@|rvy<;RE*CyaxAWkdhoH33rFrt)` z8W;-6aT!j&=R9=Iq+v(|<77Muye90(p#sjMV%#bxhjB)zHwRAV3o-tMQ&&0RnxPyV z%;A6xCkk*HlBXzw8<6Fa1{zMt$N~;RF|dqF4scZi4rDUg%p2k58VNebo{^}G)!>*V z2jv;Y$VqaHWaOlB&i&_nM$Ww8N(LN$@M5R9JVjCYQuGFFCDSQ!M$`7fMg%h}tU0M5z6oDRj=cbwkE(N7N~jxhsn z)eVhqWaK<2nlNOTleIXtloMIF$N*PJV9+J!Sa9~T2SjR85;-8twHmnK1LG{YE(m8P zGhUoSri}CFKqKeYa5VwOzIl`BG~|-=ZW+kq7iUB=2##xQa6Ug*tKj50PWxt%BE#_* zB+AK)oM+DIS&Sm%tVu?@b7B|++8ATTuwbrg!Z~f6$jRw741nW&46et+kPim^aEcU{ zUSVu2XZ>@|Cc}?7ZJl$_IjNS>G7Oz!)Gg=jaE=kDh;UjVXH7GfhjA&K=gjqlxblw| z@yw7gosQ4dNVsMa=f`jw94Ce``jw%}oZ!f%K{z9d!P#60gzE?}*20TS)3|xgpkgc; zBkDM{lS}t7D&Cu`#*i;AXv6q3&bsD=ew}{8@L?|^oRM-G^2q=$21jus9M>H2LfkZ< z+zYhRKp9Tg-~>4a^K)G!1`u(IAZG_L;-2A*obbV@ST0Y)3E7NG=UhI9#BsqNhUIZq zIu|@(Og3j!aOn~TqH_j3S5jio50|*%bWP4m;lyQ5i{@-gu7k!XdalLA7&A^0Vn`q7 zO)<)ulft+z6GQsF=tqqr<0@C2k;wQnE(yiCSX>;5D+h3{3Zr*86`RX-a1|1+!ov7$ z2J&)oA}-#*sjUo5;+%9Y%;v#xnvzpobjOoi>cUfE5Tvv`uU2*mx=La#2lFM>%bqR(FbL}3^Ip)eLUfi3;ZgNc-1}kz|87^+br3^S- zg436|loqFHF$#_G zUYW7uoZrCZjX1@D(^VK*&Z&b8P3F{WuI|MpgczgGNOVr~XILJWZsM##Mgeka1*gey zAwY&1bN(ggRdeAD#-?-fFjp1e6emWKbDc!aI%G^F157y|my_waRs$Cu;}R@fVvr%o zT*-&?+BkEKlM}hh1s9m%L{U!pva*{Teq~z3euIIoR&zvO6z-O*Y#U;+TXbP8y z<6Imr^v8%wE~CV?-8eIV%Uy6H;r~DXn{w;45mn_CrD|Tcw{X^k}wI=8L2Nt2+GAAz9Fe-A*4tw4|nX*!t_2=+k< z2f9uOB!75D0)9oepZqtWBY*#YcK`n$x%kb0e|`T~Rg9?_9k>50YDSOwpZ))j`0uY= z_;}Sjoh_}g4|xDzwf{C8Do$s_3AHbEEJuU0Boe*Xi8MQ*uFjT7G}hAEL9hA$zN94* z3p-Sm-qUC#>a>JnPDfj)G2BGIXQ~%RQ@J=EHW<$z@lh^$Zy#-rL~rut|Bm+KR60a5 zfWvY>V;^+;POJp_(U`L|(&dEkn{7?blGdiM6OAklwRJAlUuloDJBG1raoFhyM?1ou zU7tbvn2} z;YAIRrq*y1F5pe(H0CsSMcZ3rEjMc;;w%c?8+N*4VJ8-DYolq4G&hH%v>jrhwwnpc zO_ynuJBgYb$7zf-`HzCmaCDLHkHloBSJ$%FIkd1hl@9EY=A?6cY;-t^)=uhG2!~+# z<{Tf>oa1uLIW3VTG<0H=`_N7AF1u!n)0=`A;+&9Un8+NcHBG2n@f6Ir2QUe zNo!||6Am@D_+IuuNz2R~^6;{`zqDLz^tL_#$3Qs0Y&s*3;at<_v&*xFGGMsX{wt$s zZFfTLj)$Ggae28MmkrHvdC<&8UDVp?cv#P9nOvqbM{}HEr*Ko@5{F_AO-{%&Lgp-s zJ4|jan>a$|Ak#;-<{i@m3w3_jIB<~vjhjR7ME8EXu~*d&(@n;l=4fOQ?+d+Y+BG3I zy-Do;*i#;TdnE2Jc!7FZ{%`(Ke2uPJTFus|OP; zrM?x=T+t|!%<1lEHg`HATHBrWNc`Q=8gmTQ(2d7zBx+7s+^6^peTFZho|*-YyAh0h zgxbAYRg?X5Q@FXcU0(sQFtL0&RoEvjr~aHBS+nv^CC)U;2Vgv(k7|l2Q zVK>a*Ts9)6>WGHzpChC4&8 z?RI|Rj(_aujcN^c72j`+hC`iUy1Lwx#VmfrvuVv~!xQoM+%hp|3Ek>3>Vs{7SDfwu zyf&$Wcs+Jd4?Dy6cRIwjOXO47G6^}gV)XnX>F$~;>PcN=!K&Xb|)HcZ0%^(``&(nR^Rez-DI-HIF{L-6v1d@akxFy-l%UZ zmk1vJ$PcopnctYt`ELojI-72OM$;cq|IzDh#L8|~GwltY58-yy+F`sdXa4AFne<5< zxof)UokX(Y*+-;?P%mycn?1LTV_rl{%yYit2>iYOD~F}oAKtsQBN}N8hnr9oV`1ER zH@8hFA1fM9eP1Efz(k zEk?W5$jE(NA>GQ&Hg#)EFBq;YO~QxBW@Ds1*4f%gs$!_!VPZz=$_+Bm9`PEFH+<16 zk$V2kDti{g(~ny3^wDu+Cq_Tt9&s95qm5mQVx73f^m6f^+QRpQ+HmBklPzfpn@ig1 zj6^#`EB7W2-)5V|eQj$@>lSZd5xwHE+J!e@!s$m$CP45v|`^0)PFw zCGB@CUB6?9`W-T+n4I4#MAnGX>V`|a?+#LbiHr>z#?6;mPknnt>%~j;ZEOFC-6fKI zQZEfLzN0De9VWx~Y6W!qc-GtO6HRgNxa9UuTWAUE7cs5Ic)A#iEDAe5;(DhkqI-!7 zzG>4a>NHf}@ctelh(+|oxXs{{dfGun+cz(`kH?Z7)X~ZJjfc_|^)YO-S?ii~ihTCFcr=hF! zhFuV9*D`1A1hTp5Y;6d4E(wR*J$GPx6OG8@jq%)Es6%LN;=kso((Vw;ckHp8;%{Y- zj$LW5XsbnqIy=LQIy#L5E*6PDCO>R?{(BS9>b+vE*lX3G%ZYh?k0B%6_jR={4z-b) z5?VstusgjAFM4wlH!RgUyB0t^i*Z7iYCW(y5)I=MW~Q_b`C)Tqp$<-CNo@pxa$3GJLn$vA*V5l2RdD-`n2i7712L?hUU{kTAk26q1N`8 z#|9!*AsWVejHEKebK)MFXoMOfX3~k=W^kN%z;!PDdmFlOUp0^`zz(c2vZ%qV(1#pg z^dT*k-d?qYqD}Pg%1Yl6?koi>B8+2X>VIy*O$BhyGx85`}B)wIM&*vugO+?q?Ilp*Nde+C@8`m(bh$w z=u)SlE7nSSqN@T?i)MS0J>mPVJD1o@W2C*gt+kQPVsrT|ZqGY`dUF~#6 z*fr3;IQ>97D^f8IhxiWy@(3F(Bj!JUbicy-9Q;syCeqc3Go($##z@DFTOH50X4F*d zMzNVGBh z*YA;7I7~irtC6@uj{<9>DbeuaP-`3h8qI;F?&Q*TZ__7PG~9?=$_QG_Ot1FkQvd#n zZt77n;)bPe>ByLoZrm zPD{9r=Dh)Tsab5AK^mz0DlZn@#*4yD`o!{U#qHt7a4d#LjCDV@b~@fhE338Ju+>hA zxv71<(pDy&i;;M(&10H%b##QI+BjH(J0%pe(H)8!G_@{nZR*l`bI90t=3eor5#}n@ zhoF{{MICKRt$>#&s+^Kp@bTY-f*BPCwCb~uknLpBo=eO+%UexwzjI_DN zH{HV8mUp}2nNjhi`8RpKtnWoj(95LD}U*6Zaz@EVqy`@YG& zYRP+u>w@i)U$8SAZ4O6`vrBsdP@9Rg$69eSP`DFE(uHrCa8&&MbnlQxr>df26n-~K zI$YR^Mq*(-9wWvkD%a$-O=yq1kz{?!Qpn9X8Qs8EoPQ2CpEv4xqyB;;ZSZ(F?OR^E z?dCxkeZ1SD(c*gWAB_L|KLPwR|NT|`Pvz(_)s+?T_@6N~mH&hP`A7Wse*^wcof+7@ z0#wpB2<_*^`lVQ`U%m(HS1JG&L$%Ol_W>%PPCz2=NSN$>`40Q?JkQI`22!}#^SfmN zl1%`ax)kV)P?4d8Q2SCUERY1qwu(sq{~p3!kE@KlFv}bG{cZ@rz18 z5<#kTL7gPp^14h8y-1~RQtV->tK1~CgkripO;7`_bR8P={0sr993|J~V}d$Xqbt6o zdtCmyh>P#>$GM@c0#f-b(nV0*h7JlyXr>Mw;}=eO$4E6r^dPAk2m?BGy$=KGq*5LF z^+3LNy{OWLUNNeX!RtdsFc{uRr7WOIm!g0QU4Q~Ab=ip|I@xu>30xhjA>kcT;6Y!* zYkV^u*d!pRuLxalA*h{Px_Uyw(nyysga9F3>>!CiNV?2H;*!>;u&!N@Yye5RIDr{a zf)J3=XcvIx&{| zl(+&v#Fx9yg5@D3ZZv|DxSiYH! zvwJHzSc__~+}&HjD&AYciQc^xyq)(}aE{$u!3O`{3NG~Tt>Cho?5*JA-hKynx<^&; zfM)DtKK$00*3$W#>IP$2Tp>g>-j?Izc5@ z;3DG2NggL4#R3aKCD)tHmpqGG@yma*EC0#3@OW7yFT-=df)7C@ue5!6l3Us6@5@#r zGs$}dB%;v@DtW(vgf5(yp*xnow`l4%;6n{l#|&)N$j`7%Bl!nsYpW(nmT9=NcpwecT zX-=DKJD0Y=vxI3ayo70s?W4>2(T9BFNPCROk+w-d5jrEQ~Ar?UlA8s388P$T~H zfZK^9w&fGt@+6;%3Gy%y=uc-JC*uWv2V zkMK04pElEwex5sa$+H&eS9vWm0_GIU$iNc{|B;QVsS5@Lm60bP&4q-5%BY}USiBLa zj9c-DWz-Rjt z{_itXI>sP&0Z zkI5`TJ!abO_n2oo*rQ26nhUiB)ng&Ivm8wt4Z8)^W0hH?9#66y_ShId_dTA9oBJLI z%!2hesfD7)X^%P3zw&6DIZZ#JrnJFJu}?*diF8z^>mC$sd^3;kl}?8gX&ps2HW#? zZe+S|usvtY^IJ z3@WRbS0<~9PpYgs->9>0&Ppt4rrbK2JiD(f-a zCG2qnm1Q;lEWX>a_WD*d>mW*lhD?CUI$^d!);YJ67rl~{byYwTMFl|hN;GrUD@#C7 zL#9FX$}zLntJD>`US)RedesOBYR4t0UK8!J)8ZDd*GxLNEcg#puNIta4Y3B*YZ)#) zJ`bwbW3;>kw+7W~y;+@J+xU~X;t?(Hz4i#m@WQY`^*W>%j=QKGWJk_0F?W*1Oo}66sy$dAWBT?F@pEgX(>U5xL%T^)l)c zy7vNYiS_O_W|GP_CAc7&tlv~ zz478}#Ya%9iFKhi)cb;aX!L&9+Ah6O!Lg8aP5Jvq4KWA$36Y+3Xu`pWS>O^*Jmc zl~LND`W$m@!anB&q%u4nRG%xj?ZQ!9#=eQ%MBgm#bYHLcQuQshvedWQHxYg7cp~~v z=R2|Q9A78zfErLanF7*1fN@Yc*`AXvr_5?pIkmWF zyJDT7awg*w1mFjiV+B8Fj+K@iuVI(d%<5!LrxDtmWp+MuR>oP6IjddkF=sPRY0eJr z#~$2+gjWNVbHJKXIWPG2m7J5FO`l`0x}5g~r1Cxjm7}!uwl?CmpPS91kjwgA?r_gw za;teTx%Fl+xzqU3nLgpjoy)?J+h)$$+{O4iI=gZow7+}E&PMKPUw78pFKqFPcJ4N- zSmy5MR}Z6A;-(~k%6);%<@-B9<(?Oi=B6ir%Do~Wxzir$c|p^Yyd0zE=M}_DMV?px z%Nu7`Bky*Wio6*Dk|-n#Dv!;)yk^hz<+bzl^gJGZ-eYmXkhjhihP>@840(I_ z^9Mb`kaxsvmb_EclPGG$d1v&l%DYHD0FxpCD(`aq2>S&*BkY$SXTK zSptGuEeF+aF1=?Vo}l`*aJTy{v)%6J?)rXf1fUT^)ibb!3>UYA3OFvv~Hys01ze`qL@&nqAF|cz``Dx~*e8<;V{$Sq`mS5}sH3lmG z4jxqgJlo;?X8#V%Z#M@+{tEv5Dl=#KYxu2ozG3EXL=sT_ zt$p8rv0e5454tx}|CQYM8uu>h|D;)s{#*E9@4wA^^7r4(PLBSExQQ3y^yU61Z9A9v znRf-Gd+Zlb{jUpv7^s45^Kd~9Ss@hVDkzB4TMLHR)hL+gS%ZRGJ!?=f%cEKrG?+uB zpu?|P7A*FtmIaR*At_)FXTipJE45&&7HE_90;*uYfE1hj0;+)hIR)o^(o}GnrD=dL zy4HX+tx!;E45$HF?xGAR=2;(5?w$1kwKzrXpa$G-4#)x1&FBWqvhB{*@elg<0y|j) z+PntPfKJ~g8?X}R%UG}j)_IMi0b7iW4cILp*(BY78n9PDDucT~4LIU!{)~V$7f}Xk zz-2ozg^2>v+>{(pg&Ahm3yZBIzHkWH6fQg#RAGgHo^h!jG4?ZTO$)E-cUsNY9hWA}n8e9laB;c>H_3QzGn zX9Ogh#2-+F=k0jddtE4f!cdgR!cdfB7OlwP?Nl__?$e?Q7K5VNxbw2e-ugvzd`nq0 zUvDp)Km@9&!-z`JDwKblOa!XvF#(AUkh6kR z78V(2m8azmW=zgg0($teCqOwn?ACFPcwRYfUpXTni4bd`oO1$#I_U|NgBp#&cY$)0 zIaUT{;wU0G*#rCd_s+mV0cmcE45)!s0#Yn44Aj8ejg$|Z>(}20E^tklft^;`1}^tq z5(8Hm+h*W;0YRNY1!~|Xvu*=-Sfyv+ZtVk&;rtEUXSdM6BmBT|0YMFL12ym!GS&FU z3#j$$lq*mJucMmBIX9q+)65Wx9h_-z{tc+&!Dzo~%qys3E7-*|?T3r!;Dv6Jvp^Lu z5RhzAv_KWxpjGioz1fQ&C95FT)z%5B_%Yku1{R}YpD(X?yY1UPJB7sutuLV1TJgol z`Hz0wLa~cmD1O(!u*K4QD3)Z!$!Lk=9d}8Mo$-=e$v=!IS;=(s`4ZgaHbD$j$$b83 zNe5m*CRq$r$pa)iI%N!037&H%aST+6b!U|95RmRkA_G;j+y3dOo%WLBb_bNaVy&B! zv#z6}lzIlL)V168`8ROp3WxRXnd z5!S&ZwSg)btfF6VP*|{5Zkm6Ns z5&Y5-?%PQ>0#5CoG1D~Y-S{uQZuesp(T;2IR#zqlTR+|4z4k^Od_Vw<1vQv}SDds2YA~ZS z2A>s>?!lac8hp|CuWkw0c5X?D3&Y%!<-vvAGT2PcE!B2rZyD!1G;XQq{d>zSGhesN z*T>f_O>xHOEiLZDY`NXCitXK7*02-vmi4BMEnce;|L_z_9VHBcx@Et6q1|$nyLjrx zOYoN0%oN@t1q5|EA*dk%`_7OoudX;GPe4#77=juy#7x4Fnm9{zNS$eP$V_f$j%#}i zSzupmW2qS8i^LCk(Cg|OvRXi5xQ+g>K|n$S9=1cay6^ftRzvof=^pYNULx)0EF1EI z*Y7{%jDS?m5&$*ivVdfh%Li&`z_wq zEyFtePK{xm_M#ZJ$_{bsOS1!{PTfK*N|1vNa!yg9s#qNz4V1k~^fyWYcZHJuqg-S&O> zERS>zpXZgX;R}sC41d5*=Bf?XFQsx^$j zB*20ifoEc61*j1-)7irpdJqn`o%z8@b7+w0^uu zD`@3Ajd5Il&@-Lo&-oSkasy&0zbGJy(tAObU&eofrd_-wn374v-eSw$1yXBCU>bziaEYK?lqDpt`txCzUkDmEAa zsbDx>#Xf(_huoG=y2E+RJufRR`sS+QnwhIg+yOSt8B}E^Zstl*m5zW6uQCp*a(>FXOgKwRpK;6+VNnhJa10i$}4vIDzA~w+ZDyVT&c|T zR%PgQuFAe)>{U71HKNbuDn1mdCi_IZ>UJ$atxb3*Rn0Xcty*Y$P}M<65UpqmRW0_v zxNluSb*8;h)9s3nnq{IUtp(W z)~(JrU8yFVN+i-0165sSG@t4?&8NDKE)5&%ubyGWyt={qUaD;#Ms?J7q56K`j;&tK zJGT090m&vs9aJ>~7OJ=71=twrz<=+y4IbbfQGM9EBdU)XVXMYnPs!|{s?XcStA02B za<0Da-mjyx%_bUMh)RjhB?dLR)a=92Yz>YcCm_R{{0?e#y_Lby^9Xis65&COZZf?d zeLugyOe=?@AJWR<=tup^;poS(G5YvsJc`DE8okZDveUe>TcXx z7z%3i0Rah>6_uby9}*A`@V_Y>m-FBbxN<+;7 zoV+SfHOJ5Zu(|M{YVcfaCR}pOMRO|ET(uHhbKP@Lj|rI7A48_HPT~eNCdYPc%wShR z98+$(G-e!kY0PAAm&VwL#~3{WVU7GAv%u?D8Pnmr2*xb8F?Q+)9~5IY(?Mg)34j{2 zO+XSA6#zBnfIFvSES7J~DcrQC%mAn{XYAOn(1}eY2SAOvX7@{N5Ot(TjQ*Wz7rHi| zw!SV!0IIgwuWZ#;d%dQ$x5s&XYUjkcHfr1bi&MLj7pL|~{^n*+Pqt~4QI`hc*FIxb zu=Y89I<{N6s(pdAfZB8HeXG6fS>D?B{hc2hv{EoOA0MNF2cX6ln}r=)!R?H#@w78` zGB5bp>0a3$JI7szu`OQN9?Ks8u`Ac{|X!LP&y~pLa2FhsY zq@#CS)EII&mT}9CA8y=Zc9O@f)lO1yFJ5U`Rhw_reFW93jt=^z zJ;~~33rM2k6QJtm2}o=VQ4=k022EYmSZQ_3$lGj-Qh=&kX-+T&`}5$3m&w z8Rw3u+wU<&>rS|eU)@=IYSdk(%gU6p09AL54}=M9^iQxD?Fj{TH769iXZnOP`{x>- ztO<4A$(nGh)wL$fb~`^|zCCN{o}94IuEK-|U63UHVWr&+6V`c#J7FX1rxUi@aAKql zUwFpsfCfT!I;|<&Y4-AaM3<{#hsrCTJK-92t{gQKtQr-1k}VVo}h`2?eN4h zJHClklpUxGZ-AO;<2@7In|nJ<-aE zTg`wb?)HT(C+;;nYvNHCGd7X6n~7%#t{Jt5@x=4Cnd<`5-0~ivCJ8f?NjZE_(Hvt?53DB&mZcY)L{Rx*qG&$9>gbXIS){iR@fe_6Ocs3LO@O0 zpp!!zbtj(^5ExwnYLaz=PuhUm{2ho%nJ~Ma9 z91R_!LMfo8a7c8@gJz+e8Wh{VF}pz5dF)vBNCIl${1++4RNQ1u;gk(T-uwii!&QVi=idOeW!+s*9N z@8cx3`h#{2>W{|7{OV8ei0geh9rahe8tl|W0U6$`Oi)w%@S9V8_^zq7R+gvUZr60` z46{sA=lBqsQ|J4>(qScj>T+*PZt5y;BxCB6anmyODYGA^?zc+&)I{O1*Q*` zga74U8h}|hW%!rl{{*0tr-4eIDI`4p&I$kp>p;Ncpn{H&FVA>yAkIVwC|E4yq%9%^n0sDMf4=w|z@*@E!AJW&f*;>Rxw$g( z%;tW$K6#+h3x%B5IDc0F6x4u%I+xpCHr@C1kMI-cLBS;P-!!T zteo-Pg8<#nK=(c&C$1{H222Sqe>XMZOTdiWGV<2K*#S@~rJz#E1gP{rl;$cw^Xch0 zJ1OHprA!ubVbxDd@dGPBrL7XOwDrO){Bb|1v_nF^+vD|R*!71%C9D>5<&@DVRB5+^ zN}Dd^!524v6o1?WDs7MEaSH^30%pTJA%F3Ul&SdY?Vu8-3;DB@im&6i9s-rRTF6h# znf4s^A`etbp^#5s`Pw@;f!jdAP9Y1aiD zeDwS`it!WEK_$!*vSi0+djL~z%WZ+AXR*!Ypx_}P|9N)RNML$RZl<&$lmjh)zW29#@WRb)A>HAR3+r;gLPd2P-*i) zr8VJ(Tmvd?osdgHt(R~jPlF1a6>{?%%YK2KtH*T_@&w$u9++cVUi(9qz$wWC6)Y6; z-`EO4x<}cmY%b@v|p_ zN=y^-i@Wc96}zz+RNyHguiyR3ZeW&b`K9}Rk0O|!11h~h$SZfQ`A>lE_n{jHw)1#4 z?zj`6;1z6oIZC6DZ#}u<6t=hzRN$bHom+l23Fmb&RU451mO7OVo*k4+FW(D~x*Ml* zD=645Uq>RO&S$e;&@-fD`lrsFV{zetudE8%Zmr0s!*Qhwmv0K=*#= zK7Gq>lK;@-UqDnLB=JMf4Sa>V?D9XP7BR0>BH{9*pa3{Z)yK_x!!V~V%@ z;K8+^g6r`g?}G{|9Oi6L$@2uLlr5lAw&8_@{|n;($p@9{;6E0FN_`Oju>n--W?Zfe zP-)phuKGcB5c^pHD!E3;UH^Fp?uC>BP$|Vij-CF?L%3WOpn^3*mc2M_D9&absMLBP zkACC9I{@mSUdX~vwZ09|y$`w%3i;)?driSXB~ra1dEmg)pTZx{gG#t0cGTl1wo+Lh*>cyhLL71hsMH!E|FZw=T>SBNP^oyF-}l*-ID7j*B_9;> z`8zLv9y{`{sN|2} z@7I7zUMJ+&Ui!}g`1{GAl5fS|KTh>i|@$SO6-ySja6s67XG+paKCQe>m{kw{hRpQf)1nJGOQg{T)434R{Ifh+s3QV4ILjzS^zu z$461)6LQ41U;h!Ou?kdLt&sc1AN@G~co9_E6(N7VE0&HQ+5#$Jn~;gUZ^NS?;V6}? zk{d33{ChZeCs6ej^85qy=|jb!f}F{Y!b5j#WQL6@B^R{mkarYj@$6g zPj)~h55_%y4phoTA-ku>}hxgP@XE2wA$UG#A&S8B|J}kiWm>`~xTw8$cy(7BX_> zgKq+KZ-MS@Lhhf{iaR{vDyW3(LXK!lo`>IC04lLr$fwq>zXjh+1C^8|3uGwEOYL<)9KD5;DE`b-Wo8 zu2UH=IpLREX5b8N1QpyOKg~Wv^Hfds&t%TE(I>D+hd?DB z5%S0Hy#7rb+zL=htAsqf;}@t+CmjQobW+F+aUBn$)ajs7X9-zc^hdgf*Mdr2FJyMx z)#W%78$hLP7Bc-mKEPY&3>EF`z6{-0g-m`qX({&Q9BMv7uDdJv9qdaXsDx4>w{G9F z7W?9WN*pZY`hWTb9@5DRKqWT|c|~q{5x@2>Rlt$g21jsT!zoa3M#z``@Q1gsr*l9h z%@^|IXWqc`6*ho^%|cG^G4juNMC||-+>J+03aI2vA$uPESCp^hY*5L0LQbi8?=}3y zHBhNi$k418QQp(8fJ(b2 zJJ$QYEqi*J&+{Cg&-43ykMHq)97jj{zV^MZKj(E_>s;q~uJ`M0+52hh)U5j&ZhaZa zx#>r&`NL&E5gr9oM_zd70!t;r zQ{d`I#|ck1k8)-sXSP_5(Ayp)i%QM2JVA}#$r>#rS$zFwdnbl zr%YJGfMoW4n{Q&ytme!*l1V>3@7I>YEM;Z8uY8P^KPP!3{{su$!8q1nBFQ`FU5Pta zoi$R6q~yBI_P#`WvPS!m+&-tS^?k`rmOzoL?zx@qO46)6ilp^BL-9fnVGV|peD$5c zPA=MlHQJiw@D+EXA5Q}c_tlhN541hiV@>9hbZj}^#_Y*etjV<`-Rn%W7?j*%!$Fc) zwokAz2{&iuRwV6aL~XPc*~1#yPx7NwO?!Ug_S7ax4(~Y6lCp3NuuIbKZxdg$liFo( zJ;_zWI{n)Iwiauo9?721CbzS5JkA3 zm$AlIlJvZ!t^GE!oi(wGLp zT_^odv_veiQBRWOo!9TenEx-3iyPQ@iez@Zx9oA}Vpi)OkM5n&&~nn% zS;VA9XR=10A$hw|vdCI#AK;2o@KH5u!}u81_;`|P=?|=PPxS=#OB(NZuep8P$eP+h z((#hZrv{u^#~B+m#QTAYCm;OkzBP6Yomit?NoMbV(R$BVOV(H$lDGf(tyA{#G;5ql zF1r0Qq&K@*6MISCcy#3h)|9nbgZd=zANomM+f{GYXg`wo=hU{LczhJ_da^k2c%e0A zOV(r?lBbJ5w*D$roi$a9+^O6iRP?{ zRwPd}KKZDfVHT=1^l&)DazF(Enlg9i^- zHq^+v6U&AUfpRC${%)Z4fZ+qI;S`d42K{+(&U63Vk1s7{&MfB4GLn6>XGW|M=2^Rt zyp;D(gpTE`@l_;|IV&tp%Zsw+g(MG6{IsQgxs)}MCh2ic9ed7WU07q?N$UK#VN?6t zL#*+mBvYQ7X#p)<${Ma9Y1>e3SP&j$4Udq_KRE9f*1QAk-6QFo_h$=e(M_z;tt1b| zo~vzpZfe6llBH{=*l;9Ki#1V?q{E5nzp}Hc&YD+?XoT+g;3Exd+Lp(XVW^yD^qEut#~$p5NHH1YpO>i(NP07h#QWXN~W&WTA}>V(f>> zHi#kF^Y)miT}pMk9FiYAx5Co##7Y}5k*w_8(Z(&oEY@Hy$>0^YBI+DrjT|TGRqLfn zyRvB(mq;$Hdz&Tx@m;L(y(GPK`PJ6Iby>p(B#$+&Wq+F(1@ff)_Sz@kX3povB-gjv zU~L^a#v1vYWa+*iqeVK}og{hvvy~Uv^VFC%(afHw!>q|;BwK&fXNv8w6>ylcZ^&UA zpv9)M#%5VqYsDIGOY(8I?iie;EdbG1Pt>t6kQixcImwqNrVXseEqNSaPQG1iVZh&3!FX}51eE&JP9pw-GJKYeh84jCKAH=z5Z8FFxOrzr;+|#4{vaHb07_KF*q~MpEyu1@?NT zidj=-BtyEt`5AL&A!nA53~l_1b^hECtZ}KfaZJjtwvsg%Lo&Hm{!-h~dP_S==B~ZV zVqrUr9kG0{-U2kqdv7mz!!B&C_0c5L`|q;ujPqG7y1aJfdrw(& z*JRDBOY+E@NsEusL9EeIlKCy_Sznf%&YGM>aznc|m}V|!jg^tSdhPx8XvG__#v75m z{mN|XRpSq^#;1_hcx#Qpe-0@oFTWwv+Sh zPH$mNZ72D(`L%c3Z+ilhEDsIq{jBBbdsyT9?T&P2O>`r9Fz;@=_HYPmIGkii8!Z0? z3s{52Bz2Fqu$@HmK_{1+#_zVCATpOVvVi2vM^9U)5)D|RNs_`l>fUZgv?v`X3F7zK zyAjYdsA7XGe~BwJ8YxcSPj-#ZIU+8tFE=bEd;t**1d79J>rQatcm3$ z6I%?lXqFgb!!wfIHRf1C6}D#$JCTf^NcJ(di8Z#BWY)(`EHK8$vBoEoeDL~b_Shts z+aQOeOS@CISu1S-%2wJHmtxthKP%h#{r)bNmdA#(#ztD2SrV#^q7rIxIbHYAJs4;W=1iveqw>bl38G&-9# zI*;U}uCgaHx|ub)jpV22728wD7+)VXScy)UVjtf@96 zH{LX7pEcS_*7zEd8PD-e*3T$wDr)IUC2ML7N!_-2mLrGTS;Ji<7aZ(sscfFTByp10 z(xYqGJ{wrKL~`k8yDikF4zi|>kc6qd7CpmK)-X-7B%EwR8{WjqTS-PHJKbbwJI?wc zlDF%ei%HG_tjS`MtsTB?k7aTVILtDBIu>)1yTD$S8GrwWh1y^%Yp{dltx+i~do*W_ zwIcar`w=hNanr2fD3TF7ws&Lh&b>%(=v(?N<~zBNq~_^X)=zU&R?7>XY4nQ4@?eUM zBS~8PE`5jXaV~4j`mLMq8Ekvp$Qo`T+0d??4JjktSR*}2o;dmlp4|bgiDHsY_q4bB zomd5?wv_zgeOy=@>&i&}bXR~u$8y%xDv}?4l(tMa+8+3OdE=!=ZEzl&%^I6Wax`yt z%x=xSE9{Z|`MWV##tO+6c!IRT51-FtlwT9%ogWg17S_7E044!i3g%*hxu*MhLPLwqf zC3)s*OmoNg1FSBq2AyXEhSVO`)P9nct6rR8H>ke#S0sban`mP<-pR^)NVbf=&_Y0P z8h#5hpxIxo>xz|uhcE43eal|Rcstg3N0L5cYg;o0ZCQg3Bv*ZV>~{O|0@m1KlIMPV ze4Jg&Db|FN+(Y-}0g_XOLtaxSH_wB%kVLKtyAKr~&cnj8KYm%oQ{p}*# z^<36)0m-Ai8{E#EnZcRaBtNY`#rmO0ZPrMAk{Q*fTC2s!TUSW(yWf4^nklgyTzILS z-}}3^-<_<VSO1$VT?JUv&;KW+q@)ofgb~u9qf;a%Qc^>@ zk-_NET>?_08xfS4#OQ8t!~nrbhm>?ECH3F;=l^;3e75Ivckg)J`)+sdd*59?hzMir z0N>SU^0BWIep;Igq*zkg&>j;TL7vZ zNMkhr*r{%CH#P7ZWK!cyUa)rcS%$eW&3E+^^XvT%m{J5-uM|K8tH1b4Ar}SA(|Ub( zO2xUi2ErUYBVlW~G2Jv@$h1X`A2#rBjfn)N5SZm^BRv<;OnV%cr+w)>Uo;PG6X@Uq zzw#Rqve@aQd=hqMS>2%a^g9EifA*j4r^{AQo7l7Eoi5WU*wZ8+Sp8(_{bPlCb>w?s z5C}4~*cfa3v+3K|-T}g1vx?JLtY%RB z$~O||m$zOgs96JT&IEdg+Ww9WR<0{h^0@jVwU#ySK?H|*|5f13@Iey7O4JoFUvXq? zOYN8dV^fsI!3@q3Tk!%x7l9fXsf)&`*xu=SxiP#T)%jt)Nd0En{yiu`=R`tUh287LKL zdpyc!{Fjax0$`u5ZRkf9_rJhI%6$@E?gFfgzCgcfj4YH zz-nm}rw^-O!&K~iQCbNq98Hj+0<%wm)pY=E6M&EXe)lZ?m<3|Z+MBy&ZOuNUJ_t*i zjpyOn#D0cPR6Mw^6cFNKBv%teVM!hbGMBvx>j2h@i|C}7jU(#L-hGLQk2O`WOk2!& zL5X?c3hx32H~>8Dg&gRU5&nOvWlkMkFpfcRlGsl%`@&7VXD?tKoi^<^b`Z=98F+-! zhlJN~HRDts(42)=uQ^)(>Btv#|7zoi^d>P(4fYoK#mspe$w^8Ki`cJdQvcbZHOz9d zSIBwCZx^FfP(27+6} zQ6IbVoONYAaH?cIoYX4|=TSxu3Ew&Ve#0}h=eISZ2_gffPdM8$is)stZEaSkGFb$Z%bA$MuVReEJsrRjaW4D23R=U=xp@MnwF3#WHn+ zttl^V--L7#bVVclG}X2CZ-Nvcr%MPc1vuOH7DBlf_?RLpRD;5ow{mvvM38m~RFs5s zQ?QJ20?;^h{oCv^GVs|fIjiIedcsHK{q8+i#<$Ju{3Ad5%tb&c0T!2|;n3z>;DQ?R zG6{&dd$-XCc{!|Y5&EM|0pk1swX1nhgU{vEU8&A`A?aGR?x4Ayu>WK{xmU$X zhbxHt+RL$f7=m2#VVb2FdiqwTEesemkqi8#hP+r{5bOjH8o$wg?fI>)tA_j6C=;xX z*r(-meU%L3CJuT#;xYWxu50z-(%ijV0f=P)TrY zUoKP!JawRO+Y}+uwm7Ocn+`UVe<2p~yNr2%zvd1OEBIo}xvyT;b{Q~$38b;~<2GvO z`b91CM-nsh0&bkDdeS!q1U*)Apo8YP> z#*G7I0u;WE4U+9=V&!`Htt)L`pqJSL0)A4R>qm`}U1y7Wsxm zU_;7!?O&I{Ig6+>@zm?HPf0Fm47-r=$EcBj>YCbD?Qo!cv~6&VL=96%CeSamWNzov za_z1HUar!mllPA*`Y+f{t^ApLD9}C2HlP~4yI&2f;r2;g(1Q%PoSj4={DA5OWs5Dj zH!wkn2R`b9;9ZS6+n{lu6Cm>*gGnZJl{NVf__M>V4!BfZ?$ayv!M%LMp zQz1&^(1(;#OMo_m&sBt+9=;`-M?2{5!eVco3CW!U=9P2A&W$SZAvV63I1uamC=2<0 z`5yEq$~?sS)zC4LC?6bA1z<$3weygUs*+quD_`6(@R#lMpB=bJLhON!%u( zvEI21-2f{(gYf|lt2^+5rlc`5s&EJKPTO)@k4F#>4isjJ4kKWthp8bzeN%&w*Kb0H zbAE&a+)o~j;KM)eQL5ki8~Ze@9W?1Y_xQCHG+rw#VKoNP61!?t!H~xE<}f5*A0?ni z5IsaaW3M?_-uwkTJ zYxXO6OvN^MEKD5)ALb4){b>GA(+fwFJdKs8qr5cD)FD~xAv0%X zHh8W@`@SY5&rXCXb-Ain-_D5u;sgZU2~*6Aa4!rou>zii9BH*hs^Grk0%AS_PRNWwS6ObGUeH%T9b=M1=vKYj-d z+ZP_#uxT5+D!%@>CS`^Q`&;{oFgft5dU-&0N{D$kW#P!5DcwgCY%>O$%fGAf{u3+a zyM}UwUf-i}l>?`CfK)pf$Lf;O5hzhWN1@5vh$Vg3c^Q;Rxl0p~Y@XWO#$1Ry+=zM? zYKhKk6a<_vo#8yA0fJK-K*7G}Ec88nFocyF+$TQdmO&={H3pnhV&V0H%c@+)ST73D zWzjf*%$ug&H3t>k9J#^_tkQsFpsHIA!h{6!q*TC``7NQ7g<)U1&W>>2dt_V=qP!L0 z36-`E@~t3&R7j!hlw@96|7mhp0R0wb{!%}fKgcu+cw4x=IB}L2Nmu&F#J^!*w0X}?s7-Y$#K)D%DJ@4ie8e}?}16KX^bI32_C17iwF{0!&julfEDPsxb$!x!< zAU~px2h(VP-UPMNTsl9IrR2IhO{KT$^+#CAqclvU5e-G6kwCG$^`5W<9sZVhU}L83 zfysAhqkMooBrs)>$x8DcV88LaUgF7QDsVEUWXUL3^p)Ay9Psm#CQ6}>p`6w*pag1- zr($s=&$vIk6d>s8>=MyC1IEnUqh9j2aFJ<=6|dmBwAwY#-&AH2KUAP3vQ($>2)Y-;_2-ZPBkdTefq~5k{*N zh=uI+MH2;*eVfPpl#fx>821lB3nRls>hP{Is~Z?n7=lzljmpdDiq%H}Tl3d9`*mdv z1&wK3)SWv2CacWa&b5lb2jgXKFz9Cf1!B9tf*$d#SaHh)aSp%LeY-QV!OcDm1Ap)G zrC2{=N}jh@v&{u(1M-MT8PKN^qXj26pB@FnTQ@`#@Qql#@AH#Bz2zo*5Nm{M$pSXy zZ=v^6T!7&9NNurbq4(C$G^o;!oQJNvr9W$&O`^Ie!3GZzZUICuu z{yCzRv3A9P72sPwh$dw~z`RK8`hio(tJxtU%Z4Z0%d!M?dTN*&QWW`9pEuKEm97zw z;Us-auC@t_F+g`7RNdjwVPjqX*PNVJNH_F0`mAXe+?N2bTD=&=P}qPu*Fez--wT$J zK%*K}!}*C~CP+OuD!S18Ok=(i4zzZ)te1w=(}R4~ZY*Zcz9Xpg7rxh9iJ-Ou62Z+0 z2)hJl_Hx~<=5BfHe^Wi+a=Vq`Y$kthh)GVJdV6WsBj5K4L{{?~zh}A1@&y!gArBwc zSZbblV=o4AW+@suP2fB)3zXKH!pchpd=XH ztZlo20;IRinXs}=+mi3V+t2pN&&~+3)BGraO)zroWJX;XqfP^7(jM<_b@#+L^24JK zUEV<3`0O^Gp$17@!Jp|4ZCOZAtr0)KdD>p}$3!T|fX@s~J|hu>X)N7cOZ0vV38X{{ zL&Wm61ux2mtN#YV@I5`D zDkrJ#yPt_nQ8s%$aYzsKZ#Qj3g1sek$WPMArfp@38^f#4S%ojpO-_6WWTT(1?Q1_b ztWr7&6d~RzyFsKV1AYhuj1cnGg4~ibK0)s6v;Cc$G`y&9k0eyuw^O1pt#4V;`Q9 zAW$Gc=EAZagQvZM14W9dh(Pn;QneNWY#HBg%=uk?349#R^+8DzCz1l<4N@5=S;3NtrA}GT>g3e4BVbl zsscFX8FuldPP8NENtQht{9T#-^h(k**>=6Du;a%qlC0psV{A}Qd@qqa9Mu%45BcD2 zA!?OC1Yk1@IktfX{T5*3eCu&3K&uI~)f9ic-3(D6yoxc_8&b!hwTi`&o!+g0{fs}V zLz3T+fpCM7#G;bV6DI`lkpg&E=|0`g}`c2RFXN%xqUZF3u!IkA~>P|Ae^?W_s}gzZT6{y&tni= z_E2y-2yLA(%JtCvoQs)U4ZF-Glf2OB+oO8*)5EY)l(5itV+!!w{GszA*qRVxEsl!a zxCwF8)UltH18sFZLx!d>b?5MLw?J>ZmCxLP5GNLr5Z{!jt7&e`v>{yLx8e1ImL=c9 z$%aXxoI@C2F=CPz^o89?LJ&hbxa)zv6QB{cP>P7915{(~y8Sz+c`?(H@D$+CyrP0> z4^tI)_T?`F`ZA7v7j_WxFrKyosbT!_Eqtuf2%JMT9>dVOa19>zSoIag_*zgKD~yF} zazqw3FSP%OC-z*BexIOyJ2e=olmlc`5!*`;leuw}z&L8drMuXWD6|iez#NzNVP;6f z;%H-k>iW-E<#S_CnEeLy><dghLesUSfeum7ro zdHDK)#2uWxVrx}->Qp})%gf{g6Duai1zQ@9j5C4XyQBFYL%`T2l+H?aCwXUO61aA2 zo`dpg4L1awX=@ThtofQnAckW*$h|NX*CE4`&}M-gaXbb%m-n03ft;!!Cb1)gO#sZK zvf4J`%j1U%2>DM2O|{%GxIovD;K;rnZeBN+_bThxa_*Wzo|N}|ek)P|oIEur0JwR_ z?QNQwdC)Ee2$nY8M8PkX0u;lBq+osvwj@HEUcqhuwv}Q~s+6^=N#BfmntigFJ)goW zb1iH>-u&7^=sxz_sy*G^yDmo5Ca)JRmq(a)B?HAHOH?Dc#D8ibEHx3_evs`@*-gws zqOImfE1Ug=gZj~&^~{ev*ySXYGscn?Rn*2~?d&(4)Q|eB^lC+@BEdlIesQS+uCNoviNba^|)1w&e z-cYq;Nzy5hVHGs?@&RdPN;V?JLH$4|fMj=Dm-!;u^zMRkZxDOx1~+rcg3r?0nK{6y zhTAAL5VC@=mR;$t35VF9Vx63lk>6DftM@`E@wQSBDYXE6AJfow+v3tyK$IC2r>!_O zVOAZZokQklu7}Ylg|B6|_U6s@X7B&`UVF}#fQ>j5plZmKTtdn)^Zc<(c;k^T*_$J& z<>C6*U?iS1-CRy6T->`8;Z0iJ(!o>o4t)) zT)nfF+Z+u<3vg_=o<+Qz-^Ii9Tf-yRHjGC$zgvz~YM#^q`5*g%2bjz>JfJKpEGtZy z8Grc7;g;pTO`M7be57#ZEf1qb50ix^wadJUV6iK*4C+#|Tr7<$4e8vKhkQ*28bqwe z5xAaVN8vw5_2CiC<(%$D7H)7CVH$TTj= zpE)Fuaco~Y;*XkR*ZH8FQj^Fh4<4l>RvFFlU*tS&YXS8bqq)?)nvIkF(<`Fe8d>l>8g6LK~C5~d+nLfD{+a{&Hlw^ z9(Q|shfQh+LpOV%yFI>xIE}-Zo4tbjCb2`}eC6D5FY;z0KK*Kra?N?hUSZVEE!M}!>SS%H= z!)_=$1}umH%UxL!Kt>1u9N!|2mCpK9F_JrBiAZj(Im+IgddJ+nft-8yY;YmXStWh% zlzy2z)p-QLdH4>IulQdl0pZ%;>NZbQ&n{f;6I?i@q^(zp+hR??Yx5u>@QIOIs5nx! zDH+k$#j{Y&VB-5FqiB4*BqObdejc^Fu z659PyJhBhu_WKi!I%Dl`Gl^3_nX^%L#(4;}B$r6NUi}_{;TimisC5F!eFoMl<|D>( z5Zd1=j?bfKhA{Lnb;95i#!sdv#(D`LRdQe4I4`RN0sw=>@nGQmcUsJh7hF}Q3kgoy z8_wBN61&b<;Ap03I>_!E?iX-*&sz5?V!iQ40hFm`u&H)%|1s#5wdO0X>?N3dMoB7%BQ8}oMH=cZ+6o7@Mz7BW z#alEa&4WM6fatd*8WOWc5F>zX-wSA3JuQ+@S>0Z~anlv!RS8~$fO?12tVL@WAV^_Q z(m2Uetss}ZmmXRP@l`g^o-d#bxg%!8CryV0G)H#2wDhxHrsW={Wf!9#8NkV&;H=)9 zEG>^66o-C^rnROPS4+8L{bFsqzc@F?C7RUrZUB9ZOZo~Bo3!-EX3O_I@FK6|T)3*h zX2=P}e=gM+ShuWoEKWs@saBQ5D;>?7jVYNg&mlKeiEG4=6uTne#bUix7V%^47M#Z z!4Kt|Epu*Q+>o7zaMkm>7A8-7ItL8g)aybIF_mDoaSZTDD$wo2I_Mv8wcM?pWj)Cm zW*TL0Y1so5V8k$vTqq=6pi#G-DJZX2Ar}58>o38xt$5=IJAeXf`?drIh7YzG05mKs zOn3J}EHE!Z;J2aE@bAv&rKa^QM*=r!C2}LTKZv~;CuB%((|R59YNYB zGwk>*`BvAx5Cyt-#Mej5+0Bn+>Oy02VDn9>$TiU*{} zXcwicBs`@bNglDR&t4qakPB{LJtX;xhjWXQbq^(hfz&PUO0J?diOY+oA9oED5v&QP zc|Pxc|N0@$k_l|sctktbdP*@S=q{I;;E z&U>|Ne=z5P)7^*9F@$7DV9~IS!#yGLKg`L0ag9S`yo(F+K1$e^wfEeQ7nP09BZk``ls<&w4u`ydBjS^A zOQNW#_(-I+@5LiR*t-w|=&zsbXVlyTB2kIK_;$QHX^)!*D#(f5lJ%@*iBCBArWKoU z9kC0K%=OQ+iG#_?Y(o{pf@4_moA0|di7ZFwyh1qPwMMrS61+ChKKVQL{jrVO6OKqZ zUXJnG{n^9CKcA=KmGmO~@!e?CG%P3L45Z&_=x*M-Pg)eMqkyLp^OtR!@|uUIWhGCY z)Qg2N{o@Q-=~tW_I` zooXv(nZ43!t+0teTMmY5tzF);aI@iV%1e6J~90u?FZ zm2^wJFl8bt>KEBX_MvStmvmp%9_Mt&G4lPbZ6;V0;#EtI{s3o_Ce+vYz=-2TO)QGb zd#`}Fp{Pt$wJBbJwunRBS;Q;Sobi(B8-CG7=veRl2e#^E^=jW0?0IxciTF&?AJhe` z9_8xlQ0|bbegG5V0SJ(fa8yKLJpgvz7dkf9eq??k|ec{7D#t_Bl-VSUtft z$k0(JzK^Rzgm6(D5BK0^_Z5l4zsHo}Tjw`83qB?(G<~0~ovhgNS*1ac9xP%-xx%$s z9`>j21x*HZ(YqJvl4+HeI+ju_6C#$ewiOH62*9^WVu+!(TA$dr{2!2NR z1Aj_kg%dBGlp4pe5+~*6Dl3+?d_Dg|spU>6G3gm;#-dIir577te%MRAkGSdR6+Xph zWCe_y^rA2DDo7tF2$(9C-AjO;J^O1~TP(9vsS|OT@Z-$Z9;{r(ro)p+{WN(%=NHZ& zI(zIrmw%8_nnYoyBm-hEiEF{6*AECqAN1H&pC>K}3itU_@G?zEh074ikud@%LM^kx z-m)R=*z=>48D#;MVTvD=lm=#xBC`VCzAa``?94KcVt-Wf^2ob(B^(#uK1HYfKH6NI z(ic$ud!)N7j2&N;Ptvihlzea0;h5Emvvdz` zoz|e39&;>;xsTI_zlck~Tf~et_FndvEIKwM8zT4Q!Ft`qR2wBL@pF=Ff4IA$wauYo zaz~Fv!$SYMsN?94L=zB=zAVBa&yp#_E7A;m&^{a=R*8HpjFTF=V&%;^usiEe(vu0? zQW6e}QxGx5v5q{F2(ZEKA33IfC#iT5N=uBFzIV`Y1Rr4+#7n)W^GbgY|5?OOl2<5Y z1!5KPG!gKv@+qkzl-G>#lpse^f668_mW~cFhbM#MjB_9Fu|`z?BXyw~*AH{y;>-{9 zrnNs?ch`n5; zfU=YeoY>zgIy|BC9H&UqQioRpx03PcBd~V(JKg|%=%Y?9k~D^lc=i{Bp}1)rzmCI@ zy*rX%mm;cAo#>O`Zt5EOd$e3%Hy+R{R^cNV(()b{;VF|vOALPsi|?iuyhs5LtCh>r z>hi+w-QmUYTAEZYq@9SnriHi1Gm)sLdCX{gUEs9P%enH@8f{?I#Gwo9+e| zM3&IKCmU^!K*!qQ|C;P#r8XM(yLvggqs(+anl~$D*1_e!Ws_)}M-!Gh#-eI?RQ$y6 zEIV*1qZ)`qNkkwq&%tR@cUSIWLS1*O5H)LahB~gSclU%yQekEM1e3%ACf(MGt)VUE zjuSmKVS!X7Puv!ZWMAk4*{zpn9$p&>>tM8ZDV_KkHIo-ZkBR@%0>C;d5#soH{LgHP zQl1;&P%|8Xz-%~cRQG}1y*PBLbe!rhb@+pCP6{g}&jlYqb%mpzkMNNn|L2PU=-lE2 zEAz&N>x70PGo`I&t`bB72$eW}N7${xgK#wYr$u!Jl`?T|WcZLu69mm5)|XLq!2dd2oGX`JA5$l_ebs+ zJeL*_5B0@eSICg*aSv~0g$#B(TU!xS7J{uq2g&^*wEG_e?>#*&14h{rs0{L)HPE}r^r8Ub!-T#otlUBoO!)cp)hXmWHbfwXg4;)n2uDqpg`#k^o> zXHXod38%nJ{Ae4SFYv#WJ~HTOf1XZ1+*%nK_r#N}h1ltZ4kgZE1p7Vgj;B!k3i9VX z_YRAS6uTlh9^&ML+6@yX%x_Ka$8%{Y%7&T55YnGfJWvT+#qqswildWAo$^ zemox^ac0c~aT^UIJx=L;Zt{9{oEF={S0$ai;!r9C0c}CgYn)`e2UFe8qbUhw zH569~bg4#mR_dOc;-@VCV5@q3*L>x}TdO$6u=Ca`0l)LHEiG?mKU{X0`LR)`3vNa3 z$`9OQW`s13JfRzHyGQpbPPy`;1UT;j39fn6Dckhm$L9odx{|qymN-LnX*xCywEE9^ z10Fw$W+mWHPyW*5gHO<8d0rHb$KJE2xYpC&#zx>r0>BfP#d{ZNO-O6>0r_yxoVN7& zpIB};y;<66_6H*Dssv9Sxap?tgxY5A8?mvTf;bs(EqfT9NXlGvv`OitytHOf6UqvQ z0Tk02#-uz6au55TK+0%0l6Bbc!zdgPyanO&iWq1)cKX8}jguA1MIE_sxnTRG_HrSs z+cA{(VMFAsKm5}|xDHiJxTioeeswsXBQ0t8B_8^|03T8ucjdB}m{Im4dLcoPkm&H4 zNy)eT04neUa4%sJp`pB644y7c4VOsUiV*G}S;Zv9^_~D6c&>Ac6n(_wHRTVYaX%lo zD87Bu_)|_pcLWLa5J`A+Dlw#tGW1;^&h>zQG-WYFV>q_R(`_O)k6yMscN1<$!Q<<+ zWTd{SC$i;Kt_`=`TmSW5O2r_tFd#ZGjb?JABF`he%)!Zj1(jw38lN2UY-D$MZ{Xqf zE3fhQD=HoOyaN8-uTmyis%+Bt!_h#vtRZjuVmf&p}%G|pATK+u5&y0@Qewv zwkOWa#-kl!s|EaBEV&1Eg+e$d&)Iy8Jo!2^M_YS)D(ySEODfuD`4^E;GxMnZRxaTg zcfwFkxsL9M4NvIMMxcrCH*XEup_pcZ*fu47!R8S|MwyowO@#%mVV1y!&e4(JioX=i zv2aq}NA+tS20qi$dd-`(@9R!ng>gpz@=CTXJbb3*?G*<$$QXZk+WdpEwRb|W(F3gD zgLaeaFcNRen*#Nt2i$fkeEK}Sssgg5Ui3!&Iv82u))NwOzfB(TtCURK$n$PjMQL>B zLw^QJ44D}y4Q^d1C)&e?q+-@vwa}uP6ze{;S$)t)?ms0TW{UcK`M}BFK0U8XL_t&e zX4-F1g;P!3t3J81$uw?b@HvH%mzrn{$XBT){sW88s@)Bgcl}RDCb<;BWW(YC?@yRz zqGS0R^nPi%aYpnAc|L4oMMRkX50m|Qsat$Pg;uEX!$Q&t(}XpEoe!y(jfPLKXJ^}0 zsD8QZIp3V=Y4ZrVS>Bo@iCVh9dtT-3B1s?WR>;m1r+q|np?Zk4k!5yk$xn+2R*^PO z=UbXjlb^S@Vx9(-*f{e*Gkobah1tB%>nPB8l1FbAmHO*BIOejY5g2m-tKn%j_bOY10Q5C8;@E{Eq6-v zi2h1hP7LT6^e7rXm1K}?#f;pvh^KNJ?Y0S_hq`RaJ6zgUny#p{S3u9cXvl6U0JtkB4w1C*Q%Jm z8Mt$gJ;BWrHjMhb<`kRPRz0=55P({YSmgg&ignO>&8zWjv@B)>1W5wNgQOq5IF$FU zGWks%*c9Per_O+qX8h4|bkMWo{BY|^l4$gp91U@sDNJQDeYIw*wZ$k+h+;WW3U1TXNpE0`j1gUf?Ux|4x;>~U`VS1d{Mp?qp+|T7wrX^# zRJ&^X^qU9KZkt-xyj_cOD!y7p*$jFZiu~jVRrq!Aqe{a|f}qLYJJrcZDB!eX1er&? zMVOIz<2l(LAkb=%nu=YWn!trYU%ggoUrAgMI`y8%{oO`P81aS^i%9!2+ABza!KvSU5B7Z5Y) z$_FG{;&Vb~um@Rf6RrBFw<#1XKH(V}`DQd5!~zoYUh?Is9*a4}e2b$5PSCG;bQ{g& zRrB7`Bfajq6hN4o+2;=Fy3zcyh1VDtvWmY{4juE4w0Cb+C}dz{)h4QKb;W&Yi?OKv z(0rpELzBd0cX#B}bfv$(?PWKGs+)MSiJA1uCmFM4i)s=klfM2@c=nZe&0`Y(Lu;sXBI_VfFWuMmmhrZI#&mS*?oTxKc#ezPQ&=W9hq4*oL8@%+5J!+q6tArdcPaUSND zruNj(1+P99wnqrKVYppweKYo$i_wq|Ma>~W^oi@O0TOO*O|Cw^RriCvUKN=%<<(yMSa=&rx3WZC zJ=|I_S^P8k1RpB1-fB8zCOt7NsF26DO&vm1tDh1(BvCskc-t>ghkdToE>-0tI5c52 zDw)5fy~Vcte!XkrBYZ<4jV|w7Y#CfUT)6J;;9F$D$dNso+-WSS8g2L=Mv2w2h1UN8 zNbEo{`%7H9F&jvwu}yi3w)un7Ex}8 zx7+CFFiKsCkWng%h4MM$D`=2Yt}zW38E4h=cV5EL72;V!Ut!QPlk2w6AsN(L??4hN zp1ut>j?PaZy+5^!W&V4jrF%uQp($9%|0`*${DZOWG{VJRGc-B(l)`6-*9YIdu{nTL za&b04GS3zAFB`1p!1iZn*1@-JL#G4l!(>^BZkTaxTcl*e-1lp!#7?izR^6OECUT&# z-|N5UqYon;_g((SmEz@wy4U$vFZGICHOXK7u_YAB-jNk1LsA7JnkT9N|V_t5a&-AQbxufpRlwKe=!+#r_87+y7>(hNyq4rt*wq(jtsN_tQjAGGdhm7*`)^oTQV``<1 zmfki&?N-;(e9fVGvjqW!v;VkjW2A`C%rJBT7W_BYs2&fNDiB|QPlkX0Vyzhb|7aI$A^2F+V=?*KV;Bya8$0uB@Sz46LYdAo=W62tH@o9+| zBmvVc(xVDId%PMGb8lK5>hNFZEgv|FNhEuyIbbUXd4b)|?S-tt^2L(7td-pUE?)OZ z;C9^?M)B0^vye}cyH@?b#O%M#n*Sk+&1Zap1u+d<&qip`zXxJJ6W!1UK8&4xJ1mpi zXJ-dyTW13Yxd%9cL_e{nJN>l|lX36{+o1HYHQ=W8kE^G?_sb%hb}T@{KeZ{;vGwy` zDl8jXvLO;els5lA`gu`_{}_I7T?pxWe2rDh?s!Nvs@xCp1Zz#zDoutSWS`o!B}Iq- zCv`~%5-WR2M(uwZjH)g=_U213z8ShKJDA(X?y2)|pZAcM!gd$`6)vcdF>MvB#nRuG zB&~!cQJkCC|4V@oIo!3fm9<@A>lLeHUirKK*Lu9wB_-8uv3VTy>3@>&c;{i|;c*!H zPx|i0d}~eIzQ7zH*5Iiz527oKNkbf3urwyHUBhgTnLSol@Qpz9w0K=KR@J3s^o=KD zd0zpxF*{<3^d7c{I9y39qqE!5v)JbH0Sj-z)W5t!LRfzF7c}AyCjK>tA znt#hsyo)`S+&b@fsQ>PTlFv^cHpg=mjM=8?%#$KVhFs>!#CB;S<^PLVZCNeaSUdPny&qHihgkKndc<7$JmveBDutH5zwl3i#M*xfpdH8=^11f^ zAC&OP{PlmOu&1%SxhBc1#RAj2@jum>XHk|1ZMM0_ecld&D*tkA5v@RNv^qW2>?K;= z+Y}^z)%*`8H|GBsZ`totF#qFnVEvz70@MF5k(pNr>yVEW81qk-0^@(kJT?1Q{D|?t z;zaD8o`yW<|G+{!PItf;m?svSv6WLWP}t53F;|D0?1|?c#M$K<|HfLEfx}qcMe15i z>p-WA{eM4=a)IMNwav{ivRHtS`6e>Wa_^M87jMy;Zx)P~$%_`mu{OHdP;xhZ+t%sX zq+uf7)$Zr6t7N6y>bFrge0OV8?>JekvTOL|=#F0SP2kPl*+`wj{Mm>}WAo0-Vb|m3 z5Wc(P<#%zz;m;Z_6yz~Kgb(hx?p|sT(x)5H(rqU3@(aA&GNNbLU?3CV6<{(H6A962 zE#p-dROlXjE`{<6F-g{2 z_t)HiH(gP9Ia62UToYs`MwMUKqOcM&t8CAq8@#ziMIlBLi=TML$7F!|{%);_f=ORj zAEkRe+o!u(#zGboXXCnM%X(ijR2E9D8!;NumB3t_@jpP98X>FopwBrykmNR`oWg%43k9)`6=4Y9TJXAUF>kWwy zKx>_Y{T#5FAsd<{yrtAnV)@)tXfGhiY)O18`}ps~bqr&#dZ_Zl6ZN;=YF$^Jk$xgx z5f=~j0(tpp?nr`YLkcdm39{}^LmZaw?z+6LTSD%3&zJxHtv?6i33Hx> zp*hy;$lGN*Q&Zdv4<9c6Hfa$2)`A5rALxbU;J%SvO~I!f(wB1QgDkur`AzhhbeL}+ ziDum#Wf(6A&~0_>{~C;YM5W8U2hcBU=3o{gsoK zd?zDBX&Geb#=g+WcUiN^M;4uNaO8D3@)|DICO3Oi5+9vul%}8YS#93JC@qtYjF%-} zp{B@d<=r=|u{{;useun)!;!_heeuIb@-r?>U~_R~8KR%{B0LNq>ptEq*(*hwnChe? z4L&LJh&p3ROH25=6v5ZCo?EEd_B`MRU)=b;tMK05dKN+kL!HaRsy?BUkdIqmJ7XQL zn(llJzo75k3os=r9f`RNNC|p79QTqh-g8mIxTnTB@K2>M|J|G`0$;_p#*kP0E;nMX z@I&Rmq5x>i=+l=qzM7`*<_E=p)Mc*rqOAx%{w897urf%OPM7id%ZTPYrvjq=o4M|; zB%kN;@{)dA;@;G0b-p*q&i(S;FxKY#CV%x!acyj02Ylm7Pgk*%7*vuBuR=S*>5OMsYn@-yEo`3sfmD-56?U41}8JMoAzrrz+6cEGnrls&1~o zsaa{cxQJlAzrLYu>#|m3GsFu2F ze{F^MpNNMv#u=y$YcNj8t(U(l>gwkD8)k{#R{M+m70sH#DW{<+m-wt%Elth-P@uUY zSa0yBjr$imzYJ@HRC&HC6sTwpsL&PhEV}R;EJmw)8Gz?8U( z0TMP9ff61Y6@|@#InDlHwQ!*(s6lI`_OiyrK)#MS8L_CGSAxxrsm z5em)qS17j=tXCo8%k%x)fTKBfe<)BDYz%64Z%m=2uK@==TZm@XAph)&kXVT_eF^k| z`ilB0En-y%o0O#m#Bd`wnyRM%i#}a+lu)6w`d|BK^@HFaP2Rd%8OEx!z0xv~n4^Xc z+hRHW#|X+)Q_9LCsf%VwN>?m)q+p1m;!D2S5@r1QgOZ6^{Hv@Wn)?6XEOq;^#@5DA zLscM9EvncQ5X<}5xk-}&y&{>IWd#iiom4c*eU%kWL5(aU5SilAqpT5_8bbcyEQ#9H zx`zwum`on33(gAa$Wh4A$Iw%kdgUf+Lc#in1LCy(vjU;2+KT$-ippSJP&6pyuL(BS z%PdOy{f!l&=AbBQ$lur!YHZN0L`*Cspsh(+tF9xDwp3`N{41jlHfaJA$touN8{MpG zsBdZxHY=!DQST2l344PQ;VMGSU($j425a*8r5UXnsqtTzvPBpcI*P6AQ|`uKjCOy0 zgTE>ms%n|l)GQ{ZiOc+{F7R+govd6i+3ebY9;E%v4WVX>r0N3?*99L|3ZvAtqDnc; zh}YKwP?V8W1{5uI0b?JcF%W8MsE>H4U?@-}ljwgmH3q7LXTka^44%tC!#gNAP(ZfK~U z9jwzIZ)$F6Y^-=#I5exVMTn}Y2-dZP)WowY>S|i*wV)*J9RKU*DkN;<;M1HGULws{<9ywF+J;0Vo#FY*D#{@JSYIK?TbDSmdqSNnnoh zI|_PNX-Rd(Y!hEJX)wk@VpGGcfPZF7DA-gTtkQDF>IRKU0r>iuI#5?jjauISMhTi4 zG{!`R5tdrU5uhE#7qR2PDi3OGHsg&r>R`S0M6gEP^S|6fQQoFappC?51w$($@iX8L z)YOPsMPfv>VTv=TRM8b6{3uv%Ue>)X>&V{yX;k|Hpu!HN+ZfuR((rf0K3Zu|ikwM_Yoe6?H02shF+E zFf6@A0=@2uE9YvMT~k2u#lq0J8V;;!2nFO0ol_cy{CB+4_4{A3O#AohV3mk!ZbM7R zY6ETgWBR2UzZka0M13ovI;fd)eqNN>h?miUNkx?&$G?=F7!L&e6)nvTvnrZ{RSNYR z9M-H=ZAhq?_dhC!zTnEaZRj_xwOU`U=3F&zkURv0uG`5(6rk2t2IqSLkrGZB)^)OOrw-ibslEEB_SUQWn+LnQOt)AljTuA1DeJ zRSFK7RjH5pZ(NpwGnyVYHPkfEuF%3$ZAGYB{hgm5<+S~+f%+CrB_3{Q4TS1ta?x;Q zfMz&rY6(hSR#%~dNpnNAK=l2N%1}d#7)V2X-CP5mRyDMQ^fWwr-r2h5Mj?t&peb0b z1;(J<5>z24vTvpBnTXOhhJv#yLUa95?R!|DwZgTBwP zRm%J7;ZBp2w*RLG!)6PJZ>d*a!H|Kr`=;GcZ$hUg^94w>>8m|p9al6(dmN3ffadxB zErMxiX%-G?Nnuq(5)clX9}+O9jl zK;lJ1s6H^a$zKx)G-;`#sl;j@uBIVW75LAQqbU$jyW$20i734&tnvON6lkpo)@gMH634J}R4zlH*$;ri}@2A%5y5+clq zT-8utr8^#_*1CGmlJKv+HIP81@8g+%v@I3ECVy?9PVv1`tW+1a;-Fe+TNhj@wAD0( zW(BG>j}?uB>jPDRrY3PQ#)f0C*>4>*zfjvK3>r+)V{;T$T3)IAVnbV;W=Y0c8XE&4 zEghIGmQvAVyw6eZ395sw!Ri(bDOczjol>+Y;GhRj%?WB+GOMv}uAyK?$p81+uQH#= z;9{xTnv`eKw!QRem0#80rzuAk{ccVCw?T=7X2Uf$)J4*51u3-bJwl3wqs9XoAA*|Q zDfZ~;qzV%H-)gVR*e%okO^;l&D(Wlr_-UxTz6OK;m2>}1$}1BDwHM{;ReD2>zb07I ztn%C{8E{~pnn5s};jLSm1?+1(FtrsSJ(dN7{>ngopeCr&zo-$d6y*<$?5DQq+9R>m zSlE|dF^foNSIlivcp^f0C?MHQRkQipQ$uyeK3BBEtDLLH`|3!jRr`e^7a3RW1U3gk zHGz=6y`|;;p+Hk(Lw!?Fj6uENl$Er|Q~C?2w)Po`Ayxi@yu1PO{|aUDkb-Ujf2g4; zpe;(*W&LGbwKXMCh8}r=thrkCrcK{ljJ(fkvn?kUXnooFWe_9Ft+Z@x8c$i%wSWEY zMf35-`jmwKRsDxq|M!1K;h+BBf2{w>A2_I>prCF2&!D0K|6l#j{}ccHzoGt*|MIKw zB4p>2s@l(5>*q0Q{ZcHoei?w+iiN7muUJ+2l>j=#G_lP3@L1#FX_kj;^pe6>%fBrE zbn7pW6-l<{2Gy$HUQs_+wdeO3epEH&cZf4pz4dJkHL8KWFH%0GdgrSd!k42Syly_o zfKF9XrM0DZD%;nZ&8r7h19^|(TD4yB_S%Xjt;xGfE4b3Sx$E~&0Q8z~$+afzF6~{7 z);B%IvgNO}LXWzAl{wHBK(FbPv{vGgfer!MYZO5z%?Hj}*Qh#kYl~DpwatP~t!uVk z)TtU?YmeJftnyO*sx^tJPSd7*R4Zw-yi+xV#saOOGv;e;oYnu<06LY6*V;CVbW|s1 z>ym8qqXu3X#cA6{K$jLPw64W2ZIesuL2RGf(4w^y#tN-vuu~NwY0ZKivxC(Etpl)I zFG$i_{OT*R1E9TL=(qa-y=IA$)^68p7Ak2?a^s@Qm9+l05xVVDG%ZzqVr|t8T31+& zsrKe*@!IEW1lv~{&#VUYnx$x3D^~r{9Z}-6?=i(`KMH6I)@!{>UD^())~ysPwYH<$ z(ANOndg+?hVAL1c2Glx*I@M;N*3vWj$8^h&#ehz=U#K<9bhI=I=NuCZN*z<9D0Nht zlscLLu}W(%sc3R6l8MYHul15Nm*Ci-D~e;gIrgsTvG+yBKCYZXwbn2(n)(YC>Tl~) zJHZ&!=@QFTGSxZ`Vzt&fP`l4BY}3l>v?hW2Q$}RoD%>&MhTtPZBHZ2SqF## ztrea&_8dUBUWTVNz0=2D5k2;r$k$KIuxoAYqQyqrgMt~pQ3y42Q_*0U|5qt2^Q zw&lEQu`QR+Vq30c!?s+RnyR{TBevx#Fm20KYPBub7}K^~Qw-a3RYu#EYi6`szFZ&ppW;C0hU85&d_I;3ZhN7lJ{m1+ z%~3W))3Qa^8TVd5horDt7aV(w@Wp);(9smAtJ6B!XzJ!xY^bRbX1OmJ0m6MlZmkgB zy6+g1c_4~pkH;k0lVS|#$!KF%o}7qTdCCECfYuPjP(;r}T@gLg0bN?vgVxW)QPm(i z;|W=1>zQv-vt0Nd5PS2iwm!Nc^5}Lzr%EQZh8_BYheV@{f=R96hC?%4&jo{+tLEJ| zq8EL~T(s9?aLbzj=+a6nwZ0PVq6#dvW)Vs*-ch0m&03cRjbmPMi4`Ig?-W3K7`n$OjMPq(J0y;4=fD-4eNzqMd^0Q} z^wpXo^tBqdE;Mgl615KB3UeL4jet&78mb*@c1SX(omF-ODynL=lgI`U$BdCrnUJ9bnB(3 z$T5uuF6NkpfWAl>Dss#+-5bPg6j^8`smL)~G+D&#vC1N5Uz99jj+tzTIj^%J=88$! zb&D)wZkw`*wdr0gHdZ{8{E;BgG*S$T9GeX2iS$L6VjFzStvV@HdNHLH%0W2fnB zj}3{hRwb{=MIdVmD}BS}%e{j@=?ks!CyzWB2Qfi9Ke7rPz~(sm7kw zoolQhT}@SDuNeP!GfHx?cNFr}OJ|YWd2~YCrM3Yi?a~e2wJVNNp>}1a3bmVPkl${a zPGGxgKwqTP7P;L_bDV{e(r9J3$nBQuLTb0x)Wdcg+VZ~L);7FvcTg8tyEB?nv^#G} z2ije1n^&~E5e+=!Tn0IDDbXN2F5NOvT#-ps+$c*X#&P3yhQ&=c2da+ZPh6eJpST5p zScn|A6wntb_eG9du3$pLEabRN)>sg?JrYLZ_5-^0GGOGmqvm{P0iCMw7dh^t&c(Rv zQF4yErNBoBxxK@9u6=Cex%P?rbM5_lO3Cdr0b{Kt#K`S)^~JUyV-7MtYO(Dnn~QB< zYfc$5$DC(Ovk1_sN{*4+3pXOwrpWDA>rkcrHt`f%5i)Xn!92z-lIqM7u-N{%XqxxIh5?XMZ*-qT$BtOn8Sc!~K&=`wPBykJqSfEhVH-IPpxf$6E@ zOQKdCKiXV%{1ia9zQKkZUn%ozTWrYjAwZ`pbw-X~B=>2XZpiT~#7fMqH{^H&@yDC7 zEq-5=MB@*MHfSXi$nmFjGl;(&A@Z74lj3g!I@LP@9Eb5QY7A>>3fI0fU(vxY~&7yHNmN3Y~&6Gvgr`*<2u{{bZJEv$O#_Z5hf%l z5U#(dL{3PRh|sM0>x4WV9tcYlWUciL$O+>tKs#ZgmAeTug#R*ZBcNN~!bMJK)!CG= z#00Ae%MGxau*L+d2^;jqCv1&i)r1|!)B7Sf9t4cBl!7BC7(qValEJrxt5!m9S~7wJ zj&QVNtmf0|kvk?ETGlZ=YD=VJw&mfDCCV~X898#tvAW82oT`aY^UxhB9hX?y)N#4K(2na(IySY@tB%|BWp+FmCEbok1o^az6?K%r7nEBB)ikvT z8tQm8;xszmGIE!W0&t9yb>u{szLZ3h4~c0}7M_@CT6kiy)$kIHv?p<*X?TfKtg4Y% z88N)XkZyR13sht?i{6nF7wbGoT(4=hs?;@(xe?G9ec$FRlOQ=(m^AY zN;(mZSCYw#48@=jBo4tTtxY9Ie`&w;8~)^8xeGBa)Rw zjwB#=KB3a(InBtOuK@ZY#}kk{-vo3w8!kQBrBjleq{IB=)V5lYY{h@cLkwvok1@3( zc>rM70&o=;ivbp@^6>XFuc}+walDC`6ki6HN{*Xl(l8@=e zl6+QC5)wq5d_l9SBEJyA# zRXt~vcp`VHHOcO>z#zLz#Ok}O1oRm1B9Xh88@pY$M=4X6-KH{iIRc1-kh`1&^cdCa z$X!nB>e5A|9XXDH+~vBVFDW)HkI~D{kyCv7Ln;2K!BR4#Tv$qRV~#iYU&nEq4i@g$f*YaJ;vb|kVqsA|agPbM-PA>>TPTORXw@Wyoj`kp@3BC^nn;t<~2K;?o*;f*?qcZ zUd90;>7g?!L(wZF^+OT~^KOzF*H9x*w1FXM-KxFGa8M zYGjS~^fme&R?q170ea0tNyz>bi_Q9TBgWu2-_WyvOdBrvCjw$IvcI;C!ujj;1^E{# z$YQFDf2n2U_4=CpTLIfeot{AU?=Vcqf6Vg8DdUj~fKFAvhU~u#=+X{7A^Qc<=+$1x ze%9Sek2qOHgD|^CVzlk_NCWgmj>sVQC;;>rRbt3J#^_q!V`?;h?=d5i!t`i1w5`X& zs36f}sh->PSO@6Rj!+@@*r>~`#|{JZ^w_QKfi?;Mdh9n$sK+t$hEsqpt=tW{$2pa% znU_}u^=n72kbB${m}j23K~DGSOGx(%r>*C2kkd0If7L2rk<$&uPM>H@oIXVYx^XxQ zIeiA8TR)US9fkgSPz!3)2r7djaW2 z7N35~{4u(2Aw5#JkbWy#u<2xViaq1o=x9&Bb=^IS43764t@ek-ll2_0Pv29l+&CbH z+;h4)Y0pLpLHc1au^b{p?z!9e>A1o6o~H~8 z=y}e_ZhBsdY*h5Trb4rE&6NSeoQ7I@6#@Dp zhv1NVjRLgSRm?{2HC4+Kv;%R-y(-PklU}WD7}sl|{MIl_23P~wF6y8ha<7egp+~R% z2JddW}^N~X7-m&Ju}a$A(`U< zZA}fxnbSbCEovAaYccH=A-b13CM(=Xd zetS>S`PF;6=DvDYw@E&G*G6tOGq>JL&3w1_N^=vl_d0!y&DN|@e%LBnrw$Av_dXB_ zXuXe{B%b|p5bS+XXJKywbZN&4k+W>ZGg0|$vv`W7!TH&T9Fm?CO&Jvb*nFHIiMp@r+!!uXs;A!o3$nKY}Afb)?S_6Sw|&^ z)a$-1>x6aRKkEXZ*E}h#Glq|+zIQlrwR(ER7}Hu+JX zu{taI%&_bq^r>m10ezYy+Yfz~MarA{tTD@*`j`;5PjtTCCz7xCIijz<&q-t2Gk_lB zY#ef*^M>#5b3+$G9}~s)Axnmp9fjqx6Lgf5oo?Oy%g)k@>#~b2NpNR|E9kNea)gk+0v^r$pw5mhj=w!ZcqOrcdCXnx&t3%+vMbRl}-x4jM)Q^86_nn|8 z{e5cyQ68<+LQCJq=uM5j%|@W;yVO{E-<5#A$cZTAzH0?ZRLqv=cj&C>yEhWS`yNn( z>L;a;`yMlCzhKaDMc=?Rk53`@y)JlH9il?c@c??w~ z&S;&Oobd+jIg>2fl{3w%T{$y#eaM+-Ff?bOu5dX^tqPa3LQ}Y$4HkvV*&qmY7ubTWzJX--ZZs z>bFBAWgMGD?za!HUDV-OvzkDv;FSr*-x%bLAv^fMT>GhQBEW`$q;j{ z5dw0}9jsjI+sfQhtC(}g>0-{EX3U&hsq0eiOmls?jn?($&eOqv?lNO8b4M>%ykoUm zYVHPe$gLVao}~e_+?{%IoO{T^&fKHXKt5M5V#vJ)=v2phk#ldz-;#(T=ibx3NPm}l zIUhOji`+j}9E*1B7rB4*c4q$~tF%Ie?_mAx2_Mc}gwEyBL zkKcc}>GAt-Pz%wI3?uj7sx74dev2mcKWH+y|B1HR*Z-^rL$%Xv$o+3d6e`bQz^=Ub z2<*yBHuN^qR+fotRfW8=DDyiP&EBz3SMDCteX z)aXZRwA~QB4y0g?;aUrpsd5o@G#j~K6`;pBpp9Ix$&f?A?g&gMFjM@3qh@FM7W}-QT0S<$Z0m&ML>Toym0KY!ofC59}1B&%GAwj*eXh4}k$biX! zF_xp>$OEPu&$pU4%?EU8hry8tEEf1{*ua2QW<{F*_$Gaic)(u0h)f+4M;>s%Fw6nw zX2yVXUk>jBE}0d$16U`e&;#gH2g#8O<8(Arm=cX<3jHb&7_aXO3k@|d9Bu5U6dD^E zg_8{u3a3R`Y+t{B69cD2%+RD2#ejQE2RJ6pGcW z8MT3wBuFxM@tv|9>JH9Y* zJs@Tw58SB#`zAo_gFJ9Epglh?A9>(bKwCh@+6icH3OtHD(5zA(c%Ti89C*aEm4Qah z=D;fv7dr5IG>ja0*I0K^?3dN6DA9r*iy~XxMWt3KS2QLfgrX^m_v(N+a#6KGXi;-p z(G|_t$tzkN8F!sk8;Ujy^9qoQb_=LCPP-!)iC?TywZ=s!bgxu&*3jUh3l>LRWE@#3 zx~=Ni)G=-3qI(8mgM1OtH^{(*gVIcr24z@D8kDOm=pc;)0j-*Q&?xI($)HJ5fnZRz z@!nkM7-Tw%K}(dQF&YLS4_X1}RBZ#02W^h zPVszW&f+BzN{Ux#l|wCBVSVvBU4q5iHSbt&=vDD969p6>HFwmCPg%rUe7+6w*9(Hv)Rie;R^8K%Go7g%+Ba7jcCgU4BQd$3u$GPt&_ZVzrW=35#W zcZF4v28%DuAAme~D`2~*HVMdscSN2#7WLF=t#(&$oPa#|vPr}}eKWZ(ASil>EjkY# zlBhAa%78dS%w5hQrB-)2WVAY^(5zhVkjZ*-CTkf|t?#W3F^f@$%!x*vLky5NBx2J; zqHKD|W<4Mc*$wEZ4b)X5580>d@sPv1q7OL+*e(W*^f87e zDJ#?(IUo;BHL=uCqXu_qvB86(rIwxEp`!u4W}64(p_7dJYt7xeq4oL^jiGY@W1wi!p%G+1^oW&XLrRs|vAsRaJcT#R74hlA;*6hDOlA%9TbVU1+rV1GhD3-TR&MxO6(@&{H>5CZ zMx^jjeyBB!Vc0y&a)&K4@#(OY#w)^xI(cB7ZUMu#w>d>NY?q!s4LhoHb=V2x-qR8O z4AXG`tXZN`!>$0j^+Aw_-8LCi>M#f{jWgC)nxszrX{|MoOVf1LltvnxxQt1NA8_3dS`hrT=M!kwG-Jn}m>CQ+USgDC_N)M?L z%#h)XOOF}DTmbY%8h9X=UN)9;&-4<*!6J|0KC49xPu4j-JS*z;?Qp{=hL=UZz8yZ= z_+hF@B#1nGhTLH^>_8q~qf;<^9-vdT13@0XP&@EhrHCB99MCo}4|(`{W3)};P}E-B z@U6!4yNu-z-wPOHX&iz){2-u9YafC<{H!s`6$$u?sOx~fNJA0i;kToyEHg^C%F;|e zm1S7?R3;qAABbF55~(OHE7!$THr_}M$|f1=RW{AYV#}({U+auDlr=|X3}y4pjG=6W z?v={c8m3&P3b`r-MU-tZ_AAPEMvJ0suZn?2BNODZ<2oP8E@|}?MpF~yva7nBM}Vo} zBV1PF9}%xVJi>3>nrUQ$BXSH!G@>vn%^gvql>@0(D99tm8?=s?p)+bkt->XGa}?we z^`>EtSY*t&T#r;ERsqIXnx-I+SZkQF<@EJQ9(l_l#fSdhyzwe!a1#iAAsO@Vsk@)ARE zzRf&?R&LhzmG5iwO0WEo zx%l$4*2R}!vVz(2TY$0FLzl=SZRV3BQ>{;q%rw+`WRW4>k)^uCMvjguz#KWwI?G7& z7}Lml>npdBt=1P6BbT&c%g8mlX^z}(;P;Wcq9rzRU!;x1$P<96_g()Dnja>o`k0sei0rz8?79wg@aw<4ipcI+c10# z!zTe5ry}DFAX=WY*p7_*kntcO+jk(_cgr6qk?m)I9{;G65!to^*|r*}_d{EQa(Nlq zb`9v@!RNmz1YJS4Uk3{Q_WhlR?8!y;6auxrs z8}opM9*>=d7(R{Rb3pg~{@yklGM>`9Qm}mivVAeo;qUeTxeeLA2id+~SQCfrNCdj~ z>9Y6G{H8AT+Bs*utSA#1b7ZBZ$T&*wZ$);@2Rib8Syvf<4>HO)co!KN=c*)bOATMr-owLDvi?5qWP zJ?lFk%Cqt6l=v661@9teVh&KuAHV2s!|(;wQzU!?!?%GPzu4b|$k?nJdeEbDSI5gG zBikH6U9L<$h~{@cnGnOrF?6bAlK1K6O^z`%-&u>NZL(H^)=<%1g>xt$UoN3;L)sKr-EJel@Kp(DMbXI8Ik8C>xl=%LiQ{?gvvWj+QQ*!qKG{1UJi$@HU1?rzHr#BANziPwqLe(>gF&-I{fd1C?FGFn@ z-j3m2fb2Se>^cne`&~`q#O#}q?c0EE&)b_X^CYW|5A>T)rWK+2A$QuJF>Ai8U^+5Z z10@{(P&}S(nQBl&?>zF3cnR+iWN#TzEZqB}T#iz00O;Tw!$!);+mYS7fZp!hUis1~ zswD!A{!_*-xp@k*cREn!wo#%Z`lr`v;&Z>6j2OO*;cGx|XLtfa@kQ0oh2EOu9VU#* zMRpbfy;l33Q!>p0)kuPV{85Jyam8K$G6Z8W}1B*_I8Iec&(KWku(Z zofmn}SA&F7Kv5>RmXF`?Np z5!o>XsPij(#kaV!RO=GjPxZTP$gVBOuI=LKqVk$s8 zGH3Ob%L8KjKp%JPd`T{^AiJ*vC9OLnmP`sNWwS5-xcG z+3Nzz-95YoF&(pjMjr^hAWPef?AQi0?0X3b!juWfuE{`eC3O8ZnqP>gw_jZ+zSXq^ z*|i+#2UQs!A@D4+^8(P0&wurj+?<5$O9gr|xPGWy<|6wFfkyp(;y^S%5>JPoKKxZN zgZ0SvO+Z`Td8n(9u}ioNba!Uq_hsZbWM?AKDZXkPZ3p=Wjy)%K5CV1{c zoPd*o3iEm`@L~9nYK+D>9~l?P9|w@}u>5fX8BYs9JB@5VCnzHU*_kYV>_T?#6(>3! z*;Or8b5rXe!=B|5*?{`^YUT(J6OmmhKz~Y|FTT*T9oe%BsHC>;MOn)XWUK-D?e>4D z7;_2PeHG{hcS50vv0O|L=;xu|&qVVh^z@|nmKd0053*xF(6LwI!?JL{>c>PAS7r%5 z@Wdi}5`ap7<`*8i5>)phy0ElbysYmOvhOU=p1m)v7BW(iF&(JaPe0x#$guz!i)G;x zknNL!I)3(CcM&2ZI~+h?bhrIY2*^Wr6anq(`qqETjCYYe40QAUmG2>j?_!vN96P7n zkqL?grve>5|FED5M;fvt187d=i__$ikexQ5$G?*-0nn3*>`4cDv+wW3OuP$`y-R>T zUw%XZ#UseR<3O1n9xdWOh-^Co^tk`pZ6R^1>J3Imm(BaJFl8IEb0^S)rFVUDc?Q{e z9;o5SqVLJPn~)t_frf1!C?k6>AbT$ZZL>LF7Md3#;}W1Xej9G~67y>kOm%F{Jr4-pS3TVfVItljnj6(K|1$yU?^92BU zHY0ns0i}2U^cf*wnQC)GPfSjG5HY+O!|Q;4_;$HO8RuPr0zj>m4~`X&y%pKDL-bCt zkq7AblfF37pbg0G%|I8Qn)*9oRyMLP4`}# zWcLN2M`K?RaO#{$eK_4wb#ReA~8X9GI+>9j|*-inmut##S34nu%pLPi_V#M=#j z6~XOAwjBWEm^85=pnk~@2sU?}MRr{PDt!2W3N$ImENZ zqH>P%!xNC*lY##9$#?-=p7F?@NkFkl_Y2Uommu5Aflj48^aCfdV=1y@g}8~8$gVZ= z$8D|E7JFm0c1rAJWS;|Q^*@gZ2z6XQc3cKJ+v_!u%WKGvn?N6b@9E7l`(0!=1NGbX zx`1?Fmg+u4J68;r=wq9RY?}hK^NXF5HaoJ#MuBF|s1wdRLdcFeKqueX?2!eo)w*i2 zZ>Q=UL{s)mllW^}g=||3^!t79?3F3fWg4Kja%ZiP%VuQXJc%s%f?s^gkbNtG{wphQ zoLm+m`$~Xz{j*sJa88qq5$L0F<0UG&LddQ;K;EwBRl2hX*|ijCabA_ULiapm_d=lE zSAX(%8>V4~_@DX6_C-MZE)5PKhF4*DEzooOoMLzG>B#PCpy&5~woFuF8?tvN(5Hi@ zWb5C`9=;hQY}qd$+b;v1|7=-_u;CW6<1SFu=`IRmEk<@O1Nwa+e!a-^ujK_i8 zZ#(D88Nk`$C{sYToZBmxojfMEcYHH-zV&5VB_u(0fn4AUU5m z2iaQyv^s9Z4iUjZ3GzS{YaS9)b!V&AZq)9%_mbsuKeGD}Q2SM##UFY@$lf_Xzbw7D zQK%|Iwv7hr|4g<(S4SqYBM0c<<0TT6T^EsESAcSd{&Kr0TN<)kBl`>SU?Z_ z?3aS!JS&ksYkkX>~^T}LXBi-CGP zKr+0zX4hhdL5$jxcUt_+~Uy&8pkBXwe7g<9 zi!r=Rz)T`CrU1o{og^yeSd8pg1{5CmmN4EuAKASK=p!$m5jm`oj34N=pUiw$F88U1 zVpREyUkF<8T|xF;2m0}^ejtpu6SBhwv~$U?1qV7-Av@LreUdxuQK5CBYI;a7G`ze* zh+Co>+7bS(R~M200c7uCp!T63KP@9~LH2D2`t1+8ij~ATMB9OOz5U%^3CrSuR%G9Npz%9z3QEBg)jF4Uzx#br z4O=0yZ3s}8?SKBKaPWxg-$~m)TP={$xdz#}9w_|g1;H?mdB~21K!@Jk@D&+(E3$6~ zkng9h;(zkvRaMIAVlTQfEGm)_o=*QD<)X7jTWLqrIvMWE6+|)iE***zqaJ&DO zxZqxd>|P3Agq=T;d2S**?f|t6_~GAW;JwJs13>Nb-%zT*Pj#=PFPyK4PqOVqw(S90 znQePPqW4j)kuBS&BipNietZ10lXBZ>)y$S&9`>xnCEFRv1b{wEx?ixdcO9~KBhcE4 zXP&lUcn5}eOYl92>^cJUbhSqX>s(}eA<)vlUR73g0@-yM$XWP9S&_RA*&PCkIrRt0 zUvV`ut^=C71qR5HQji^fpwv&MZ-_;9EEmO>#2`=Wh04w?$WBSAU8|8@>*SBS$SxMB zGa1=EO`PF6WY0$V;|{Wi1PsI@dz0jkI&rx2$0|wuB~06peIB6qKLtTA-lNFg6F_UG zoS7~397lGX0($j8Q@*U_hHCLii)OtqV9h;6;w8}710OWY5J-m&K_kbRH>vcgZwrpfu9#HAhYza+{)yR%@K=0Om|4%YS9kMe7^!|YX z@5tprWakl}Z#?>)PBKLSvacBEPp@n%5R=V7_GAO~sCl!4(6bBKyB8?X{Q~E6--AwwN@z&y?FX4DZ6QN{lezi z9ZN}s^#*M#89C7Kboet z*)~b;2lT5SHH+Rjrz1P7fzHK0vrUF7MD`5<>aeSSjyyXF*)|nuXDt>XITB=Vx7SN^-7W_eoC`Y!B0eaPw_oVzfAKAMI=&|PF{c^cN zcnS2=#*S*ca<4>xppxyA)uv7>GR_D3<5wUM}R`tl71{K8>?D+)BW|g)rLjA>fTMbwon46 zqYT+G8fa3upQJsmbAo??9$q{8ZTS_D9WJ0(-3Lb^hVLOG0Cj)yZSfSi1R0kDz4_-8 zlKtUkWZVYy^SO)VQQJDz>7DBK6+D9&K8fKoK(30^zX<)Q$li3I!{3`ZLkFU54yj3H0>W=U)&Ka*@4-K${1h6p(}CkZ~f=zKN$K-^36y z&H-}P{!t=@Jxd}k(9Orkq>I~WM0U1{SxrawRRjI4a|cQ5aH9Gu0M(pb_&3ow53(l? zsQ8Vv`{i*C>n9vjyr{=}Dp?DGR9)ZJbv zm#dI{Yk}+?Iy^3ymymr|fvO*UUTx}KLB{Jq=!+K&W-CIrl>kjmnI$Qu{VKBk2GF?w z>U2|vnE2o9PJY;VX z(6s@NN;c*Uss8Eo{LP<=1Hf&_xD#mDr_1GLTPCtC2k8B)-@#He^S}-N?94e9UoVJSBhJQJ+Piz%%w(8BHwG1N1_VJMy@#Sbg?@9(?Nh z12T9PvLhGh^+)_aM4XBY?FiyM)o*>lHcFiU4(HQ z*>?))+%xBYEtmI@V*u!>E`K_TI0nZ9O>G_|S&cmb*`5sa@bAAWf!~>;y4zF6j*$ZJ zJdMbnR-pcKH-90+99E5#>05WVmddlsk?pI1o=RIFIfN@mb$_S(mXDV#+dW=2K&P31 zfAj@;)*)dWDF4Ybl1ICC3+9znuUwTctVecl0($QGTMOj!hF~n9nY(}Vjt$wq8`&n$xZ<8@@;Euh!B9agms(~)h}K&ujt zizBo#vfTmn+CPtpuGovk`vF~Cx5h4)8fJQygKh=h1I9lB9SY+D-pa*~2-X%0v zBfDn;z4Y{B!VJ%9fwDmHPp>W)`Q;)z3W5Id=}Pr#;tsN#fbL%SjoN}7hwPgOWIwe_ zQUlvkWZMd$?N#wA_Fhsg$?3%(c3Ces-$eG^0s7`27QAW0avUSHj6=3f1bX-DA4>?p zYDrDti<|F1A&6&+_OT7NEkw305xz}DcFX`;`OL~jSzU$%SfI)K_bWJ6EfE^%v6YL( zFfbWee538W?XX!18QGf#qRGWwJf-kB`IO(3dY542|yqekJRY>dbeQPRI=5=AJ_QvnX{ogeA zwQ1~(jdM__FT8>Z|Ti+PrvHu=^;1~{l zcr>LrEX{~#hKP3d_bQ3BTNB_* z{NQ`zK{uvtlsp~VfbyF*wS%X6TIY`lx=u8vP1+PcC_e{tDbZG@y^BP2^Mdy{=Y&U4 zOIh|LD9gpyMdS=VK!tO_8_bv{P98wCopQ_1EX2?EZ8XWyUfE5eS{ zitP%OGXxouOp7Lp`%}hA7nCv)g-?p>mA^p8I&9LOb6-y-l`<#mB9d#tvl3i)P+Q$$ z``whNt310@`)$|E0m7=Un{Yqr6~(hYqTSAqMxFIyhO4YbE%h8dq7$5|K3pW|J+9wM zViUh-S|oQY^E>IKf|$O_h81@Hbu^zbiQBIuif8+2cu+PQ?*niZ4t(EY6vR6Jn%en$ z>5&pJ`DxF2l5lg8i5;kPdr03rn!kE?#Ke31=8d`e8^C-Bi}QLbfh^yhGT=}d!P5H| z!dT_^-A=0alzTug@{TB*jua>NWCdz%`sra>@=a?XyH8w zl6&1$)+B`BRN)>P01oj1mXIN{66xorXG9*ConAN z+}YDM=h7_gWe#+Uo3EScp7~mb53`UmdQopUSmZbT(?ZSp_xeH)B>!zDc+X_HY1rip z;xWzVjrEp{JkjhKM!}7pnZT!m)rdOp;!!Nd{jXr%W0(d0q-;DWiJrJf?L40dy40lC zpFPV9A~8Eh3iq8$iCeNxM(16r?>np0*el|;tfx|<^9o{wjWXW4i~xQ&|H-4Dc8PLn z!Bg72ex3E@U z$+s+R?m6s8rr7mW&xTs!AYpOHG>wp1)05zZcqnJE$^G9U6!Rg>JNsSe$fjR@o?pSX zPBmeGqi3+A2i%4+EsUg(6I@mRpWmE4G=5M?Gk=%n^V-L`>p@j_H6K?dDcYt5VhGX@ zTC<;*xSvOCrm*#snf6um(Sjy>0)rI0+PC*$p$UvNn8;TBTOK&&g9@}i+Z$$e$LLL8 zdPLEpKqS2!e(`ygPz*q3DH(+xvEM)$5+GZ`7q+41Wzx05;=>Ry{CnhnP z!L-SQot~d@zKrd2dGK=0=i~Dj1`Vf5l zaDC0lmR0lc@dC@))`Rw+hry~d=ll_~;hJ-Y$qdOZ)5`e+ZX`_$B&emBZwaqYH+cf% z!>9{G&{k?*#7(knJHL_C^cg`(y9{F9SKiApUTviFEsRwU97^=o7T)eJkln9!ZWZ1+ zGhvy-J|;Pd}xry2eZs-AE#?x^I%YLl|OS7w1sbQ|%nPRrU= z;04@r{luR<&PO|Ql+PV~$PjwOyYLGy`1Gq$9gAHx8qLx+n;G90_?$cNW!U#8FZuc% z>7^_1mC~+uZQArK>~`NDbU&*f-ZF+M;-T8yt4SAIIOba@szyW?jQ*OxhySd|T#9x{ zAwuu%=C06QFURdK^3uWXHaYQ64y8ADi|#Ka@Bc3Uj({&Ug8f&PTlL09xw4-H ze(8HnFz>^*likev43NzyMU4KDpK@=&egp1EA1(~)x>EN)7j8NthL7ZWXoWBtZs8U# zNFAd20zP43i_e~K7Yo_~FmFF`sJo!AJr?78iBCaQyI_HB6F z@(M^F;!bn*T2O+qkxn=BE=04PGa$~VO9`l9F*MHp`^v`mQn==E>m#RaNiUt6jDtzw z>zAE#Nsb&%j=YmKTm`jRdG~%gRqUL#u8<2~aKJkmtG*z8kusDL{S5$nJ-8zA&Ts;C zAD$esW!myhuf1o+Vb`v@CF=OFzLx&`-nwfT>xw_WDDN-s>)o)zAbFhw8#lFcS0X4U zJ+JXsU&eV~{GCWH+Z&02V-8A&NekIvehuP>i7qO{D1G;cf!Ep25hTuk+L>rb*Dl~) z0F>9L%Fp@^xJ_XAow{~Add~^6I5_y?=yEZ6haU5-OZn!(zz;cU=O;f=Hl;Bd2tmQ+ zkUR4scsnN+s=$xI%uT@(M#F;|K)azQXC;d7E+ZTha^_rdn?;Is;v{58>P1pHUWE~} zd#%tvdmOfJLy78|*;>!q*GUV-<0uR;I3P7pYc?c8v$)rC^l2c@%fZ_#g_X-5gFGD! z;oT>fx-SYibd7Y+!eZrAlfFb`v{`_m(OO!Pal@N>~aQ-L?ms!peOIS;Gri z+V&m&eH5Es(Hr%=;oyH8@O^Ly?-nh>RolMu1AjlPE{KKKKoIiCRQz^S^i(WbnEIkoCKxTD52(fAG<2 z!uB&y=2Q-gFgHDx56e#uGiIoEXxt|PsibrkUyUG5H)w|vXungeghDkJlUx=^KJPAR z;?hkcxHEMbDQPs=B?k)x8f}f8Zo>wohdoyl1Qae94O)O5+XP*I2MYP6P11VRi!~1S zUp0*~>_)yd`|I$b)nir>Hmg>=vac9omDQ#_#apaUG!S|7E^!}9&@T12C5^MqE7c|% ze1RPh$=Z7twf~MBRhwePXYBD4*s&Ylesk_vM76z&?bi_xU>%QadN*snd#|0DTg-k` zXsLL8#HX?Ss}1h$`>;TyEiN@u>Z^0eru~4KuSKPZnZ*P1p9Kk&42}>O5cmfIiKWEt zfs&S|On0D7jl63w$sqMDHa3@q_ggHBBLpO!@DqF5_3FU=XqWICb~F)ts)F70_uKhx z9(eTT_SeP7v#uirmftSuX}h*}W2^^Zavw0u0(Q3F$37+MZ9&UZLIMRRHg{gJcZu9~ z_i@=DiAhK6!78R0j;@(v2uos0ifck`uL<)K{U~xT)yii>ZV8fZN?%IHv6dgXOf;Nk z${~D=kR-LX*LcQQo0%S6q)3x-KKTb;hC?&-#|nbp!HYt=IWP7i&P!!_o(XkJsb0ne&|#HYrS1J*=~BjlOW6v%>%aV zmLFyn71_@kA@c%iJ}rVJ(&9KQ+8vMlsX2iQ!;nLx03T;5tMkh@u=Q7&$+z`+SHph~ zVC%ku!Nt4hZx(|t;3Z2?iRHT?vOO1a^#~~TfQECe_x*{gpzE&d3YKu;)AyK?&(-zU zQ~2jEzmCzW{w#X9aNcv(6$}Hm2r7Bse08BOB|$)njL^#|904@OScVzw=P52R#f}ua zPicCC!ZL}qm_n6iRlY|nm_}{c&`tui2J{4T3fKg~mg|u{HFo5-rE2X@k^9{hinX66 zQ66*eiqV6$OgSz;9Pp8D&(=dZl&f4U?55D49lgigLPPhnHWXsf6UJCJsQdTDa|`Rg zuCN`Sa+;g336+-{KGXpjA)T)u6SYhkOaINzz_0~mC>>nNOaGP4z}y7N)@_dmq{T!| z8U7jf6trwiR?tH}n^=07G1Bo)&o5~z6kA*5wg0#+NpI@_iapegeN!4U3`r5b?TRTa z0_}ztpjXTSZW|6RJa}yR)Q2*cB||y!)0JRqG(ugk*7p`)msBF63XKC6y6Gg2BRTmP zmMT$NDjo}RoP>1JjW5fK3O~;44ba1$s%I|C_9PcvK$7)J%TUGLB)4-hEf*Jv7F3Evj;@m^()l z;=`QlXMnV`Q%amYha?KLPq~NeJWIY7X-TW@)JI~=-R&h5La6TZpB)eKO7_#F#&H>= zBYjswMSiF~sqmhS8emhs(W@FZ%pKT(uAGh#<1Wc)X&N>TSnAOFG{m{00SYOaO8Jo# zUzPPu@OJF|f}BUCC9FyViMxAbVcH{3&o5!B#Xtd(;IefOCX5TC+a~;uuZ>#8PBn%% zTiK!gTvQ!~P!&IPxWwQ0zY#{v^>vnooon}C{Z5%7FHZ=?jknBU!u7`J31a>|Ioq}@ zy{$L#jylqr+ZmYoKVw?0^0mW|R~aD=^Kyl*jau_a6Lf7T`pl4)s^Up`I?dASw^d0i zsOp)Nx+H&BLN2aUJ<9nCDZbW^TUI!7@ZgDOXRPxa2n|rNzmJD9PW{Mc;U; zu=$m%cmhI#c2uQHf}s~l*C$&34O_*>AFX_Qlg$^$)4|B2o3HirJXAeupqrYhSL*MV z`JQMMKtpS;+a+ErC8x*buLpeKW3VYlg}U!=_ug;MWHv6h==3J<-qs;)-Mz_szjlNR zwE1e(x>L^t)m=+HP?H++G@+Zwe(;(OHMH^+3jw9tG#A`-u~5;)Q9Mt5<@w+V&*z4|Gv>#^*m z>t7OXliH(|rVRCqOFHSl%$Wuu+iz@Z>D7n%GzFC$9apa7&~~L52Iz%1rZ~-NOZ0x* z*-BAIvPBzbK&I#is}#p_P`P`F-D(G(5mZ~mW6;}0kS z5$sFCmQio%%p_>fai~pCWL_yz%@{h^?)GlIY2zMZ;Fs|2UdOU;?Da8zF5Ecaw0=A! zX@r57f%5~Zv?uV-*Be2iU=O9Okrq!>PHaPH*=J=x=7~sW{M{b`$jAeB_y89VL#Q6| zaq#ID@hnI)Gl*+xWBzZUz|q}%(o z7y@e7Kq}3Ss&yY4`^ab7{zb`p%>2dAJ(en03B-OmUS8&An1$M@Yy-}Tfv|pD@O1a& zB?Y!->G>s285)`%7e9poBQwW2IB)b1_qXK@EYN{(UjI31QQ999?Akn*owtgs-@H^s zZViQ?l}mGHKA;#bumF`s4UkvS)KU?eJFq5h2}U27f!QvyX-p-^ql4A#X1F=G$LM>w zFy_wrmFwC>$^+xRq{WFnLhncj7j6Vjr0ZB4p-VPVvTd$ju&OeYp&NSC%DPt{ChTU8 zX6a%!s-(~TE(VodmU_qBU2fw0?a3Qa$=VRoz9-(3Z z-}Gu+#2i*C_bYqslSwyLX4d(CiXOVBFQAU=w2o~_=|8r-rvA3bqF|Eh^TuG<)9Xyk z_z@@D80R&k45gxpVOm_lqX7T&9cbyp8VmHre6>~EL>)gvy8+Vu)|94WyTpdwMJV{^ zKp|wsoZjcH<;1}yZT8To0Yf8>1I-{f*xD(gk;O9f&P|Gefq^AMt9bu zcPo&I>1D1be17%B$2&9RCUv%<$roNdSL|)GL44jOY#Tk^6SB{x5|ub?#ob!3qTSf(VD4bNfzj~39LxqcnN;)B&gicj`jC2QES#1d`jzA{WUprFT9#u zq$^<4C%=H5k0H<&Sx3XE8t|J@`fn~2i}bZ<(kV0GL_$M%4nzN}-b1nEPNqXss9Kf> z2V0Z%U>~RUd`CG(L`FEJQ)A}prC_BF@yv zapY7kcfb@nz0|WFmgSRLa-C79uw*rIK@DQ4F-KPiIxbudJ%G`_&*=7ALC_ozbs+eB zg!Jo!JxG7BwyPo?b02cnGA+mt7^C;+$vP(=u>(y#AxKOQ_RkCP{s;Swg29G2`}*t; zxIRf^K<#i|7{-xA3jwuQ3=LyGivpQ4nV?e%P^q+ARb^Ex@REipCqtLo?U0}dJ<`0% z4n)+EDZ2c+5Tn$CLCL|(7c6uYa6is$_&h!6OHniA$5ASo?w^5 z?3kh{eTQ7H5&B$(?cBW3;d+?%TGcz5va1AjlI1k_vqny6gEu`%wMuxNyPM znm)U-MWyG!!;mtE$mYf? zK~>HZM#aT%7-0Q|B3;uew(yB*=m;6Fe&oGxPx!-YB;#k57*dCgsx7j%*Alr!!_Nh4nnZ`vP-jPm?hN`q2 zbad`()&UvXG?6KL3Oz&mOWJRrWG>%xp~`ET^pP%6$XGrp;bkv0pGmKFHhRaYFfskH z6jJdt0XG9Z60!U9T;2c_07drOh2nX9zM`F*eSIF3Qc$^2jm2e|?FIe9187=_5Y#Z_ zOcuensP?v5cq#atS=&?rS70DW_X*pvaFZK;>AwunP}FSEl_K^M@BB7nd*qs)Wv}yFB`hfV8(V6$&u;t0LR2dI-UN z#6G}`kUzW!*hq?otZRLwQ&CbQ-pAH=dV?>^+0H-cMI)jtyo?YSbEVwx#N}Ab{Pbc* zCVP&#Nbs~o_t)A-K@7QtQ}>!P^omnq`hsde=2e zT97(fxe6v)f`z?Fz~yGR?xPTOT25LQ1)+ z{|pVA6`BOhIu*fs1Wx2~5vEWlF6xyDszo(Wvk~(3HS@7`?8+cd*;ID$^ZP2ojqfV+ zry}1P*g|Iz>PS=;TO);)A!w=?^>HEO*c9WZl#57$hOS!K<&Sf`j?&xIXwGU$k6W%r zxvtz68Pc8=m0_l#m47DM-ck7ACc1mj9@&5_-dX2+xjRAS*-EI6qt|_fybKB-P%T+W z023#b+)2E0Lu;k9jqu4mn3{W&@sc4Z2#hq|W_AD5E--ASXN+bO_;XBuM<_MU#kl=y z73eahXb3uwn6hib_#Oe@4gNT8vr(5HAUoh^2zZQK3H>v6;pKkZ8Y{FU{cN4M+Z~kq z_|w#N(PE#n)#ofd@5HGynw~*jyLDFSxL29o=Ty-GI+K9k`A5nKF5lw; z3-o*ub}NH+33{vji&gJ!?hcV3(5-(bT&KxklQ^l35zn!mnh~B^DWO zw`=;x-ZF5qGeH~YCrWt~r+m5bxOtXT50c+4N>(1XK;W=M$ns+l_7pcaMg6rCBaa5g z*S-G&t#1oBHixiPX(5wbB?@?VS5$9(MY^79P8aa(hQ31=ZCtfw3Dizis&lMtX?3ZA z&TFPt^f8pNrB?Izg>FxlIo)pJ33KC1aO00%)=5j|MZ@$5@3WZY*RjmGT8K$U>%XV4 zoro!lP+yUTx3lSFD^)0!AU;CHQDy1Bw@?cUx8>mif41CXYVl!z=~N0RcAqVjdeR|B zkFNw3JP};rc=9+-&n00>_RXxv5g#Xa`dqE|mP!ykc*_a_X%-4LIKqp)rpLW%t zVNO22lcpnTiav~{>9?`f0X0IA`@GD45mBs&sOLhrT`ML(}OynXXiu>;G zN|acSKUebTL|VygX7|zPbwi9-qTc1R@}j*{u zkUhtn=j9j5HjLW8^EP9N8z1u40%BZ>_xr19MTM*KJj!SQ67)=~2LK4k9x*vvJ{XUMrk!+i zZei-*jVBt z5^~XKm3-8zsX(m{j{?-tt-i$E9&~Qq;8|X{Za5lX%S0)8SpD{+gHR-05e_jeYqb)=PY2UY?p1Z z7)?EbyUaPT2HxaEzkxwS6@eRbp6Z-_#2dhUlAxeo8wcsHHq_ob>LuUnH~c7Lp7sib zw@~VY%efEA$eGxK;|ZfgOC8w-*1gHd%Fg}w?Z4LGZ$t$@I(~^)6QS=#==S(=csn6A z5#L)xEnXzAv_NFEb%?VhE$&7?Fo?mLCa@mcadGoCkH$fxZ;jP!CDn*1OO%$pPc_1d zu!h*)j}{2Tqn;6bOo&NRYkE)cTpg}}Cq&RL*<0S29kD6tw>I|>P{pCcOd9Svo^n9% z0_Emqtd2wEYMygw_lNEDy`<)QY44I4RJBDB_Km=t`k0RPA?a4?pOh%_Nm)P&SpZpn zs?C%nMp@yFNUTQKJHn$GtL+f<-D^$i7h&Rk^s!ZpS_U4Xfc41NBsZMB0ooq@7@9PR zedb|#HjJ#n7Ce|rWi*tCBq6pIj|p#@eeNE3jtor5q6?>uNzw>g49{0rBiWhvV2L>; zW3TL=(DbGr5S0e3{w4^bRs@;IHWUzISX_$2s%S_W>^b^>aY;7MeI_>HFa|XruIk}^ zj^}u_`ZL@_tDDe-Th)fhiG`0b>~n-4qe7{DEIi09HwU{7I4qVeT66 zG;b!u{zowNZ~CA;JDOYa$`80bIFs~z{=Esna?V@;X)N7?2!GNRuUdzb3UvY;fIQ4(U%8dOE>nWddj%t((W z0zJ-`=ZR(vr%US(Hz1m(I5})+Ntvc_;qBiD49tsT5<2~w*Q9+4P@v9XOpPStmYQ=5 zHytRHPa_PM?7PF-l-}qL_-$CWhwnxXr1glxJBi=nL*%tr`aOO4v z_Sd-?Z<^-<@pLIYy5;h;FvN4Lss*IdMynvYwW~j5G8geK-eUA-;4~sWmK=(Pf`}+V z?zfG)G^`-@Kr5BiX>*YtJOdhbZ9o}0A)mc`@e9=%Qeh!(z1|1mj#g#&BHW2?2!3gb zDYt#R+;=U(n#aFw8kD9Iw-)ZZnIBf*DGc!xmG$V#V;`2p(pZ%8X)}UQH>VNkACbO>tF&o1M^6&;DugBZ=jP zH!`>b%meZ6-x&??Nle5Bj}gpq;9c+=Jn}@-92zf3T@<2!UxT+uee8_Z@UNe8xj%e9 zs_DbJN`)})JtqfW&Yh7+6Pt-@TY@3$fC^wcPUBjL_{ z54Uh{x_uRHX8e3_?lX-LzF1`sBVrT-0^93AW^3-E4MY=vC3D^ldjKY;B`tF$>Fn6{ zpv;S!qrqdRdNC-s0ubY3`O+r?XtK56gySUzb8O5=r63z#oh*ALlAt z9z2}9t}Fsnu?Sj}9Y(M--smIf1|3*^9!uN>V3sCU&S;a}RG&rPH*pEu7$v=gu~$VB zQnQsJ7zyt(Oj$I3jJ^+SNDJqn)=9MIAqvM!%L7kFpALqD{>EL0N;1cV&CT?FV-Wmu z@Oa_&cK2c(KnrVGH6&yt3?tec=PqY%LxwpM80j!Is;cqWT8dWfgIiJIeNqz$1WpZqv%$I)MPU~og6Fj@84d}b30C0vO zS8s^`G%NMgN9eWl!^I#KlE<0N zSt-f4m~4PJeiHeKdS5VAl{j1m+zy~2Z!Xn+GVabKLqi0i(<25ahTD*|^KuUpy<~!e zH;cHI$h_om72K2^YdB#vKOB3s>3ivSsWgLL0Z!e^Ke{sLU=Op(Y0mA`& zfKojBgFb`A&xA(uYl**I~YCk&$Xi(mS(&zY$8 zKJ~5YC2>9zX;G>cc`zN|Ro`X&y$9?ektC$BGX#bNdACd!?o6r{B&iaS>bJW3`?xj_ zODvzJ4I@csTjc}Pb5k)Q)g~VS@*Ym%MjgUA%FFfec47(%!C$>-nq@l=1zszPnLWVc zh$qx8PQ~jQ&4dPoeOhF1NCkygcPN}0*l8mwX-E*oPJR9g)bKu@2p49++Y*;Z%IEM; z^I_}h=CYy0NFi=hyeL}5I(udH2z(C`@Fcz|8MTAtQ?+SAmY`xA)q09%<<(a_evWqC zFK=@+R#}O$96LIxPl$jZ=X7o|(zb~3>6Amm;0D&pO0cvVFA?K=?lD4VW@n2s{W;v~ zjJ?U9QC5#No->O5N(h$yD$)D-@Qqrz1XCrMXy1^?P-i&Wn`+ormHQe@F%o&=@2G%P5KTjRm739%R(x9A zYXp2N_E%v>jHE+&C*bpDqL9d1Qa;t12@Ds$n2D1&sXM$s*yM>5WJQJ2)d6BPq_ln%sX}m84(iQ#qgfe5B8G$-nOX5AOy+-@3i( zez_X6p>d=$yAm;1m8+Z*Wz3dW;yd&q_O5s=Wnu#_j9@*uSuS$5|{5sLe zKw!j^rk&D1dspN})xd$gT9KM;Z_|OjAcq%s!_gw=8a%ViOHeXq&ht_v#@XjP5vOs{ z5a?*jl)PVO%AbD_y1EvT!$<~FMe9@~R-KSOwUT>B)F2UM*3TkpLKRVx_F1;?j6%fO zh<9|?NauNrhZPyORuc;Ughp9Douu36A$8hoZsa5$?gV_Sgxn86Oqe8}yjim75EdB~V@y~bm7xIta#W?wcVtTcE84(@8=Dp-=KNY`F`LnU zO_08y-QkqpjU}~_epzQ&fnj-*J34PDY8#OqHD}}ZBN}%)D2QLmCU!3h1dbw$a(#@O z5|9ZqO7*)4evVKZ7b#{+{YkrQZ)`_?LPbtjFm}Q;r7=vMUgoO$42mhhZDtpT!B2j9 zEt1Q|_6kJxfSo;~Nv*(jYR({J65dP5+9>WcinJc_&xxh`pm-)=h7c^Olm%FJAOD!H z-`}B$DUB#qpP~4}Dhbhv=v70Sjs)dw#P943Yo26g!w2!sH6q>8F20?~lKT8D} z!_G-ln!~1}GMAraQdn!H2}U9lkj z!{6>yaHMKd3f|R0H>I;PUKY`=kkMr|yp7+{p!IP6au&s_%|gqc@W`IHKY(XcKy5(y z1)_g|9wFl0IiLm?QY%k$5d-7%Trp~B@o?SoaxM9JhY3(pLVsrIpmGON?om$u(wZ?e z3jBl+5KUv{Oc4DSoQCDswVla==%@~~L}X-TrD}4o;XkC1?4)p+Lh_SJh0}_XN*a)Y zqI~Q&$)l~z>^u7}K=Bs%KWS?iRR!aPle8~mzklRIk-ty2FNvzr?d|W!=Sl3qFJqSM z2=k8C)JHT#v+I*CLShXh@2UBTu%7^;FzG7E9>SkTi}g3XC!K`*-TDue_)L`5UjV_oM=N>?YSB!&-YoiAQ54C{8J{h?;11nx)q%|`RYALtG=9# z#@Zkqh%`4xkTZ@v)?PF^SubZXib^l%4xu==R}^qcD9TukN3<1U%)_{oD%l<1TCs^v zFe?+xBZnAybmE!7=w^NRCe^A$zC+Zkcs`(jVBWAG2aY>kWa0Xx(}iFLjTDRGLSU4@ zfK~s#@FE?ao8QnaPk=wZ_8lmZF%LbF99~MS?OVwlvR&=ZLebBSv}qwq8pl=mzZkmB zOEwrEIsSOa)K^nnQ#EOv@S*MJI-O5|aS8pe5IPLwW--8%2*GdCrAEwC#t+P6&k%3= zELo~8qPA7KFXr71>=e&$QtRfYtWz5VYD@oMrz`$Mi=RMl4sRSZnJ06o@GzrT%In?G zcx}XIA9WWpuopdU-CI}6DiW~OrbH!`r(KVYE-A)~@JZB!OW4(Y_ZqaV+pEezN;EkHZOHf@Wla>cUSB)sh4r8un*$^UakK zh@^c3ei4&1$9oB;x(VaOat zj*jg|I5W8q&phL0vC%l$r#ScknS)nY)6x&}1epeBCJJPS5lQ^Rnq$cX(vr!?q%0OJ zv(YaMQQtJ4Yw+ERN;V?IMoT?0+5+zIlP8pR3qVv#kHi$?f!g98-X(D6cLYCv(0Y- z$=Bc*pfJCv6_A$&T%?tp7T0h~$4cYS=gm4$dY8$*hn|O2pj>!WuUik)e=gwlPA}>p z<^2n$wjhU>v_fDU_2ey$GIcCnf&yK(PbTq_B#?b$Vh zjRxrZ`A{55+|=jT7o-~6VAUxy1#%H?Tq}`l)=#ZJBd}7J(0tACor~jV{{S$k2Okds zLxq6Mz-SR+rv6cW*q8s*n!djC%o1l0PTt?vIPEH0dNQpVS{WSMkB*>zwzUMNK@W%c zt$ziwtAv6#fAjQdxanj}MOW*z-q+wV^@BurYG&wN;*9qj7bD~R4`Fb|KQ{5I{Gai* z+4*h)Li*pfjjGBhmt34lO}CyAjH<@()c*m?{=O>Kn^d-ENB9JXc#_m|%qrEAIkxBc zW30#99nsVO%pm$d-%S9{HTfMt=RsVgs|u`=A?3u3f5g%Uzg}Bnd`6pkMQ@@=r*p?v zu|$PwUd{oGgYncQ^l9Yq04^1_By#3%jq%|Ffg4=apXDPANB29P^+ zGHNES!Eaxth(Ppxa_D@Nj&I8?Q-ZpjSsl~ zLn>7tC)nMvMjD-@9UkY_+;Bx9R1y0rmNMDHJb_*DUx5q?1|XmB79jd3s&p|7>vz^z z>6S_X`i+QB-^SB$PNDOpFJZe=a_5I#vJLk?CNok%ii{CXIG}R4>>%OGz^K(q;NJ(P z;;PiZs|XXL?q8pVs`mymG$b_2enP&f!BqZzx5zI|FaIDV{TJNbZ91F?=|^5NR5A+hR9yxy2}&SUDHLl{`qtpXQzSV81i(+g%pmQJYZRM zrC|!5WB(6}fQNv~A6ypT#F;KJ72KzrpznW4w~LFFb+({fR-VE;!-d5F-{gPwCH_Np z30>#^SAjWnrw!5vHS^GLiTGEQ=l$FMZ{js>K(}u=tL8Cs%5pz`Fr|G6gS2WhDWISI zA7E*MW=$+Uh1JL7;drA|+5)l^I-FCTt~bnDAXoaAwi|s7!PFYIgboJpvJsg**WX&pZ z#Q#2#FnU~Sj|tj=((Q4P5CP$`fpy@toIdhb4JM2gVddr5lBe zexUEI6YXNaG6T`Rr+w}G^Apa#e;D_~YPL#W;f(x8F}0R42U8cvf7*wmDLxg!&l3nU zNf~g1<3Bi*|9$HJgCoD80HOUK*?`&(@&PAqfJ3>U{P@Rzm<{|2CDL`kw^Lqu@vOL7 zl^$l+{%n)+#5VOiBR`--=T5-ARJQeze?-v-{-d1lJQe3MPpB2pls`WPXUn=&(L38s zFUXv2tMz|YR<3iugqGF{KH&mlM*c%A#P@%&)c>hXuFk=!ZMfB8=l#b?1L%Knk=a?> zB7XjBaM6IyKQ4mxr7{j3?taxLMxVPN3t1BB)laWY&2=l{4^lQpaiyjP=SfXRV^y38+@EIr z!)CbZc7Ny}Chs#lET#&ZRd4-fFyAuiQ~OkPvbV#d7p6!R3`Y{~=cAa|WvHX$Mlbd> z8?zGBRo&*fY@O+HlOpkb5F3zCZl?MG)3NfP!3)cEl|A;k^7Wv%R+H>(syLsn4H8zP z^fUEv{Ih@p1=4%!mU}*1oble3wbwXTr4?pxg=>2y>}n-b*ylE;XJh3Z_VnF0RX%ns z=@jz{gO$Q`^h4R|(9*X&bW!QGcO~E}SpN1R6bN}{xU*7c)`Z<)V%%M+6NIciKtP z=Or{3&%Mg!#{{23sxd1l_xEyxidL0X^uLl{GMZU&A;@jBCt-@lDGl zxrVV59`ZKcnfi-RsphU3}Fz zT35cv!H}0V_Y@w?4#e&pUKk3PaiO)?%&SL(Ne#Pap`Q)VBWbY>UO8)mOnT5*jlQvj@X<&0p4pLVjq09CB(9G&r z=qHQg$l|=O*F#*Tag9wH5xI=~FemY+y}ghTb#XMZcgg_w05Ie6|&&3-bPc zPz%p~tJ_($lIymR?;=rV$}jGz)ecAM4(G))+d1o&R0f>1=_=N9jKPJn$a9;Q>4m2X zj}8yVp8aksuB)p%`0lR1^-w${mrYA)he1}+4;HwTcC=Y1#<$RMa`(PRO~yW=GC^@U z?cmC~8orD8oP7*8X`i$ic=h|VEt&G-sZ{Rp7pFjlo4$MrJuwgk@@e~I_MvvE(}7a7 zw{LqSMbU8Aw9Eb~@S==jCp~C;~C5}Zw1*hhS5IK$@B}jt)zOr(0?d) zFmG1TL7Fh>5GZXSe5}2%-RvqZOXqxP##5l@+p?t~jig@CV?OhgnxD5qJd3x<_WQP= zFJ;A=7=1XSkA%5P6^K@Rnum3Ny65UHQqhEjS=2}r>>Io(`_Y8+#H-PNK{ATr;)qs_cPkt@^PCDKFZ>QH&HU};)-zDMz*g9XKS60FE+ zGJI$jXaUMLxHDBv-T9U($70?7KwL@d)72t+^P66^NUp&b-^?Y&$%Zm$h)z&~5lW)n z@_C+-i?fvZcShrSJ*iyxk>|e_h@BS_A0VFmRB64FYb~9r``l|$;#@x3+ps%FXp186 z>hP&@_RA4BgXYSngDw}{uB(LtA^v-9s0vQoRrFNh{EqInwBiGkg9GH#REj~vOYwW?Dfo9mb=PkZ!H zh+X&p09in$zaVzB{H1&w+yt~+25U!0x;w=af+tnF(8osAUg%D3PPSq4p^4JDj(njA z1K(4~cjrqy3wV_)xg~=q1J}d$W4WV|DRt$0OUd-6o&vXGPa(sP;YKeO0~25Bm)_K# z<^`_k-ICp$ESAt)ynbbJZ0@^wXn4lCto4dzOGYUlH1&Zh%TiwSO%8vXET-EF=@Qxm z&4r%GYA zPq4%Lx+C40$ysw;WDg>rOEz;UFI_y?xxgR@jInfII+x6JCX2o8U2aIQy|?S<+u8fK z>y}~{+q$+iTCvbmNP82nknhU0WdfEXFc%yW!n>%fY3-^p^$hXPNaF*FkS6Fwh71 z>4l5eC)lyg$&IPawgXU`KkU}v#BF28V>3KVf7q}E+t`(M7xG2#M($qUXmt$m<=lAT zpZBB-MYda5;dqsiH7vzb&qgJ##@yk#4!csKf%1Lq(j{~0lIJ0$a$*_8Q4{CDO=uaJ zX;=U|EVv@wy~R?p#NI3#>{1=%jP~2LeQID6qD zOQ}rGn0PG?yE%|Z!_8ZVii znabokGKF+|DOv1Ew{w$aa_z8&sjQ2Vi|LztU2}G%x>M@|PI*^~-_J{4A!7|JFD`|2 zu{V2hv%58~xA-;&{2j-o_hv43HeW;$*5~sb8#7sVd$E-7=}E2UX6f$f$H?yNt zbURGhbgE!?77B%QU*485P=bM-C#}_)(6-;4b{3=4itdp_5h&9uX?x>e5hlifK8M$E3YdvmZlULV-KNc)4F_%%g|lgkYe zwwK)c4?E_sg|=L28|A&Nr;+b1aSPedtv%m!@p8w*sgr#OZ?W@+pzQS&d)qd!fpe=z zA$@airjWKMM)nwmM5%olR$&Bdw7&U9L~T|Cp1Po_D5Y)`+#@`sJnH1_#Dx5yvAJodaJtwOpl zmC4#UR}inL&3gT%tTjW0bUQXk?3gLKma^N@>Eb&(fPtl7>Se2C*EN1xCfDAJg9{u) z=5ns#Fqz?RU2N32J$ic5DR?%)-~4-;eonGGQ*2LXv#DG<-wVeGTb*LQ2Hue=CcDyE zv{4&R4%acLEebWz^0o1Z9^JdE}Ol-{16bisPd8`+ai6}{6W99DE>`Z688HkwGeC^|H5iLO+^Eg_jqvMrrUcV-YB zl|3<7qNcgNC)?{r4=-t6#V$$};4a;m+H6m^{elj z#Q_OI1D0z)sU_&V2mEV4Vh)~Gc>Ak`i_4TLzC)%D+==V}7rx}CM!x8sbvk!|KS(af z#V^VPcjA%BWbd-O#n{H_4CE|yW(vg;uga+=5uwdQTFck3Y*`Uvu%`-oRv;?E!Sr^CySZxOg2^UdcfT|aL6L;%#`0Zx>!1_TF5UecDSmV^^w}Y)hwe*7@plra$l)m~uPj^Tmwxon4n~{dsPA z&nu++GHGjx+S7%SKRJ5}d0veF-_FYWBc(ZTheL z&p*b$|3Bn^zeS-{{oBYyKIW%+dr96kfh6xbMKqOWDym3zW@#2awJ4%Knvaz4#Yp*H zrYOu-kgf`5jj#3|xGwO(4KBZYOW+!}Z`38rLM$d!%0v96B9+?=!Izk4G9mF2X1fWs zmQcPEk}L^-rGy#_UeSN1?8bB6jZi_UM1HMRIjMwCXthglC-g{So8JrJAlpX0zpu4sj*J^@>6~!ov-U!aZz8K=h3Ih3DIn zg_kHw_}R~vu-Uyhe7z!Svz*B&eYjImT(Y7qKXN5nq?J>)c6&PG`LblTD)gQ7P7U&EV1O2 zp6Q5O>x?tlBmGGe>2T$WloW;8aka#b5t|Wii5tW030o4za7Ui26S)ubv}E}c6Mm++ zB};ULc}7ts_D(E!p{vqM_68RNS@OZSY>-)|z<_CqCa{Kqo{x%_oiFBPm|JSTh~3Mv zw0ia}(Pj9R%{jMZc$j3-8@!bdq;&xs8r>jPD0)k^1Zz!vAydVQT&h*TFV6DUc7~Wkm6oRus7Y6;!aEKz_Yl^6T7@Y zoOnP{#OB*t8Vy&R$I4nJ@szYo;)vY(@&NmmIOW^7#0BfFRE7PaTorTGuHpe<17}O! z5N^+Nv8oz^L#V3RD@_$Id~A%!R9zFib&Y>(o1#kWpOUG{%8i>8#RZ{2rfMq>ZcP2E zonB#f%dhv8Rl4e7sdUv-Uei`R&+8?y3}mW~C>m6TXdqK{!gXEMn>^l1WU6IFsE+Vo z+;7!`6~!f2luY#qMT5#v4`iyRqkTQ>1DR@ejk@w1$yBd(FRAVbEZ^0hyKkyDmDQwr ztJI|W?y|vJeV?L;#Y&K=-tRs2Ngj+gtBg$bL2Eavk1C2f^Z}Xb7h$9=27yfVX>>&o z7vq9!*+C(%JcD9^UK=!2dTr2n@23s&(^+LjAJi&EA9S_zq}G!e^nSjaDRq$x;n!X@e4q5i;D?-j9DIOnAOG=$qN)IXgv{Wl6(xKW37Nq! z;ewZPN@nm`Hr|qWN@hsd)qThqUInnnOlC+Dj~0vxnIW^hSu$j$wf_!pLS_iBd=_;= zW=Mx?m>~skVH>i^o1H_pIU^-|$|;10R?gGQ+qvd{70MVME;_KWqxm1dFmEGi)YXZ}*eM ziYmI&94l*@d zYj>_gre?dF$u&ErS~d3u$6ie^cd$n0vZq{aFMB~;uFH;jF4tuz z#O1o|wC{3VcFwarmuYdiYQy4k)eaVyt9F#DTWx*7J_!N#oBt}%y;3p~7Lz$h6$##MB9W7+T;&MSVpFP$SZ9FZUY zgqQnbFR$^#ulG&G@HWraAD&Yb^YB@79Ad zd4@k9tn=`riYk#CnGx(k$c(rk!+m7fdM6_X`@YY}8rKXX%T~XUlN8ZhG9z0!NOMRfG9%~1 z=k@SPWJX@&b>PT#5(JFwD!-{P@TTp4Ffj67dB9$8Hjmt|s5%I0A~W(aH$6wPPhW;q zBQx?vH&jQSmxo;_YsFDYT5(j3d(x=70rAnOdOtoIHQx=hQCE2#Gm5Lakr%#E{%{|4 zgY?NLiGD}r%3t2+zx;OZ<#!K!`8|P`@AsD2QBSz>JL;e;v7?@M9@eO%>~{eGKxWiS zzNr~?T1?HT^PU$rO7rq)6HUmB4#Nw;uK(y-|J9?D-jp3Zv&^;bATzqf6@K(8_$NNz zj?C!yyJ0js8+dTRZsru>X^pysn23ANm=W$hW9owb!kBvNeAxkr}fK3vQYR(U=Fk z;Wp;svPm@Nz<^0K<~cVE#vGStp7ILusuc2E*-#vFL5AYkgzJv6wYG)Eju|i{$By@Z z+~V46>=O6mt1g}#%V#=haT@K`;d!gXPPBGiqM ziCs6vD@R=;ug^&ACsQ}y+byVDS$@t@7u*W0Yjf>f*JHQ#?Fvh#&N+pdb`AsT?vO>I zPE2Cmol9+t|~J>e=-C(%G%c{EVxM+0@QxW22yE;yW|y4SsH#%qmn;b3vbRl7Hg zt1Dmh#?^aq;J8+~bqSjaAB9O~+)6A4n@VKH-KZ$xBQnX1>++5wxaY>*>Pj;1PB#M)%y?*?{z+f1^!r8y^*P-FU8zHv@!@8Orne6RKLp{_SBuM;2j^7`OncKJ-% z)VqAKJYwa5X!`Qi-gCqsx_kr9?_@uN%;g0en_hm0_v1T#x8d@g1LD)mAMkqP^2Y*w zaQRb#KDhjFAVk0XWoJ|_KT{r}Uw$?aqSuGqAgLefm9%~YhY3FTh)n%>yG3o0zGUiK z6~#QfFPVDrf9uzl4W{}I8BFzkE(ojNY9kGxfyvb0=J!ecJ<{m)dxDK#zn@!(3;C3@ zkM+;E%GDq8o_oZe!s722y|$`972FT0f3<9m)d$bOCsqf2pNV7K5Sut%QPkm#$xLi4 zKT4dqEcp0|YZN8?1b;FUrS~WDy!TPaWF~s^e&Rje0oKIb*gEimJ!B^CRWxJ(I+@JG zr@g{)oa>OvWG0^WEc(PZtj(Q>T^_+MlbOU0pv&?mGfA>rCN*$N0L@HhQlp}9Ayd4G z%%ml*nI~QCX49nAa?ct?QHOOVGs)ZennVbT^E43l=aPMfq#X49nmylFaV zuMC4p`v;iINsoK4d7-Q;Cmpl1%fn2QnRMC>l}SkS@bJ@QCWj@RVe%;Vw8{1I%E@Kt zJCmE;mcrx}WtY9?n0&og+sQYU)pl~H)OPYFSKG13uJL)J^iI-*n0xcNENMR-#UB78$G$ zuaO&`z?le}vxbA->kli6dB}J&4Zc_3@CvU(4kb^f;VcG;#mbYJrr0`Kcq^G{-j>I- zdPP+M^gNkq4c=GFyk?)a(i`g2R(lc7wCnucg=sfpdG-+YWTtHhR%qH*?}>Nu2m|Jx z%(Pw74b%1vXpm|9U2Ug5Q$9PV9r8!?v=d%d%rqY701S|scHRx#X)@rahhaIai!i-f zQNl;y%prKbh(CWm9MRssSaO&W`*7GSjpCIKkDEncm~I)pU+! z12{4=)9-Y_mgt3`*frPP{Ev^(ZS9qgl=GA4hd*-z=(Pnz_`^>DhGcyYVBE^~h z{@%p1ycln(5^>U6D`} zu^b6xt`L9rit$0u?usdaL2-q5ka5LT1H#5DuJInaPM*{0chVJJ7V8yTr01_FOJKdi z9}-uTo$Xw)XF#uBaX?1m6^8=e{1wN%BAikb7d{3uSDcX|X^cpZn}N)%Fjll8Cd;fb zZl#^o5ZsBHHOuZq&011!vS+Q3a?HBU-yEN{&J|-;HW2I28aM!EZS}VbX6^DbB4<4) z8IiLjb8lAp0p_fyW$?^8>W%(c#}!qA2ZGG3R}_VcT!wSOWo6bIieer!1ew_pd#cN= zJbRSiS+gg3&z#*jpzUYRcfQ%|)i!2#>=9&UuW|0#><#5%_3R$cdd}W1&)uPDP#L6~ z%j|B(G>=RzO&X#TT&h`#$8Y4W6!AL=-vAVp7 zjbrQqzhkE$(rJ^(P`r*Md8j=ra-1~i{}$GZg*ea;S56K zo$kU;|JS>4W(SH3GK~+oANa@DjgJPa+W4fuz%{bBBXkjD8ebY1iZ;IH)ubuxnVF_) z@2H@u*7>4Mby#)CuX)ngag(n^qNYV=m1CNFQXNg~*$YXdW|>H46<-qh*ZzNt^% ze2W_-P1~h*O=V|VO*>`yH|>%6)wDn8nlwG`J@|P=l^D-tnqH71y*yyhHl1?Ar|Cj) zUN;##ubT(k9p+~Cc$yl?G>;g#EH~GA4p(zaU^Ah4F_Ku^Z?2IlHD4dBQgfT%VaHYndtKJN-)p$$$Gk3Ue#&)O^K*eNYd&1oWz8>pUDoVvVK=|-wNLY# zfi9a93U=9?p{`};B)vD!nc})^&Mc|loR(ny=6FHx9B&tA&KmDsb8b)+?m$)Nw8>s=Y>Q@sD%UX)-Ni*r#%gB4k=-ipSKl#H(maxi8S- z`vNU(sEFe>A=Bbqf|jj;k<{YPhnBksj=q-rtk-Gr|72PY_yeltdC$ddIp({#Eyum_ z+H%Gp}R-?J$KvCw7xM*8t?j$z`<}R^^c*31RX6{w4)92pkwZ+_yvbLC; zmA06BtL$IQz1@!v=HAV5i34qtnd{~7&wYf~E62D(W-f<&O-*FxKCh@cNRvTk?hA@4 z!NEdi?ip_a&3#=_T=-bX%+=mn)H>Ab?AB3%hH3Qzyw+Lp>@y`Ytu4sJ%R1mg>k>Cd zTCbHpYrQTwAzIgYleM+SMb5386h$4c3z=4KhJ22Ds`XB1n_BPjny+;?+|J$t&(GGq zu6bJz*kRN9#6@*)eah|^*xlpSm*hcb$_9EX`vQ&`hRnQ>tLeNNSAuz?0P4zcG&65} z*{$=Y+wBCGb2V?V=ZMc+jnph~$dH+LouZh>BtvFir`HejHu=u!JjrOAw}XS%)JFcP zm$^G{w@j{idoHT!y!~Fzx-|;(o|l3gw+yNooib!x7T^4^SCIK* z2A&tsuXp#)UnoCXrYK@rYsk#Mn*9@R4$Qw!vK!s)oo)km{w*#c5o|VO=5O;R=KNjq z;N2HHL-Y4a>d|~JuAk2yJ{WJv%s=Y%B*!9qn*$Cg88fn+f*Si?4`J>SEBdwZD+x@1Ny$OUJ_f=zC)E#O%X zUL7(E?({lu!My`b-hu~x6Su(2XIOwNCJW;yv*56shYOBNp-w5P4)XAjS@5bi1{a3e zih7(pWEOg-l?#*Q-r&M1uCNP(hc^qCc>`(TYBymPUgy1d;aWR@JPsc+3p*|vybEuU z)>*hK=!h(Qz_0AWM^H!5%#c|qc(sLx2R6~dV_x}R^PhV@*y;;4+skG$i`d^4&Kxp} zYOIgB2zg4#6JONeDz&KD8!?OK2Mpt)Wv;astwxR}h-Antl9hf@haW&J%DSR0+9X9= zbZf9^i@cncMfad3(l+^i(eB``(4zfihIi3Z-Wf2;A*c6?PGVwv^h0D8ol#T?5+X8- zjNi746K)_a9>J5u(GrnaT<7Yxcor;VyUhYw+~SST#aFSPkuT!!Ro;MFd_AU`r7j|~ zcwM=}vbYcoy%yg#V0&ost^v{f;)lySY4JhnoW*iJv-o77Bxh`XT(8AJEPmbf;FS?; zjdI@jy0ThU-7D*5!|KYJf$qJswY>0GN(R%F*YnRnkVNLnb*^Kt%=ynL1)p=PboiA! zf@A#3U4B1Yxi>h*uYA<&$SV&H9OG9W^4@pKe{LBj>B{pw#^;b(5>hm%jC6_2l9=27 zSu)A1&XVcnzSNRt_l6}admbI%jH^bSesWrL!GmmSo+CS<>f*)RJ2i4JxB; zBC}+hG}4mY175q&Z;mBTde3+sX23{)cP+HNbDB>Mbt?-bG zE3PR!yjpRsn^`N`{Oq9>f?i)yLK<(uGkYs;@g8%RqQw7A*7wKhRBivS_j#V@JU?lY zh9tR~d?aa-G@+p}#u#IAHCHZ@G>IW0NzzBF=(5veC>eu!i@&(cJiip8Fj1ahIONqfcRIP$_ zV6rpFV`{D-@>g*&Sce7w!{y4VUba-rsl)d3Fv*XrSz<-shsZR`S&nhr}wPN#MAp{DdeJ#Or!T3t@bvQYCx80ds)x5${d7kaG)>=K<%rTN zE=b_gkDlE{;*~{%b(AVzJ63Jw?pV)50Oty&!8$gqo|ql0_V9L0Jr}7urVFCy6;Ok9 z%&MBQ9s5`7sbl`x4an?q zaphk(HeGOI`#BHPvEo8p-|>h5O<);vcD*md5#SnFhF{=sk(_)JEF(mzoh#Y~%cw<* zdhnt9hQczE{%6lpM)J9SGBVCzLd(cLC(w*O)nDvo^#7liP8mbaNi<{p|Gsp}m{>io zjCtpeE2H#$JejfL|C3q9#`D+AGxnb|){NtSHF)~`20QuBPW1;ojN7Spm0mg}of|=? zX6Jo~+Nni#Go9LB&`j33<-bn7s*BL%!8%pF2I(}cYG!pBb-~Q)R8T#$I!!$%k4{BZ zPtE(E_a&Vc{Pn)16J6uE0(7uW8>%_qQ8lqU?Kz+I>i0jLPF8O*?PTXHEHhL+u*{n0 z4=l58)nv+SQmx9&sXOy?T;V3mYE~uvtd>=S%BudXF)O`lP+8ekAtfvKukDFheg67*Gi%_vab}II;wx*6 zz|Y&Ttg4M}S(B@WnKkW#VP+Ly!0V!O@jYu*HDBwh-y>&j@={u_;1Vordv%W$=WQO& zIw}ZPmkoqv={XL&RKFDNlIUTC5p1)Ix4p5doFJ@AQ<`J{2RTi_OVD(kZDFAg@I zh4@{zo!|eCa{}&C@mB}5kOv>(h)-7ASrVj0UY4C-WoFr9sxVS^bv~6nsmkE8r&aS*^w&#} z?D^H)XD_NAUG{}~RqY7Q-otEj7(-q5!SlAjXCJ#@ZN95`tDr-?1>UZ; zs{82L@B$O<+VuQEb!}T!{i|!Xz~BEq_R=+%bMaj1MOfFq=Kw<2VbzRu9d#ZfUB`Ku zTvbU%Sl203+Czh2T}!Hj*0rokR9%;z%{e?L*>z>LO?2H@wW`zgf;Y%rcX>O(Mh&{} z5m?vix29cB3u;_Y&JmX5oc$j8?8oal4Fnb_(L`%91~7jP%%3YdwCHqCzyil$fm5Q7 zybw3Qf@5I8aiZA#L?U3}Ua)XK(cddy*$P;+DJ+^SdgqH5xjq<@U}!3^U|U!)jTakW z!Ofz%-(mn@PBF}xFPc=4REMp#g@w~Z567GT0a$D^EVfNF>(rs&92f)!m;Y^rVTb4? z_l9-?hWaox68(Bj(evbRlvi&@-wM`h30TAri$p}Tj=Xj$Ib7?N%Q$x^c`x)6UD16j z*vt_aj*0#>d;AW-e14cOBKpr$3sXsMgZXxf&g}3LJMoWz`A3VUo*cJ^sNz`8J5wn)u4U z$@W25?rT z@bqQxu={>6cYx@EvhwL^5o^h0 zV_3AA=;0Ty=t{B>7M&zIdS1n?{FFS8{Ay*q<*ev?( z?Y~4w?)J(6YU5|$f0;jC0t=Lj-qO6y&m=2hfs>;5-m>u`2Ns+N3r-OV+bct(pA5Wf zs}B~c4GYzy#`?lSc|z-e#d1aa{nla}87_fEN=2Pr&N_BFopvBv(In%aWOzGuCc5C| z(-|C2e^_*&=#a&yexQQp!JGx6oo_1qJGEKgD?+EUZ`gl=D%cEjw~1c$_Tq+YDisz> z7cF{XV<#Yf6!GJtV}I>>iVY2bIYUJ6d)XHwImW9lsE^IMV=#w09~M|Bni2iDKgqSQ zzy{HxeaopWw;9ZBA-d(IrJZPhAy}xU==Dz9IEPmk7HJ@Qi{5!HTdogt8i}6SGw&CU z#}A7}M0Y=Q=|QS?H7vMZR2$9UkE4!PUPwzj-`kf1XaCSV4;noy`KqQ#ikm;VogMC zNQ*&0d@(EkIXoYCMX1U#|lOnG~l?3MavT#-@xa3cr}ak?+02uOL7G)wpw)Zh^4nvGFxGR9img4I&V=Q3t+x7(eVot zIlj<%SZJc?JD21f;rSHWe0|DSGM8%ZHFGhs9Qj&VB8B3fb2O=Ibw- zc->4YFXX~PG121n@1>FFTt-#VCq|~MB+sp3k@lijFCLx4h&qK+jL~BO3}p=5!(f3? zqPsKeBsnl&4CbrFzb}LZ7Kyf6+U;H1;wV^jtmv5C)1RV39GDXlZMn5cGFci5^Nkek zc+(f0PX2)~|4`9ZPd|H{qUcY%5#4e1Cqi;REO1ElXtXFmqc4C33q=<_dnum{C&9u^ zMfYz@{)zoIhecb8F2DQf!+yk%A%2Q5AOjZ6rngzdaK($$UQH_9@qLJ%0V!UgRb4o} z=>KSley?(&Uc3CGMHJaYSZIpqryrCuIEMYOa71)SnV*g>TpJdyCz_Yp!JEMqu*eb7 z;#=l%a;!ppo#?;XCjQBx^x^14A6!5FagxP!7NU=@etQ?monCcCZL|JVZ!$3e<_;0P zzhWQl#Vzn^8S2NK$5Ke{c$hm;H0AB?1PI|xUYRz1cFJT1HA+M;!CNm)WtVLnGR zAuo6tH8~RI8zZ`>Xr_-X7s34VME7@=_9V+-{w1PM2VP|)c9URkQ_-V^-?L>jfuXtR zwazu2sk&aUXg|?I+b$+hh}MTi8;O=Wf%o|1(_SfAO*=IFce1ev<}4NUy*rGgzZuNm zLiEg_IuDZ^1@n&;eR0?&9$v|VMe;>M2QGUQh;KlAv*-k!`6xleItqXwA{XZCC1)ys zc-{|lHl8i=YJuFdMK{fDdA2&GMYqACJ4FKnKTez@4Mb0DYSMth8wZO_5KZe+z&IW| z1&f(z+3E-H1mYVJ-y-_h9TNy*LKU#k5z!8hbS0h+w}yq=izXIrr(H!hdWG}!ftL3@ zPQQNy<{qQ-T>%TN7QJCx$44E+k0XAXL&<~%b3`j&okweMR=}LqqEqiW)saFU5A#hF zy`t-;{SM3@gZb%&LJMG_GST?@Tj{<6d(KuVHFp`zT`92eX~I|0UiTGc(wU^fg6X0; zi?15(U?j%S)eV7Rgy`D48hLQGJIw7V`tXgp( z9iD~-MD)5LK`;9Cf(81C-f-V7>6FiGuh68rGakH~7Ty3BO%nBG@A#CG?dz4Y(^;2& zx&g>RuINV}g^0?0Q@zr?`uu+;(H;4B!~FY2|Jy0i1D3U5fx4p8S8Q9sF|U9HR&&e= zFgKAGDKIxxblSH=_VM@oV1a|8<(Y3Vc(|!BH(hkuYtOrs$U3jur&b<#tQioWi1-xI zx~-$vli`uD=oryy&+g3Q*j<{X=$faO=dj&Vuz-msG$^C-g{FH|HgyfYyPTuTfW@*! z-~RMRLYLTBSZutUi3eWbqlX9(MLR9}_G;Q~D;U~}2A_U^1IOD9=Ins&f(3uh38%;xe*rIBHDjXKbjF* z!q8fD@5-Ugn19tiTawv)^B93?g}Y(F{hahGnahc;KJLz;M&`nNC8EEUF8PKmHH5j1 zMK}J|XE4x;ynpfhvI(@?DPE~*b&B8oibHW}tfK37-M^6It_uq^5REQ>^D*+?02WCS zz2cE^3~jMQSggM2UDn_?AifFlt)h>}Q%xw{ZLrWz(YkwDF-Asqz#_Xve~(P;K}Ge1 zMf-|gbojaeR}n+|icLkZ?(K zz>aZ0vcH3{&{5IIi=(os`lhgOvgrI5=U>PE4#J{GMN4Y<=s}#3UiEFAR%;VeEMI%C zqP0Hzk6-B)d=p{5DWYp`s(g|n$%XlQiJqwW>TeWDDJ-y9H1Xt3+eog01=fky+4~3k z4K{=Y8;f4Ky%S?%s3tQ8(LVR~T|!?z_iRaX^KbR)GHdKmH^#fzcvx(rXoFfkXyUP* zu-G2adtUnSY07RLeUs>*Zw3=R`M1IRJ4K&5<~$3;P)oFFt$UeQ``dcur}b#>bOsr> z(ku0==`*@9eTbznXAo`PZ!6PGWWtam`s*FnGMGC{U{1N{?yIi+FNf6@7DyBQviFq? zU(t22=tj|Iiw}E?2AdgWL@$44Fq?{vgvG{)o)~`BP?8H^u`GY{A3^+>=pPRkbKt>Au;5hD&e2p(m2fdEJYTe5$IG7Kz|&xnOwo_)#wSuQ$zBO^ zefzzQFFS}&LVPN{VVPG9UQ2J=Sd-&u4RhLyw&>W3>T$;siHL5WT+WykTn`Iw5QcC{!(DRRMAbD!x&b42VuUWqQ5Qa$BZzX4hv_A*135+v+(dCSh!O3&Ph20 zdXc$IKSldDD;xtfK$2*#ExCnvR=^^wMR&fui_b+D5^0JK``1pwyjX%)+f+~fn!vg0 zDlF)THXFRxTL+54Xk@d$n84iLY3NnM*IMg-V#eoeW2z=Pf8*$mKA0~Z=F6fO3t+xN zI;I6)kybre<9T-NZ{*ce*IxA=C?UBI=07O9=l3wteP94AFhq34zFY1C;&Tx%5lz~D zW;6%e5EgDMdU$sNU2<$1EH+#8lM!wILvk)GRwDY#C3P4>LhHSH$T}zI-4EDqUsxbd z^qHgmNd^bNfbyekdTh8Rpw2`p}(!dw^kn+Syv|7D|VOvP9px>`^KvcoG&=(I@Wj`XRM3!K?GD z$G%zq3cW%z7+O$fNibhi(Qj_)y@ieMhWYl3=C?_vDGd|bwx{ltEfwI0?eH(`sTN9aY~{!4DCgS$36&i{C!~I{-U>C zG0-y>ho&p~Bd zp^mpmG!qsq7Twyn^gik&)2olI8J{K*s7LC-A`L}fc)8mGlJmXN#=19m=IcOw8sf7> zU!E7>PkoJGz9yoFZX7<3qZt8nM~kj};2J{w=mvTu(IsC$#t`aEWK0N6Y zf43MGSSFhKQo|@kbCUB{^tJ6u-39A;_1pF0F{>MrEQ1A?i2go#y{Fj`Uh!pZxub-r z##f(`7X3$^HjIkSOqf$FS~pOKK*iS&<{Kc|FY;3gCvFMMUn+WV)6XNQy@jyIBGChv z&RE4kE`?!*==HJXx00;Kaf|LRec4-r84Gj9i=LU4QdnrQXsy#N50Kw!Fw7P`@XVfnk<5c3 zUv$@rN#r+h1Qs|Zy7re7oNB@Cu;4DyInTdIryp$(i)M)4ofh`i{~CK|rRe<^7tSUd zIWQ+zw9%<$o=|JSe04=X`!PbF<{u677l_{ZRGhKE-2roVi#9yCg>Wl)6c#)#`qarB zV@K;@(M_V0-@LyPh!-I~PjtzjBfXrU56tZ^`umpRH`#7K@9YWfUTX>GfVjCi3*u+UV|lwptlL~;q^r0Bbc|69Rcm%yUsqP=#MzQ&eY5q5~K zOnQJBBX+>BTlA$FvlvNy4d~QF$MoJpVBpJVHYz&3<`m9^;4oNll<4O6SJ2+V(_rD* zqHm>7UdE1!VbS@beee3|-_-klSnQB!USXRb9gIc+L;o-sMv3-az3LxqehkbvPV}qM zVH%y=1m-puJrnqVy*a00J`p{fox{8+*dG=gD7xUAyNUh-Ww5{!(egRha-swhyyGu) zPkjGZewbSVb4!_>jfI8B^I{t;yp!RrFD#lTI&{_h^va=RSg4g~$`=iYWy39*Wr~g* z_5*D>+#ePmDEh;1!u%>S(yIcmtBOK}Bv%pZiMIM?Fk`*5lv$;y|B_AwL;m$J|0dD4 z!)JU<@(9d-O!T?epKeF;B+SnUQ|G`B9Nk)2aD(VAHU90V|Ef#iO#juNz*)5G>jx6Z zPb;sYzvlh$DuFK|FeHfX{BFY_UbdiX{+D& zJmv;iV6$lU{q>l2Mq9$7twozJy}?hi6)f6TwC)d!uOvAd7A+8+ld_1JqOS#!m*{Zm zxPhtvF_?QwV3A(1NI%hOpNwVT4u)XCnxeHndWI7xoar5Vpr3X*>ZRJQW>jCGr$a+p%vG`<)DnOcUMt!fYn8k*&s0 zdWyyZk9zMY$H4* zZg?ClJVEsLw{K@0k2dm7gV1|^ihV+%XQvzoOxX`go_Y=*WRJzvHM9 zVd46stA47WZo|7^;eDbd7w7sojLl4PMB6R7`6CKw11z>#w8`PWd#Udx@5lw+Sa3Nl z9cfIgMVm%{8OM2$%m^u(`dBJwrhhfvxag7V*AvG255oLMMQ`~%z-S*(nCpm6pI=OX z_{DsZ6PbL97B_qD8;Vb1AxHFysfEP%7ze`yCPR;UetkQKAv*o`9kods zEaDe!d3d|0#We5u79Dft(_WlOg*oY>>)N(uN51JWUy*3H_vSGf2sHK%e^Sf4*2^ne zQ|_Wy51Mif*{Ec4BYNA5of+Um$6=wa3a!gEDqy*q6r)NKNDnhVUyhdIMU zFKwIXy^t6Mi;NX5>+Kw;NPEI!eMN`X9?qE;G+4+lI%Gu)h699Ps405+<@pDhJFPx@ z`kVQ>d54dw<32`T9LRzNx{2QZVdr478v1>&0#-zIu$^kvL6(cU}tP8XE4%_l=E zVOUFsvSGgNqTkhjpJ|hG2>Icr5HKH)yVR=0WwpXpEUl~Az$p)mhQ z(a$S;cnkKeV1c%x4Vq=NpiIUwFBJX!k31$4PIvl1(V5HdBMZI?nD2;a+u3^=OvqJikLk9&I#a}+EvR`i;h9hfBrN??If(Z}x2p(_q2 z&O;R$12_)DY0)3XC?j<+ z1`E~_y>($7Z?{HU?_3;ha6PAZeKZmsd3g68KDP;mt)lf_`445}^oKbEMW6fgfVWDL z5AzQbP5aAkn@MVKY}QA7XVfJD?64Gv!F?UFeL z^PLjy{&k7>I({K6xJb0i@;koduqVL6lSR{7jQgGB6j*q==$qvyCb5$q-Wf*vuO`Xa zB=^B02Sq>l=5+$5*hE;2?kH&ufswNv=Ij!k`tsCa>~{#vH$t>{$`2H`@3?o|o?dfx z5i@fC6qtXyXvV4$^tXYzREX$tE2mBa>tKP6qN!6-=nbN)ndyj@Hf(yDVmk=KQPI(l zuG!3f*Lz1F>eopjP89!6n17GxHgsWA?nv*bHO+s&6T?TOA1pFJbjF}!nsj6*lSI+m zPk%|c7Cr8r2Bfu`zC=_Kb78TV=+fv!KI_ziIdw%pT^3+A8Jp>y%A{cEYef-hr@Re??m^(q6bCae(;fz4n|=tBim3IvFlwAK1tx>@8g{ks2>dY z;X0BlVg9wEFEp<0Eu8d(h5Cy2{Hiq(QY0M~$r8Q((Gps3bQml;O0@lN)0mM(m%*Yd zML$X!-hhl(z?>tZ*Zef(QOa*242wjczBegE32lS9J4N#r9AgLyj)Vorh+aDJIGt!L zR+MDSDspT58qJhq=Q<^NXg?^1~}&;nkv3)=|EttQqXvH`AG%WuB?_^A^pPcWI-0vM+ zsTbYw#S!vY>YW3qduH`w))(0hi|i7$6>|xrqZ_>gzx3NDe!LclFGYNX=yS1!)OoA~ z7AqCKf9dU>v#4MeE&6NoTZgkKP_RK(_oR=qMJv}^p>)=cn5T8 zQhGxQ-=7BaXNrFE=fm zbN7qp4S(VV+V&DyxSY1#6c$MqounysD2{Qk=mgOk-KY0*VZKC|kMq|zALd)g+-J|( zqZchQ0u~ufsI>|fT}P<36c$?{TK42)MBh#t%*hljo%s(UFJCImmo9p$)bW_#LntB| zd|>Wn{BaV@X)3w`YyUwB?uNzoi#n}uZbkASEOu1%iHl$CM(Iz2g{O+HyZhc5e0BlM zDHFZbrv2%I`AT5EQu>_8+0z~^lnV>>68#0oOWE~C?@(C{?n~!XakF7=chTC8|8A1G zFt?ZJ)ahNiPywfiR7GnIIZogoO@&3%MK9i0)&__#M0}Cxw{7021X7SH`rC((Gd`m> z4E027yYFAkHj82Ie9;pft|PLH?uSJWiN1O3tgnG2G!;E^#aBlhn14LX&jcyh0~YKf z`rmD9J)qwV7H%Q>b)Ad6?OrLcaH{Cw2Wk;oMY9Q!M0Y(r%zL-IjA&K#%ji;~9^ZDD zZtSlTHm>lLHr)^2#YdK87*x>dx#a z*ccXUCVKtEA8#c&8y1`^+Wh6-wQ1v}u<&A$5xt;KN04yFZz6kNxzUo2Nn#8p7{4y6kw>#JMmNRcqJ601Gt1ar@T}n%g)q}+vit6NMg+7?m;_NZn=3fZ&FXF{U@7!Xo*Lx}< zQ>+XYTOzu$^?)mAGC8n7uIP-!4DaRsbXd4ZbkftWdULQEU;fReEBZAjNk}ZaJ1+f9e?pAP-D(1 z(Uz05y#TVF3BTy1ikG~7i!))qV$tD0_uk8n(qaBA(YzKjD3H)%SZJB(=F<;7MR9F{ zMYf8*ws-WeY-%(tRv>!K^aIQe(FlenqId4wMl9tR%;y)q?1KzW5q}2EpDmid^x1DX z`xn9d1RMbu7Kn-F9!S54T~xv%Cq-x9^YR=A@qOND?-pDP3oa8Kz5X#qZhtSBzn|zk zZ%iTZbdzCjE7AM6C?Qp_nRmpmM$_~D!!bDCxw!hkOWVAa#RMjEq93mKrkvzvCUc_Q zPD%zzg+(3FcY1yCUk<4S3@M_QwK~ao8cXud%GAhR^$D8%OV6HPZGjjpP)l^%p!!S> zeVdrYiVlBtdIAM83g(Oz-CY0AOUYgd{4C9p6s_)8Z7u)INMH^44@ZPO8g$0vE-@+2ctw=E}GGFwO z{yQpx_-w@IiZwu%5hPj!i_R0hvh{a=qs+xSHCF!-JpD7^XY=(d9%evx zCcvD@qF=oCJpHRX(>vH(FHdSQk>pXBdt9`}tKTzBg^FRJ`J$!47rotUAy~Mk=<>=!;SGR;kZ8NX zUl0WNn|SBp>+nC*Um`gK<{u&Y&-*vL!NK*1xdTPtU0JuO0|Wnsn1O7AVW;Sx)NS61 zX#os{qF28C^{3>Ns&GYPE1F(OG7skDi@NpOQ6$0X-r3-K`s2~Wd!bgaP+QSCw~wXH z!j-V_Nzqr(gP;UGVCW-y>z>k&IINAZz!uTxz6_DQKtJz*dcCsuM>WaqT9~^*bYJn5 z!E8CjJKtLqE=|APK>^~0=a;SAk#b6Bt?FA8D7NlX~Vz{2Bru?!aW_JnRdd!)5R znx8#`+9Ij2NIEYT!6M#%@kxXnyr_glPl~?%YKHepqzD$9C)#gb@5g}w%$Ixo`xfu} zmu9d)3(<>`#$7@^=fR@+qWuotI*Q~@Sagr*&0kD@9TIjHRAMQtMg&eg`y)nHlu(k{f6&Jmb%Of+?3(~}g;6j)%o zXzTh{zQ&g8!@`Y3V{-aqj>z@S4A%h(@2>{pix6Kby5a6o53`|ZFlV;t6WI^Z3q{s5 z?u)jp|D^Zr(sX(b(HaR?w*y+DwP@P-SqmGe!S4z+2IZ^!1J&*NA(Rs*D6)C5&;rpKvz|K4ew%p5 z-D}|`4e4Mat-Mp-_2n_`n2biodPlWu`GAXkBn!xj=(K)2CeeB)!Z1ZNrPqW#l;TvF zf2Qb~Av0)l!30<^QM54m-nZFrFIcFbXxD=a>AFHEVIdV=m{OlXDq0T~Z74c^Mb-$y z7nzh6US;emU#h zO7)8t^lQUN7A=KE7mHrLqMi5Rfe0@o+M#WVw+=fH<_;ANeE9SkGBAWdTlA9WFJp|1 zoMtzok1lHPDw}HN9V)L)a?3o`H(=H;8vXdpl?)--Fc5;F4-EZ9Cw%eW6%>9B47s9j zR=)T%pK1hinure0efn9-r!CBvCR%*3tGBszGc2%8^u1B+XcPdqjVDIgzk6I1m;bDmwYqnfJ1bZm?(%(Iu^0^(HwU z7M&>ib+;3oCpZK{rRYNi1s~9_O@PHFi}recFXOy_Cd^+fIEwoOz=Cn|(&~;SPnlBSjZ3 zk9rw(O<1tD=*;m^N-$)wuwQgWMSfEJstL6{PRt=F1U%;ESP8 z@~I41AX~KZsmw+W%pC@EN6~5{uxNtloV<{SFh{|BV@2!vPI68Bhe-$tB-1yr@8vwtmz87zhc=Zhxl)!tgY6? z^eluqi$qrqY(V+>XcDgI@4pP>6!uSs`KO7V%5O>Qa6>S+rs!|4RXjv81LkInRx}yZ zj1p)73nqzHY6+oRWI8NTB)a3Vf&w5DIid?Y-b*(YIZkIK`evuYbpLKInA=Zu`m5;V zz`{#mVP>?EX0S*L(Y0UB?MTbWg++UbuK4zm$Eb~Mu)t2y!{au+L+ftK_GKqrK>$b{pwE0@<(tjqs_(O!5M=VS%}#_nldGj02hsbEk=xotRFb<4%LQ zvqit@e+Pkjus$rn%FJW3FyHzVNGx~;~xgX2zJLzPbUoazD9FJQ~WpI%Aht5=AXdnoC^!~5>0+N zxRbJ40&~kno$qUSyGzHyg5yPhT~+fllBZ!I5e?1GEu?;C!otO(uby6c5{OSge6r}Y z*7Ioqkz=sPDbaU7svw498WxR449 zrHkJ5X1y25ekv@SF52tf%N_AHTyn9GeG=Ef5`C<7w~9^5d}BY0>+xyu*WW%V3d}qFd@; zR!lZd!NMl`$qV_EM05ZwIz+V1q=iglgAsx?QU7Zz=`3&*hU22o^*uU-OcubLLeWp! zCzg{u1#?Wa^ZbtF%D)QcUnlyX+dK_~Ho`(%M3WYE@U{zfgN1vD-db|gYH~FR7MUvg zWWwHUKnPUT$($o({|GF0O!RQrwv)luuxNYHdu!F12EJ z!9tm$Z@u+@0d_qN7Md+O;*FQQrMmsF$RW|@nmGZ8&qsWr=&ip^-o`P^g*hdnqeAJO zsKU_<8KPhP`s+_LjGi#JujrLW>@G@Y8Z0IP+VKg<-ttmT$5=yi*Q~t`Z&j$8s-mt3_lY+IZXE=4@gh zEHYH|k$VPFGQMUoUklNq)PMa#Uh`prVWMw;y_mVDI~V4bh%V3C_=E!sHH3v4Q#Sdq z&@j=QZ})oN>Gy+$2Z&yGVy8FdCejo|kM15%?BL_S5ObHQu)s{wUq}5kj$)e3{7&@a zfe(2};uct7yXdQL9wVp;ZH9%mi5`CCGq3K;AtvIYqX$i(ABVymN3`v9KSP3lJIudJ zw80;>yw4dLz}zI!Hp^@9S+^ACE*AZD@DGEj;R&$dWMY}gu;?_=PE+q7;tj8ch1ZLo zeyNAI1{I_1L<1MUcSj>MLkmH7^c2KP5HA(Pw<5kn5I>0cQ9)c`aHxwc7`pMI7t?QE z6vHr|7n@<&#*0&Qt@O%u3CDSn0&`M%kqL8hcu@j#N_nw~xdEeb5zOb+J~;*R@o|4m zn7=kJ#=`u>E&eT(1x1|<3$)@z4_KfNFUny70zJ1T%;i&VE9SerSPXNQ(Fb>f1qm{O z%V5EkRP;hvXb~^A!a_R)79I)&MH0I5sX}Rrs0`VP)?-mVy9e9*a_JKM5Mf+|4h0ZP19Tw^-I%E86je+=1 z#P^7{Utf?wW66XCazt;wVyvg(c`$c@==fmMLnJrC+%2M)ci8G}xZej09TdIymlucy z!#iN%-J;RA+HWL-Entxp(WF5KbID*oIxx}iA3S-Smel~}PZIt3ms@|QQ53*pg`%_9 zzvAsxN`?`_9epN`Q_UR%bH|DP^VFS;m~I-(%@iH;?vQ1K+N)q#M+N4SW;&(d!kn0B_o+=72t!9;p<|-%J@vhBBCtq;=qo>d z=j~%!0E?7~c7OHt9&EWg3_V4M4}H(u1h*3w+9SH;@~?;$!sSd(Mc>q39(0VsBDF;K z&dGR}BRvhPA)j;SuI$ku4v_=TfQgR78Xep9l0ab zTWy>KbEb;U+Z*GQ@=b>Mriq^1b;N`0<#h0(|8)ioBe@Ia-zT~;o{;G$!iELt-h$&{ z!HG1zEwC`HJ5mOVED;^Kb}uuB&^TCVg6J!&Gp{9XEQ5uY@MlS|NK?^E9_T-VBg%rs zx`}@D<9`}Eh>t^@xC5hMC=h+2V}4=<-R)P_atiSBtmy)O_iLwt$o zgR{yBYy!(*ft8}~{MyHhiK}6e^`Z~$e%&j;kPnLu6FvQ(7QKLUWXYMoIbL<1ZLr`@ z(bTJd^SsV}Son}=?~nbQ1HKV3-)Pa5Lo3&kTnO_m68(MjYqZtiN?33$4LgnCKQtB= z8ZR2U@-`ZFbOL>v=;59TC7Ag^KI_WoW zb4LgkttmR<+Mp`yJO2*E(0HY`w2bi|>==SZf*0$HNt3Z;~D zaUI=)Xx_sU7LtWSuwbR=?C?>Bgis?`s0rQN5Ljr0XqzQLZ`G^;ESx0z(2UlEho}id zZPBk^NFZy`bXYV?boI75-rkf7Sonx&%AXAjfcO@~8O>G%=qQ4%V8OPc1&2@VcMzX~ zxL0CjF$~K@e{R>zE2YsN<{v29z*WVj;)5p){8d&}oSbsw7$&La&9E?IR-_abSuBcaKab~w2{0#7v}fciq85K&m_JX{my_Y8 zz|0liUpp?H7GN z`9E}Uu|2R@h3HMm+mdLeOXxNj&LXftg6Izid)MUzW(e;uT)B3n42g1euLv) z4xUGY6@6^Y2_lW?SXgwt=T;7X73~REim79(f^kFh?o7t zVE$2}e_mO~s~wdGbMr-e|K4s3$>}h+NVM>h5rog-smw}5?bDi9vBxp6=s3|kK5I&+ z85;S z%r{>2xhG1!h3xJye^1fPDc4_4A+Lf3*NJZ1JZKUPydKdT4LpzNP4ty(F6Dd*I5k2w|5O=`aHEnZ~9 zBHcx=DIC0q0dfTlt2y#4n6I1Yfp7MBD^FWs!R?~$`mFJ=NPAc)L-d!&=ai7#L5mlC zx3UK_htM8as6w<>Wnb@eo1U<6U(vrkT{4wDrobYpq8oqs%-dU<1PeA5T{1uy(J&$~ zUjnDb0$89-w8Q5kypLBJ!Q3XIg}2?rnHxC-i&Tn!KDgmCKtBu+onCZBM+ajNr6n$?~dy3MxghCPh=-$Kv8c%(CPO5DHJ*VjCZm$!Zhg!fwDWX&E z9prs?I20BgDLSB=na^Wv2eAFG7WB zno-kb8q4@TrY#EJX8_LM)_^*i$Hqr{mL zUi-2?_}1UZh+IURyTohH|IQ*D`K~6;UFWrb`ELtfC;H-b`r<9G{ox(c5Y+ymtOc*V z{HN{*wZVGg-eIpj|Lvu}FCG0jasElK9i08!aw_Y&l(^>#uYKWj-xNbU^SW5VUOWBJ zpCRPu7;*MquN@BVT`N_5lsIw1Yyb3ZSAs=gj+nk)du49)h|Hl+iBs&gzaIXf?4G^1 z5cl5hwVyhA6T;r#BF?_+wWoaLQG_PlPh5D=Yw!J;#tHfE1;m+)z4pJ(eUIz4q^oF>Wp)F316= z@C0$;X|H|rr=KDn?@8<5l&#-uzwzPVaIz=m(M+1_(Unw|#k5208TsBGu|K``*@=y~yV^~h zzR+t=eD^L&Swgz4pa_e5-6`y{{AZ zzU8%_{^_;9A-mT@GI+A=GsMY0uYGXvYjOrk%Y09H?I-{3uYE3~@E~#K5wHDw|K*PibK1L;%5iD$j`_s?7TOVaE^ z#Mx`S_SI8oKO>dAnz(SC*Y5r1iXX@*-%Cu#z4o-rr^4{>m&xk2>CbM$O|e=e(~F7IE4+4f z;)!iN#Q6t^^I}rxKPAqy*S_KZ_~1b3vk;5RXr zPZIY&J+HmvPtO+I%)d^Y zf6HrMe*I62_209exMx__!1Kh3m%R2T3qLK&>`4&!&tul?PF-vtv*n>g3wwRilxl)OIuj*Pk2p7_}<8{~}p5OG#k zeQpJDuHS1v{q0={%c#n^!E5IifA2P_R;nptSd1M(lw zig79b!Nkdg*Z!B6Uo19k<^$r)$6os{|2L#xWseePZ}Hk!J-A*ba`tZG?0sJQHx}*t z4Ve=666cS5?Mp|0;sWW%5wX&}cKPeyM_Bw##D!bE_WHlRZ=7fa2}-1X29S?1E(EH9 z9u&@J+^c|23alD{1SE}^C;qUQScH^1+&?0f4GcpdF9LNEBGi$|f=_}=a9&5UN0v^T;U+51r*lc z88G!0lMw-cxGX{d6r!0BAP$ZLkO<()0}~%4d&trP%LEKfT(lw#42~G&C&QnO+Z&M3 zfsYDY);KRC2LmiNh+zk@A_65r)PW=)&>JB_6ApRPn7=>qSPA(8@bklkgBT-_*y7xb zq!zFeA)pg6l%U}Ol_#!A5t{}ATFh+_N`eUy9Bs&7#ev^^a(U}_9>K{1{!Mr;VaI`% z4a|**C5BlFQXBKw;3dEH6xh5#Jq*(ubfzFkL=HUezH$6APwc+_Tm4`yM>HTpF+l-| zq+YO3fNTnm3s6cU9v4{>2!F)&A;@IFagNJV@aZ6a8tm%eTLEz=($&DN1m^>sp9o3@ zsU9f&z^V!|q}V+F^vGFnA&(ob9i)4LE6p?^|3{xMM6d~%2;j1WjfiMNFbu?g_p{f$ zelvpFa3Teb43gGx$@qU`uD!YA3=qMO&?Dr=f=dIWxQMF&kqs!QL7IxN zM6i;ZO71IN`7o}W;qrnNWS(95{puu`0lFvk;G1K=4ssu4k02ZaM@ghT!)1(&Z1`Wn zT#8c?h`7PxinP~LPKK+Wo4peBF`!<;%`gbNk&B0;E~2p!UW_bIgu;NN9(Q!eBSug& zQgUOn?s!k~_i+P*D`G^Lg6e0MUHdgRnBQ^;|M_q zi4g8{k+hAI6Rv5&VS!*dTu#7;3J!Rr7bBP!%-}zwlJ`#O9YEwg9Bv>F2W1m3Kf#!V z{7oE&5aA5UEqKpyO9oC!P+TJ*5{%nmlY;LQkwp+Xz|A8#&ynV6TKV)(m3{@$9Y{t& z?hv?i;dTN`A&7C1frq$eFs~vJ9wAKT55;u{{xc|Oks%E7r_Vq9-Z#xB?wx*VFCwH7 z#RdjDBrqeM5xnDIQ$u(Vf*O&*2m%+R(;^EK3H^v1h06OiCl?ieAcfUtYq)WabHwrZT`aI^$H6i7djVTa=a2nj*e1$HIanczn=P$x%uL~O#1 ziThF?h1C<}b%>OaVT+S*up&+&vl-{u4KpO_` z4^ZbJq6MySkU=3)9Aw*IDaLgI7@HBPg|J4jI>jpV$N%u$Ux8g0=>o6%Bg7{3_p@8od2~;@cf~WYDk3DtSOaB~h z7PA)q*@hebB`zBgM*>nuoQn~AhQt7H7J>E}$rGSL0iiU=fxsJvcqU|qgSix`f5`ek zDlGD;kop8w3vhzteg}dq;M|258T_m`Z-Ss22~NnThnpYNXSk|BJ`Hk`J% zCIkL*sF)xf0tDTlW9#n8&52Fhk$(wCJb27;wu0md0?)v2fbd`NK_ZMDk(r2{0RuTQ zq#*->g9AKnVAP9tiO<*nmo1_b{0cqIj zcOLBjmG2&`A|e5C-=I*y-6mL^p?3n3PNZQYXBxaUNUH-08Di2A&j_C<4soC(g9jRf z(>RTpYFxhVk-Z?Ef>y^V+xD$5#utNt4aCv7C_~OD$cUlVf*fYE5v~2TRintI2lph{ zY7qX6{2-(_fTzH;_a`qKS_@Ass8HZZfM*VDx5x}c;2K;yxRFC#DQ@pUD1-PpBqQPm z2P93=wgQ9Z+-iCsSoA?TiYybz1whsW^rldpK;#O7r*J!9tgc_oU+|y6st&pm1meK$ zfBquGK_aC!AwByoV`9E4T~!vF_1;#5HlhZJ8V zpo8%N(Y2s`M;1C%Nf4)nkW_F#;z|)hN65p%^&j*a5Q`744v=CYt{dUfAXNop3Y32k z6psiIP$)y(0gOS=bpSaPNM*rG1t%QBN5PMblputU;+_#%Zy+TDGdi-|5vqka7}Hg6 ze)0C7g1!Pd-Uxxh<$Y{S27WF58p!R;PF7PFX&tmh>M_Qkd4DX zi_0~nxr0{?>6*ypHNAWN`1h8;1qO~k-0p*Q52+yL*nCBB*M*35fU_T$-aq0nf2!SZ z-nGA8$8|dBR-o(zqD}B*gLN6%4q%Ey8VHE=Km>}^YY@;tuneTI5LZJmEksqif$wh) ze(xZ3nm{8CA^~K-!@Y+{MI;P@h8RizxC=q7J>n*jg9N%+Bo-qS9vn&F7y()C>AfF7 z`&k?J{UxGYaDffEBT#QbqyS{&rgt|zcI|J1pBB8cxMW3)|L2Q(Hh@1CL@cOnpR#wloBftv>eqPUgEEh1__=AMB7F6{-tFz1Ef@e z+VB6NdS}1>!|xzw3AYFc+k(sy1lqu>0Now%|L-;KOCLrGiT@745AjcQs zN60*bvI)HZG0S_T{5RhLUZ_6dHWs|~27hk-li&JHWJZEu0)%J~!vX<1 zv|bQ}3=&8Lsm50DVejWxAf5&H`iQr~wJU;JaJi3IM36v2It3yNAiG4IA6(X--~2lz zuu6dZ4~c9BtLooBa2cpcksSmo9Pr~qoe0t#$Z$r+3B+TdP6aMD#JNMZ2;7Vid_l55 zaxoF+1U7gOw?kkFOby81gYpB2P{0QO-cn>2f{Pc7x8OyA_zMIU5tfb%5ilcyV;=-} z;CDsNJ~FA0af%>rFug*U3KXLVe1P&JnADMqiXda~{UV$P40)hQ>E70U{8xW_Co(p` znu2>uL_4Wj{&3!3W)ZXtwg+UCA~FU-W(YtvzXQ%+^_8=}0eUyc9wL|!@fL{Qfm|EtkU=nq zJXf&vA?)7RA3N_Uy^FkGkli6k2jWr)#YZqbl19Ky3_3L=jhLP0dw=Ww6u3CS^n$<+ zWV|BJ6)H=h{X+IFBn%Kak2D5w4kMofBo-jLLLwE|W|3_Jkvb4ZK&ApwP~iNBU?M_+ zK_cHhOAh?i*Zw`YX26h(R1SnpLDB*=I^fMlJS#5cAeVyFYusu=)d}n?;44AO890%_ zLkapYh|_=$5czH(v<7`VNDq-a0ns#QkRl=pVd5a11L+E~c90&1EJ2XjBCQpiD9BGn zA}s_Ep^*r362zSvw1nE6gWWlb8lof<8#q6{lw`~3xV!F`E0XaW}DZof)Y>71w{a_Xn zMZpCa9R*=YU-jPhL%8V&{V2jSKpY7cFlcZ<#0Z%sNJB=zbj-g07oYvfbjYK|D*x!; z4Lkrj6Ht^OzY+v7V7COlBZv}^JOaulNWmZn2P&;lctWBu7@R;vYxbI7`pP>s$ZUex z2hkr8@k8(uls%yV2}NAvY5d*mfAxAuQ$mOc0TfWhgpMglR%4g8e?b4|*^u5v7(B8I zja7%&-?Fwm7lC<@tOQ{&NKg?u0+}+<&f{9#d??@dTaO@)6@l)EJAj@FG%-ZAj1E^hXgrV(?bhpT$X~GFi7|l2r3bcHk;;opzlSlGiY<6 zW(1)mBh$2mR(W8}G~o@aXuvZ`PyMvsL!vSSmcX(By)*FcAsH5dc}USlI4Ag&k?{cL zCS?4AJ|9U(T{`+k^$pYlA?AjgPi@S~dsE#-v)2Ij2Nv+0ksMt(EF= z*>RwJuu5fCSlNaaJHb{f0%@84ZH~VSGQh^xm4;6>dOfn7MRWc|Qfrw{38YU6ta8e#3 zO(akSAmtp^JaG+^Q&~>sc2<2FjZ#;p2IZ2{gf7)O(EM1X#!_~Fr8QIS3MFt<_8Fx} zS6P*~MzW#WP<7 zscw{#lg1c7G-E*-_BBIMMF2GIPepTWkiKfctC+Adu*Wl99sLr$L`<(nWM!K5s0v4_ z5T+yssxPnf(VC$w9R*mFP18>ikZz<^8l<~bN;;43?nW9Eln&_zX*dq>=tkfUl$1Ie z6r{Tw{`cVLd6>D`nVp^3o!yIP-#n?{zSt7ritzPUaM*Et3GDLO))ldUm!+ zX7yV=C5|E;Rko)}cB&*#lhf6dSqEtmNwUlgyZF~o@T0zvlCIl6!9p|gK9kaas`3T+ zQSa)yO5`DBLZ0f1bF}B?6VpjsFBlT6ZD;v#-#x0DSg?fFh1y1RQaD>r zs}sCL6X#9(KTw*K?mq7ql_M`+IirXQUbqq&r>3#e)2U>**0hh=97fx-rG>1L+JZhU z0tGCMmB7r!{0)AVOj1%QEmXHe^}MbM7W+~=5Aw{a<^*tSS($2|Z^_2DKC!%wmn`qg z4^1XYGvdC69SBzEzP6W}H-J9Aqfu>(cdo zVuHikf+hGDjEbm1HFB0J$?@;U(m~@Y(h@*$wD)^ihV3&N!3V5_sp^wuzPVPBJ#-dq z#O+`(D+prsU#U$E5#bre?bLC=;GKH98-1HLly=1}#cXN8oC<{`lnumJXTCWr?^R`S z*mj1Gl+^U5*VZ($BlglL5*gG?GFvk0q{$rmrkf3JEFOKEfmiyA9!TPq1DzQJoG-5n zsXdjigS@KGcn1-F2QXOA&8Pns-J6CnWgD8hIl*Gvk-%wNo!^I1_8r=2J_$Av5KUJamY0aM@| zFcli@S@I(dABY5Zrn7{|m-fo(ZVk*$m3DV>_c96i<*jK%gp5B*oIamvrfLXvYkraj zbtT&wr>B5|-!%;+`x-<>5C%&`M1=z|#s2L*rG;i((*hDPJnAe=6MT7Y9`O@&B{Oyi zs?=mgO~TZ#d;8E_l#mppV_C(EW(4&5*~O*VtFD-ZcMVM_KNxT>DsdGG?Yk^$#?1Jf z+Zc~jb5#D&Pi|dv?>+NdbK#McC9QOdWMhSgQI(_ttQsfqOD3bN+^+S2m(~&?@cQ;% zsoJf#QjccN<08w3XZ3x)`4@vM%7qTvVEYoeRCz=NxagpkvHQ4c!yc;XxALU=@Eq(} zl_V#xT&|}g4+DzCv(8*tCID&5*(=GpSuXRJ{J^;1BEj<2<#aI_0~KU;;0CbmDKM5) zELcu#428q83j|kUW>8*zg%LCu1_Gm7_GrIz0u?2jEfoR_5MNCV8{lHx(yb9NJ`IBW ziIO*9Wo?VZD|2b+yI~H45LB=l*J4P(Z8H`7pv6t)h^%6JJB(T&8-O&CWHj6F)ln9z zNCH2WF9|GH%;|YxNv4WWu(dpw>tQ608FMcrt2J1V`8;t>HjC&Q`s$z$n;j05>&&%C z2U^XaZWg5>vCz*X43!INAhG}fZ+&Qdx{>6hM5vftsU0B#y5Z^Z&UcXPd+n5drb($4 z8IcHNdRUJPc(aV`Gpz;ss4Dal^aym%(JEDnQ2)P{lcYglLIC2wmVtq3SfLOU4zMB8 ziv7ap#j=*Hk@%nJX&8YZ=rC*D2V*MQ@*eBp3xVW8o+c}wwBOkkJ<(DYcxbLye!w|T zAF!wX1VKD9pel!K_s|g;+?6~{A+!epUDssAuMYyW_i-oCa?^WYfea_`LC z`Ks{}$z*v2BptvB#;g=s_d@EE_sY~u?k>ky)@pi0MY}vS@bwbPs=A< zlS~pG_0R%>CM~F49GS6Tu>5wS-`8Z7-ykx#Fp>XPpctIe4&)aZ4Ot}AI+ss>%Ijs! zcc7`L3ZhHuP$)Wm&@p`rTpb5Uek11&)?#5Vx{L<*0_z9xy#H0Do&k8im}vOHeaKl~ zFT{4YPx^t)s_=j(Bvk^CF_Q~;cOOkwmHi>=nmTLdR(X(siTLYQKyw!2Sq-RtwBNwoBM0xzbvMe zhVsH$h)N))V6(DgYnFKCho0!Jdi#uq$gv7#BgTN`QD+3|NJbdLM}VNPotsn24>%#WD1~&T%MBb}T#MUhRvyg#;M|f+ zUuMDFHdm!#x#3G-;L^tDrYuDo+7*Tq<-i<9+WU6tm^Pj_m0Jv$HJ1m1MPZa8=7RbsUp>qV&7~ zyi~Nvpd2#RhZj;hUg}1{A{z}%E}$kgH$LE@A^^XvcK%G7Y@uH?0ulnY2p(X{3@b`= z040O=Jnh$qIrG51R`a5G&0p{Dhf2XzoNh{9QIabJ z!VgGne1DKgE`At)S|RH{njAF~O?HFiaUgQhf8r*%wVo1zmQ&4^6ou6P12**m9oUo7 zIY%J!WzzqK1J(|pxUQ;7#TS~{%D(`P6J(tMf-->9&}ONYs^M!=?4{7oAl{{^ZD-kK~aGHKg;J^2*!mVGCbh- zEKMo$M8GPS*=IaLM>Zw211UyQNC8IoV5FZ<(=}(+Pp0mjPV@;yM$9J#$&f?NLjX6` znEOY{FJ}3qBPm;f+>1|7 z!*n}ywqC4NFlNupt71AGln#_8D6~BgKh1K+pcK-9XEe(E3#Mi%^Xp!RyJ)$p2cu<3jRO9#GJ$wm3pmKtnV^}^hceVrb>uFGRD#1O( z2jFWtV5%ait8kTuakF22z`h@V z?H*FOl3dq^b}KdYX?7c5-S1t8 zM*9OefO&`<5Q6d)E)2z8NdzX96(apNb1*6vbkCrLo&adZC3byePHOnotwZdN)COgLr0?poIFW+K8h zV6VC@A1zftHZlri0`ImHGbOVKuYV2F4?Vlp;bUTE0Pw#^Sd(S^Qng!dd1yN~yzH=X za}x%_i)s9puvDH8W@%u?1okFC+3O8I1E~>P{FW7?K;HR$$Y@+0%)A@QTwD%+@%erf8bbBKVYct z14adoEl6*7?|qm151_=Jt9@d{4pQV9v%4-&sRJy0v1CDKo|L0|?}%2j{zJGGjOG3V zvc-6APX8j=RPBL@ujiQr!U4{;?s(V!>B!jneYuCp_b>)2&ese~DkLXS5bfZmA>3V+ zUf_}gm=l0!Z3+N{V)e5G02_r6Fm0GBz)~!qitLa_4}ilR2!2x09PNtVNxjtiM@9T? z*02xDD1`EwWxDwB8~F&JLiqqy=}mbsS|0)^eE?CO2NXP?&fM$AK#w@$WFT5V&%kG{ zM*EB%(SjVnxyc9c1HfONJ>Vi&5GCNxQLw~C%)N%FN)~ZoeA4S*doPACZ~uAWujP?- zWFq@CT`pJC%arQfi)#TNx}InX1M~9e1)OimoTV8!0U(DazK7z-fp6vS%^(KygES`$ z1f>Il4k*RNTLU(u+8S;fU}{ox`@cjQxJY-;!UtG~vf+S5DIP`_yZ2@$cElnLfa5(F zElmeRHAo>0uX3ax(0#oVPuJ1cTuN6CBoNRm54IBp^a3h_?tQMi(;U*e5Cr<-njTys zebpl88OpkP;Yyv+hI>CAF0$ic9MY`AI0B>+(S%XU2PJnXNL6=q_mJow^4>$Nd-(bu zQo7#4!)bILLIXoa1&C;B#8H(rSV+DG^0-r8h4mp{Y`}p68K>EMC~^;507%1{TsV^f zM#)$2d5C||eHMb=WjV|8!qgQFfOlQ;rVsdXj)k+7Wdk67Eb_pf0*R-DM9Saum=$T+ z(QkF)fwKaDiL3}ofkg-|CPHG2qj1b9T2x(-BM`OVKXIU5ZE|2y0CG|ef9%6CgC)KM z0_`fM9~-}OslUA!QZc1#&82MxZ@9F{nyuSj_ylm$G)aU!Q zqV09Pz5A$0d%@p<%7~=+?;*}TWWR^h0PF?tmaT?jd4Ho?&`>uB#u#UGdm{c8}H(-hiRy=yL?@PR59Om=w;c&XtEZW)v06H#eDN- zLZ?Xftq|0qJnJ6SakViqDb)e1D268Am=7*Rj>>CTEUK3!M^d1oMGivcb=dhcO!0W5 zVzi3Lh{8hoi6{aq1j#S!)Qi->qzZwq{g<7el;B$3sf^^86Y>S?q`g}!eKzz=_Eeh% z@KqhvIb-|m%6c`pJiL=Fvw;Y$!GY{`Z0K86hjH1=up3UMScHCE^;3&XB}UgTUu>mr#tfyij0oU%3|7+qWEmbvbZy4^F@A?GS*nCq zaHy>}#INr}-CnemFN(Ip3d*}Wd8%!96_s4v+N$MDa${E)D9A+0*!6>krVMqR|H`F6 zP!Bh1`9y{XTzhRSWTSaHshFK{+xcys=9Doi@kA^9mUXgsg(HURx?PJu7yDlUZ6aK~ z4S{tdLr8OZ_zYxA#9C*iTGQ6pu@V+oL53o6sgd111tIM%%7_6T&|DH!#yg-=lpR>7 z2jQjQRy6JQFw+jWj)(vsLt9mZi^Mm{e}{XjVke*2C&GQ*U!zA6ng#@|$C2X3O{`E= ztm=q4{49;r&N%GX>W+P1AWl7HJ!fxz0;9y}8~x&BrOis4N28ome`ObA^(VEcA(Fl$ zbQ8ElRdyt#-fHxl@4>!~Dl0WkWK6^l2_~hj?e1=?^==bhbjdQ8no8OiM>ek@Nsq_u zOK1kqVG(iRi&FHx*OFHLW|9<{n62FB^mqJ)KP)~c%+CkYEd!SYZH)UPaX-(mlLZn=#e<~(dvk=pgMCu z3K3R3JC>bJ_UX;3^r*&3{-U7Gt!66lxpBo8n~a;}fWl?kJY##!8-@i#jILtivR@nw zn{_glh3XZ&tRzDJ;Ps3uLecYvHbI!2y;8}kD6-cF5qA4U9Co$x=F$@NqmLo^H+hSY zZ$&zbaJ45?@B^&^-8o}uA{&-`a)i99J^!QssAA$&6a%X5SNVH`KlV(GNW-g*Hi`L%|7U}tx zM1;a@7WNk^Z;KT$`h%vtGf=m41a+-GSLd!D6DncSDBJ;Z93Nc2;O73(YSs-hUI+^V zt{(ehxyn$RskW{^kD<@aMWNK8fpQbV@1N2sy?(WQ_G;DRqesFOC<*kVAd6q zN1NMYZmnqj(tO;=5u#hMbiw(&qVK)JWUukcX&h&o4PQXbE7=u}3At8r90B#zRh{SO zgKyq6XjaML@|PzTPt*%A{2R9NB};>l-bkLvlP$7gBEA%#ntlu6;QKmXb>v>^U!+w@ z2~N^4RSQ5dw-v`vCr;MmdQO$R8gqpPO^=p#PDkG&G^Mxc8$!jy!ui$xe6st~hJ}t< zvK3Sl`*8awsC(Y)xs?tUcBJ3;g9CI7Uk zKy$t-A?x2lzRW?6d#zXST=Vyza~hNlU_HCxY2jQbb(*)rG{x+kCK~!1f_-_f<<5j= zE$7DwM1D$%Im|3e7j3jxGBl1G{ty)ZfQ^>e?3iM?qR>BR0=k*GP3e$e!uSTVyDg=B zIv3}9`1M2`To1!YnVi(;Ht%Is^5Pu%ZzGHMqmc;eYMLf{*neLK1zzl>W+Y2mwN%aq z5?!s*@Xj{xWp{6qtRC6 zF+cj22J<9kKu`3{^VRJp^kcvm^%ABglg_9Uay1_*%%ugQ9Eg`YBH8FMU)Mh?0vnUz zzALNV?MDkSd1uuY-TXx7NVD6K%`Ew+YxR@00X2*x%R>Bdc|i$<{^P+zLLIjeAJ%#m zFV9S8^3edP)AEy73o-XuALi5BJdAU<_o_MIq{K(I^p7AzV1HXPi z*HT!mwmyDMl!d+Mk}}k;4re0@Quie_9r3v&osI-u^nfqJoc?NJg-=FnzT@}_{w(G2oOBxEJVq2U{ zman?dTB%M&f3RS-PYaNr8Ns%5I{!!Zyk%721gEAab%={*zo^p*+_xJH(t6hK{b?X} zY-fHG*WIf6Lq)k<1$At92Ii9v2T{`R-N43$XNHSE@bT`2!$h+PWRy3^j`v3O$K2ae zMJL7LDSMNoY3xC7oi4W`hR{Ezi{v51|E;MC2jp*3sDdXpEA*H@ruJ_qrOn-;H>ww` zAIoAuDOU_kc3x3=iX>V-TGIL4L4lh9L8Sefvqjsm@fh^4c%DI zCAI~=*GwHve*R-qu4QXREw2zFGxj5JI~>v-1bk}7SAjaH9Q3i0cr0M~MDhcB>}8QW zNF3GR%~|eH@4TZ>F3Hfr%bKI}y8P&gmmL`9j0nNCXM9uv>bUTmP-A{9s5m$}it2HY zT5E$0LzMa8>Sud6b4!W1Z*y@=fpSCo-ZdyKe7UFWNkd5;?Xi+owbUaPcbV56eKv65 z6ymg2Ey&#KFKSY;FqY3>uhhZ7Nrf6%V00u4-uH>ZrtCV6i&ngFrA^zH+@SOc>K>Xs zkWm#wnMEI~>Zc=^MVNRuIr*QfVQ$6fOnhoF)F--D3M{y42}Uou@PYn~>owXG7|8j& z+_53zJ`R%}*sy-VKXo48h12TuMWlMt-nd$t4A=Kx61S|4UvxEEwhWixt3Q8H4MKT6 zjJeR~gWF6*HR`HevC{azbOuVi)($~!5AcQQmaicjcaV;8Ms-=)8bJj9%+d^@eQP61IHYFl?_Ou6Q zS*8_|P?^@VG4#&O09L6rCNsL3IZxLv4+< zHf1yKV66KM`I&-wGPw)b;RQ!%tCd^}=z`Y&O-Y$J>L`wS*@O+E_vX7ge8JwpEnZTq zp3L5LvU}+#Gx^fiJ1&u_mmPXs&Ig%rI}>)I?iWnTsO_$78&J;bke$=jZD8XOG!k&S z&Ja;cw-@tjB`jw`pZEFoLAAu3ZgU|~n3sE^`AU3YqNnt3Zw_8GHjpf@L~ftrQ!+B{ z5^i_dab>q#TM-iUeFm#5Q+A} zU_nd4h&yi#g`fVz7s|?D%{mrin_s5&1R`e2n)=wU|LYhjO=TInc>cMdePG$7R-FgW zd8qTe=~Lf#Zqv%H{)2gGuol!wPUlsf%vhDy&3PcT{gBR$%g5(vP%Lu7|6~M&!5$5C z?I@EPQK8bsQwTT#P?$$baA*R^I<_zAyx?toQL@($>D87Kx#<{D2?=v(xt>h0WSLWw{1ZEDZi4zRUFavdz;YRjn1 zw3`PJ+cWYHvtoNHn#r3JG9~Yu`Wd1M*b+a-C*4!alPO zR8VcT^`e=6#KP@DRlm$^_YaY=rOIXG3M&4%(faw4#H&_xV(XZC6mFr}wSKA0`h`CD zZ9L)AO4seRZj-sHADKckVDdkuTGx8aOirAk(Nuel{cnmFq-tuYVnv0s+) z)(x6PnUQN#ymbv)?|1kdSG;^V6Wg~DmQTH8GvQh2!G+f@a`)NW48pB$o8cD+4$>(g z-2Svp#P-R{R&yW*UqG^Oh)QOMxiTxOZX<5!Q%+LrYiJbaJC~Y&*KNaEtBntBB%3XsI{8e!@>+AoGlGmV zY3GZ{q#M+fzo(KJ)vPkvg4c}I!J-<|pk)ul|H}hjYyh4l@0`^0t$U?I%^!uPw{p{C zY8@Rl)j4bg)wV0Bc3+Q^*?Hc~`n*G}YP#j*AQIcyBaWZE;@imI6QV1kwy=2Ff5~l{ zl0Uv_mQiEKTwHa(=^%SzR5OUk$TO}?%0T=0js~gcs$8b-BWBtm^4%!r@_#V>~CWA6)(6I5$>cu$lNFW_n4VZ?s2&ig${v!SIPdHSVcX|I;Si?UwYey z95;F_`N7S?CY+HVA%T2swHh34=!K(Bnc^oqUB3^73zUTZTnky}#b0t`P+nfii+;X5a5xPgOH9#J?UhsUe+4Sb>Kjq*Pt$S`&Lst z-FE&ZZCVeUCFeMiwiwyrd4(n#_6Rwe!YXC?SlMX<;#$jJ+J71TE(UK}-8nt&@fo%l zK22p(h?z|+qM)P{SGhc|&iAIcoT7_?&1%xVM{e$inbb!W<`0lavCo|jO6%~(hziz8 z`{KR+x6ejM3Z%D#;62Y7qNd#W?HZoc8hoxj`p0@XHJq-`9rFs>Xx$+Eq{ZAb=2~pf zLB=3gQ=?riyH-sEg=aet5l4+D^hu0noh8YaRt5o;n)noR40$t}g9TU-1 z7ubIniWKUG9;nmaV*F_npa%37aR>r@%YpR3KRlD8(B^uH_R77>R z&MW}d(NkOQS5KN#K*&kNf=($qQm7k6{Df2XdDT3fYQ`w{$t&=s9&5V+xUSEKiOy{hbZ6T2uF|rj53A z`7THx^;ZMR#)XlP!Z9h!7ruV{-zhYtsm(&{aSi>9eqnhGHf@UQgv7R}L!)^tj?*ZW zl~?><7wqi?IlfRtM$W0u);pKxB{>#s?8KOSZ!B~w$ao(P7*fFTBML4I`S!|5g81{E za7PzPokXp3y{Xsos$J1WT-rD6vR#`Z-9*P92IR0 z*+jyV<6}yX7E>%b<)qiB*A~+lcmqqKmt&P}ERV{tqj6xH#8w*LQc~cUPOF z60-_PHwQm(70e~5d0(n#e;Y4433*%b-~& zCphHtXZg~gbg@JsHvYtRyCA3GPn7Z3RLbO3x}esLutTPkz#^?4R;!?cC^4z@y9-AB zWIgwJJaF1!>+r^8T!5>XYo#=^IlFICedgm6pCoP>VYYoHM@L4zWwjadrP@JS^1>)F z`QWFY*bkVvcsV(xu0;Dbon#BBM=+XaXjw-1s7K_R5jU#B6M^K8TNUH-zjt=$IRziZ z@6zp4X3Q~5W<8qNp_?N7Rx&TbTl=5ebQ^XDs^c~!f%y$iOUXaU-@?!Ip8nK1t0!+) zD^(6}v;SOL7o(c7`}P1M?x#TWZ!#|??)={~n5rJTWpU(ks|%|-CPZm|XOns|*1A_j zk$sigFQX{0R!3r$L-e?kqw8JFT>4kpWHbf28ZG<3#bYUlli8zu;u&J#Pv~e`pR%O$ z8>v!7C`Dhb{6Jr4u$bgIY?L$bBR`on@EGOns<++R;wG=%9(Di&px{ zZ}ZtS@lOb6bCuX&bG)0mgJP3YIa^J61=wN&K1tstZtKZDnmb!B>Rl$3v>JWuC6bSLXgHKkiq93$|OM*mN((T0njI+CpzmUz&v(rVJeeGn>3 zw)TzB%(gfo*Ew;%V^(plumvUW(X2-sx{8zK<`-NCx;rL@*d#$89FbsLBhB4-oQV-hz zQBHOK%OdNgiNLa&X6G4d<$!-`GpkzUJEV#O9zEDf&BLa?zWkTC(R4C?^chJTw>q*V zUN8}0MVX|_NAt_=S+rS>OZzIcPvFTd1^3&0`KGo}Q2%Q~-y^oz8I2%KBQkz(-4U@R z<3;Q0t>^+=0wXIH_JlS&9%V9Or0<7nu`%LVa$rS)?5K0gnz$=oypvxNNSvsE>l2IR zDr{2T>Qi@xVuvj9SA=b-BA7fa%ZABKf(TH0z(Hjn<@bVwrsgME+>=eXi^W?^?j@B4 z17hb%i@XVsiv4)FnR~7!KjobWgrxh_7c6l!Pqt6jJ+)YfFbynr6yeAm8|qiyT5sJp z5V)$b$Kw7Rj5U4YG-%~z?`*-*wSaL-(!I8zLB{uayLff_$y6bQd8pNV{cFFN+Z&m< z(bJ`Am%I|i^R$<|DoLBw6V^0e^}<5E=LTxFpQQuGjPDWVDVDijxj}NqcVPd8)Irv( z;;U&tH8JsVn!r_~tgVPnQ}#7q5rL9|L^o;ug1N6XC~ldS>>o9HzG@ZZy8G=|yRT)9JPQ`Nav#O~A&{U7%%Vb$6f175GZR!6w!5@6F8TFwsrsNo+AU zg8#TeafK|W?63y8tQ2-ZAtr<0#(62>AADBD_n9o;8DPb5A~cK!2K8PX2^xQ|`Z?{i})ru?8hl zeZJ1#_1VDgaQ*GlKGRF%666mnfE=zqAJXD!ydYh24xm46YEW_d9Y$@LO#Op1Vd>=Q ze`N&yE6P3~iHa#S+e;GuYFua;ch+;hzhr0sm ztuf-7yd?7W0%r=6T!+XqWPw(DJ+5gz$KMgAX1pBdn#Mm2lQR<{K~8<5`c2H`kL-%L zmYARPJkH^!T;wSEi7=B43oqF|VX_qb7+zvvyCqSy3Ff1(-D}j7w-{7xLQL57#c)5> zuO3l}r?QgxZ4r4ol;kJcC!J>i(})$RG4D&fEU-3DH0g^Hb}VVl%k|MV^^Sbin>2hp z8z9z9aETGQ9#C@v3ivJye8;lX4sz<)(x(ag(e7Bk5^qHteXZX6 z_{wq-AB{3#vW5I~o@he#G^;1=k?dlFl3`E8)ozrP|DxpK;#(%Baj&j|oKVe!>Cy!b zrsLx_^6cizg-B1MMKluj%NbXX^qGQnZcmx*HI226w@xg+D=yjIo4(TrnVp}ne`JmN zY<|gJ>n|g1*-i5W^&SI{Q1&(5oLFrXIPrxR!2)09_!exXmo;oC^Pg0nuZo=*rMVHO zw`$NFFg>fJUn*1CEEx7nLfIoY7*B19&C*z`P2)Tv6f0D6^s30PW!C;>d$gUVqEDco zF{agoP@0_^X|`?o$E1<`Dg6}GAX>b0#DssY{@2#>lh#xYY|O3)mi-}STrC|lyu`31 zH+8;nfk4u8hD*e4Y@;{jnKaQjtDn#b7Zdzp zZ=!zfA09npdLg8b>`dUVatiro!PAo%=}iO%YixCst2=<_DzbKstX@>VLaz?+DdzAJ;%(!8!rvBrfewi z?OC=P%{eHfYUasF)ELfd*5S8qU7zvp*`hAOVkA8vbGDDmJ&2bL$i6m!Sqt|2uNu^h z48FD-vto}BaJ1_9a6UjqO}s>D1modIf|7Buy04-$*bwe!rV~=Jr=(Bng?Ox10IIr4 z4+ca~g1@3liyj$GCZ*$gjsI~4JXqyeN+0@`bwLssP9eLLzPG8Dz2WdvS4`N`+HyQo zz&?~u%4+b4DI$$$NjIu)*lOeJ@C>E>7{su-VUR$(wG8w$(m#hQXe1ry^`_hTy5}uxc}D&eQDdH;Uk&(A^U} zPCEgvAK*_3_C{D&7T1*grHlI(xHRo9$^xV147aXwaZYcWEqEtt8smK`%~@Lx`jjxe zvOZgLqQiAHQ|Ub1uGVq06YIR_ci2EP($MO1*kF7Rjv8(qR7xvxwS0~c&RlNK;K?rE z9eC>GKfIl`ZCwdnc3G|eN#RzU^(JQVc%W@WIbd}K@nlWAP|iAK@SX6{S6OCBs(1dY z6Wo43z%u@;YAuJut=-sj>5IFyTtlss1xZc(^x{Wc&}RQJ9&2%P>+N(7zj2Y>etV-D zjeW)GGc)`mn05P>TD2t(wY%t9ozNE>msLU1iLCq&^I4W$4Z~TV8n;Y`V$%Sy;7vL_ zU9wlzbLFNyRzN2>AHFvCg5-G`65Gj|4si)!xbK3t0*0PYarlhm{LHfUz*`mFvw~%5 zE9MUS&|WUtAfIB<_-8k> zJoCygb^YL@qb5{KHSA-3S)iEEzYxi6$$%7{0ni=ifvy+RZkFf3F0J*v^VK>8%~ge# z#uvtW3LzZ1%H`eaa~s9aJ;_2`4aWL*Y;E3@1w6a#{9HpgyaP=%t?Y(3xtq0ddA$wO z531CsbN4D`7W0ae)et7ojab|DT;@Id==UmVM{RmHe2w~mNv%vyVQt}+LY)=q{G7`=j1q*WlKvm!WSf|9lFzrv zK3EI;ec5WQu^dkP^3MOXC-KeNwxQj1PJlGZ=%K+h;*SHF!^$|VxITY?@vTca&&F_j zcYy({TGsy*itaWP2vdA^F_Y-}QS_=Q@zLS-q=7*0&A~5EN0HQjtT`+pj4d4t{Qle)20dC25=aTUH6BNXDkfG;GMyh*6;o4xhhN1)%mdU?2I zUb)Wa@pa06g!h8j*X=()aA zK$dr4=Z)Fz_PN(8$M?nbMVh^=3$t^Cz1haVqPgSsg`c7Rnz5bNBlDWU#Wh8-{bvNj zRq1g4J5)pHQJl;^?;Ss;%2Awc(e6&^_!H_C^FmJ8k;>FU=3TV|k;zNCoV$z#`k^3g z$E*vQ=}*~P?Oc-VrFC$eU79j`3ZjoKHWVEY*T502t5uK5Dx&1{KnrOyoXZQQo zeSF)P2weuF^L(n1FU~B&Z2qz$u7ATupI;nV13_?p#qOI;q2a1L@R7OF6~u&-In+QI z3xm(sheV}%Wo1Wc_jPikGlWMG0_C!S*7d;7x{>6hy4t&3*0#S#)}U2Gq}Urc=|0DT z!NYcpbo}4pgU>xE^CCHH*cM$nCTffi0;aXGWa(TF%=NdVp9yG1xlx@M^LDp6j1Zjc zvgs9cEIjW#&Zjag*^(x0YA{|A@37$&-yqR_uj5gHGnl_M&m&-eL^4m}ypZqQ?^C6h zy3(@#-@H4iSn;Aac$s3jNv>|4uOWw|jkv`?_c*5lRx;GoRJyZ$0DpEi3V)hjm$LoY z`#=%xZ&d0u2FLDMDV}u$ol)yTtM7r4B0UjR3)|J zD~Y+d^KRZf8 zK71@vnHti$gk(l57>Ca*uqT@%&<{o%P73zy1YX~8BCvCt)>4Z+wo^}RD^dqtIdtk9 z?bX@zEv*Nu4G$b0r=N~=r%uD$wpj=-RFivU=FRi2%Cndlu2pjy>z6;bZTpu5Pf(nC zX=Tsc_zdn~FvRfAaqfIm`yX-zQz*2W<;Yk=rnc3brxNaFcC1Dsqddqf&+T}zwYO`& zHkrNlZMD);52x*pq;*a7hO0G*K^isk9O3hv?RCO1)>sj*`N)!O3;n#c9byi< z<5)2@lu2-#)1Hy%Q}(gt=QVb)7zEgU$=zcDd*o8b)325W(%fP%p>y&v(C9ULe~&_| zd`TyZP^6q;xc0BcY6+RIheBXw^e*`eje!nS>tfuNX@37QSW_hzbeW_xvnqApK&o#kMbw?2Z%@njOK z(YIoHU9HFRIeyi&>6OnQz1q`B11tMFtAB-h6=Xc7P4<-yH;t<@H>ZPCi|zC8PROy3 zb=)Zz`BCIy_SE%hHLJGTgKNUCm?s;*mhm}HAL1d7GrT6g86WBL#t$o*aJ+3A?7EWPcYo4h=OZy}8N#XsH_?@Zdgcry}2w@R?yA!qpb z#;bW@vqRP^=XTji_*CLC$C)5x{FYEH#)3f*VYPIdIx(~!B%3OI7e>`$b7y^Es;B=Q zTUTV5&thlETM{9BWV`ddva+rvf1>X6RZBC%P6e{F9#Lm0;Dg|=v$d~^7&5YVuHCZP zaim_!Z#7w}^ZJ1MX9jIPwE2|GItLQ)GGor(cq2#FesR>N_LOHQ@sm_*o%iEvQH!=)k9*ukctp)?tT5316Q^-&oJ4C_bJ6c#l zfV~TDeS7#?hWCB~lj?{4k2^`7l9gm3lOE%ioNhWe3#ReT`%YtvA!pJun}1xRctYZQ zHN)L42yt`vnU+iCBG3$RvD(YG%=Sapd2)q&4^`vzl_i@RJ;iw?wFkd03}ID7sC>;k zal*mfas8_(Nvgl>x2}F}N4dv;@S7N6Xg}2u5ysFwUm+>5iHUz;1w3UT?Dw;ieUlLH zb*?C=q{{N{B9{jtZn}%xrvz1!2-+f* zorG>(?XBUEbnfc@qBD00SN(vlG_q&_O8Hg$4!R? zmy+9k>bL}S{}&4`2C{D51v(#NrL(=e6YEzfjPNl{6&KE${e>X?(bN0&b*23N&jZd0 z_3KA!3fj0B1M>^J-^Xs6TA*?<<^4)oq<0+ z0VFX3)IWCcm>}T-%1mj_t?ai)nRA$Xzqn zlSnVEcm>+QN`8zBwYUb1wL546CqMRVOP@pt*s}K&H@WTdTmMiRvY8oo)+a=i!!i+D zac5b)djdiVc4Ib^)U{=HS5{*uKQww@sI+d_ppa(9O0&9*xwgFEFM~I3Nclf?@|}6L zS)2;&U6npdN>!bSj2a$^&tte204jHi+-Wyzk=8kOU%;8^c&+pdYu8W8`i3>>V2JUAC z#1Ei%2dK1OSWeC~Y#T3r33}pi4$EBhBDTxrP15%@)7H>m-Eej8XPFe!M<|HJNCrju z+BXbq40d_)Fc$^qQAQzj=*zx-0 z?CO*3mj-TcL|9c+fNWbs zx0jR%>^qN0Se%zbYBw&~;`zQtrG98Gl+W+wVG3@?_+K`fK*_->(-;<-TgA2&eLqMW z6!eEE-d^8&YR@@)3*1n*uBKI%8{RNeyI3(R+Q;H2dVnyj7icg7W15%mw&?d$^&BBs zD&Lqp9KiIePM92j9Yr)OeBlA-{bpS76}5777R$S7KWX~;dg`)z_ox11t;iwbta-Vp zKJYnq7yr{iN1lSWQ>*pyj;z9{rurRZpB-D~UO3E}qnggbM_xFzz?7>TuM}k+DI(aq z91DP*g6IY|o3V<;TGcYWvp2iDj{bc9!vIx&R&L;|Zl5WsNx?FPUKX3l? zV$RIGm^0_jIro0==RWs&5`3#;wHN7^#Y+zo<0!^_ESy{#6o|&@o70THCo0sN9kgk( zUnO?BWgdyTmz^iZbUdC^ik!L?i2l*nDNP3qnAZXsy)^de8FQVw^`~)twnm%Q#8`6X zBpjBVx;^W6WAnLerUSNfjvnt!%FeaMSQpysY|V~e`D*Xy#~d`R`Ny?KEcgd*KVFsP z&q)-Gpz}#Ow~i#ks49JM(BozF{|X<>qQ*& z;?&4a263mJqp7wb%VR~4tM3ede4YAdd5(5Io$A#Rj_>~)C@^kXUiZhUKRoVASn4^w zSb$1+c+6k;u02L9<@?IU9CVd-q)v1`lBID8n)7S_);1F6KKg4q_^2Pp3{qEC!M@X49jmQNyFE9=!I}+R} z%6dQpR+*0smCOCneDhW3dR+5A+wC3+Ymc^x31oppXpXe+{Zr%IQ{x@|55F4rSdiNu zEfg{jKN8~>Pv2>37;Jv@y*G5%7k&7s(hkXu#M}Z}tuo%EV{VH?;iCCK)G2~~)vxg3 z{h8X>Tj>W=zK!@0eDoDB=ZpMf{_^oq-VkQ-2Zbl!AoG!aN!uB}3d9DZV1in(OY%d= zF}08NN@JCa=*8gV4`(vKar+c%jDeQ2sf z^#j;R^+N{B5IPOat(r=wn)(-#2v$k0t-Z6F_r1LA47}dR%j{ge-1fAb-wyDzE{xgV z3;czR2~MIW*-s#BNbV^U9Em;C2|IHW&)?rp6#WwN#U4-g&df?m6QM=Ad@#?xhkB3r z`;!0WozjXNLO#5T>4W%E9X|eLRsE~>b=S1%5W;w8v66n0%oo(9icjE7 z!ekZ+Qf5qdKDp|%$2xzh^$@lx?`J2vXcvy-g(m#N2lh6e?)sXliV)B%q~4}5vNb!) zXQ;Z3t+RReIH#up@e_wDDwkC`u#IiGB_VpHtV_jDH{J;UAOu&u+AHR9%7~6A)q0&= z95rX>Qd>~XgX&H5x$EC#KZL)|@jBVF&W7uT#-6O*S!Qsalc>z~gwpP-IXzsY1jpW6 zK67tWdfXdhQ=}lAbx=soS9+gKIk#JA>$Y~0)Rnel?VeS)7>GM%t*^XMgI)X}>9XDQ zkh-N`)3tZ@Ka9g(#czO%AtW*W?y^VPOeG`qlYx35^+t=pW#Lp zeX^5vi930c{|plO93t5J^P|KVGFa9lt>MWKom7H+$=vR-3aS#@@ zYnam1_k9w=R@wN8Am8VaSDQ{7{B#((U@xV^#L+D2tR$hy50$(4SKm*^3fPJ5!dokRNx?z@e42_w{6ytx z$RYV;-Hak(Md<~MQqFn3pX|+sm|dl51WU85SWcx^38jj>iLF~G#F_O;tAgHkc6BMw zYc~mR_>kgWtUVRIo(qe3W``K+)<8hN)%1hvDj9d?Oayk>BsYGl^XeaUDGz+M^9Hd= zrreGw2s%q{?U@zr41d5ZUb9oz*M(VfB}Al-!ExVNp1%oBHvQ|W zWB4imIe+upN4@N*O)|wTu23K&-{7kme#cyYFJZ@R!{zCS3qdz|67e-JW%0o_3ir~^ zi0X|2LEnvh6oiBwV)hz%IwhV((lRBk!t9>2c`+5B*fKVCo%K~iJ<%+;$;-uU>g4M@ zTa8K0^uYtRY879Zx$Hj^`gA6zIF`}U8YfDZc!X)-pws!}Q?+G>`>m|q(J3vnE7_&R z(z}T!b<#+Ig#ZTr1$wEO-=Bx}ez)}BrT35PCHK;E`|`KNpo@nN_;ILRDX(esZ0lu2 zWpeI%w@lA|?~*Ye+u{~O0%n$Hj3{wu;*(9Py~ZF5iipGsQuh}JCCq2Ixi z$K|+;*9ic;!q{hA`8n#+t7;#o&6VQ7-T7A$?waBmAr5Ie(>KbIIRy-S#b>Xcb|_5c z?y(ndS2?PYI}~T;@N3mwk8L)eQBzW==jzr^6@MMK*f`Uiqbc)qwHH~5DK}K?@R5tA<7XG0{Yw&^=QdRa z%G3-Ves58tO&#J1=U>W}W&bHIICn#GT9s(F;FKQgN8X>3DP^m5hz_AX?ufy z-&Y>}<{TLDyg6G~(4anP*7!Mp)2iX5tK%i?k|t*f+cL&pF=p^f=_#X``&Hx@!%1-g zFSaMXve7*8ZHr&xSfIX+e{=SJr7PRGH;0Ekx1=W>_*Iiqckp`8L93=Z{Q0WVKa=1Q zCWT9tPY&hwN}5umObQc<(L=@M4iDKpEb|_F&bUL*RiX!*IHT;?a`yA7&;2ADNlD|g zzy5S6&D9%seg2e;)>gw_SCK7yc5-@L$Bke7s~rEfbqPFsWVCstdfK(8UWEqi>8Y5> zzd!bFe1-O|iTCu4T>`(hF#P3de5!@h@2XLC?4(&uBL2~?IKgTq&mD>C=JavmeHwrG z%EG(H$4gk$86gy#KO0!&8Rm+g{LFFs{w;ehZu%`D3-oQ4H?8-=u=1#fMIVDbJvH&N zU3<>kxz)3`=f>JS6M46G(|^a@|32@0q1*Xt-~B^|v2NAm`mugAO0Y>Cc$VA z;c)a!gZ$vXfVfJmYpkMg+;$jJd5vOUdOM6$71O}Kkumj>7t)~RAi6>~H;kPx%pTmS zj{MvZm>q9->DG~up6^_$w757qq0)F-r1#~3Abj~C0B@*(ny>vpN!c*39Puil2Rqx1 z!fkZTp8IW!_r%M;K87%$7~bgSNWE~_2Q4AmBMao2C-VRU3%%y$ zcjX-OUY4I@TQp7?H}s3`PPHkk%-P3-R+{dPcGmK)jYkp5DQvG9OznxSopKH!E&_Ic zUu~$Gykn`Q5#wdfNYNyQ{vCEyR?g`YP4*S*O5PP=1NDuMm@bp!T6PRCN z9+v{1=NL^|)}|BxwTh+denC$OY6o78z1*+qFVD!JqMvP93_B{}qQMTYns^HjI+tZKp;mzpd8TPQ_0@^?%E1ZASB% zWE(yQdmvQxr|A#6JSWpE600@rzL)MO8M@jL)=hs<_GJ=H&`L|5 zgm3u9na@A6f%`4-m%7;jR z^1e$&yYnE5-eq2`79H{dML*M)h z^QOfFG}(K0JB|HE*Vp=LTaB3Y7Y|uHrN{xW%3P>M$5q1z4I{e-KL0sD)MQvlJoU@| z(YDIxqgjijgw&0%IxPNXrT5oEii270G%RPHY-Z(?mhsz%HvMGu90VVHy1HzP{#6|f zP3EJb*{4vx4wfgVo=y#K8M_c65El-OF#W|+IcO}G11Bc@FlP2vJSz&HfU{_kH)}1D zpJ2;;U-z3}fE0mI_P4I@e*dgYo^dPy+d(?telDmu9)Jlr%GohRH*k3Z;s^UN0v-!P zL8od2+Hol;T)f4S>^J>#cRO<60{&OUw((U2b?!~BSbt8bjE~VN8@cVa*-M{;lR=uK znuO|K3*H10Y&18gwjvfnXkjw-_*D-}5My9ly`W;T=*$Ay7L2OqrNjk6FP~5RiQdjP zJBx+t4b`_BSE2f$`j9V%GL_#Z3{jjKaM`LKw=g(4rIK|k*Q+=kUB|-{ig95kzdymr z@{LbD|D>`x=NKk)SF`@+f&3{gCvcB4AC+c{Ixm$cKZ*;nwDud6{i{sN^Y}`xZ~ycZ zJECliNEo*rUp15Yd%qqJN*1n7Q2p0-q3fBnPiHCRZjn@;EXBi`a{^tYJYqC?G@0`BJ+$GpA}Ur>;ta86JLhU1OG~MhP4o5#-)U zqk1!$05T2|Tn;t9c8*~uBVcnmQ^Rwmgb6a*K%{(Zsd?vs5@F8dy8<%G8m9m!)25!u zV=6ZgcFtu=6uFzr#SP2j-0ji;GMNplq%@s9jmk{RkQvmU%%yL3EJVm$s&ui3Qm;!C*b+OhxM+tC zm}VsRNO`+UGDjqN*f*7WIfx>Xr2MP_br*sg$G)YD?x- zmQx%7&Fa-yO{qPlp!8y48xEF?Pj)#gndE}GWliYe$Z`H|u~Sx0=F!YoaW*}6B||~* zW(U27p&ZX#?CvtRwOFDRc0YBgFiux@NxY|(X^FV(SH zTDEHx6v`Q!((oiFB>9wK1~+ngCvadczEP=cR61rhN^d6gm)jMypQZk!dZ~kZv6R+8^FZlLV(fAU% z-$$@L3mt#Rxva9$!70Zg)3BFoEJ0+W2`GGHjRa1XDl^Y)3H|`n!M0FW5Dvt2WRU)n zBe|hAe zY~>bWbIf)r!^Z*~W81PM>S3o;b`3(szoab`U2a0d<8odWI=1X*m1pUKek(S%T`1zj z8~Hj6BFBvGg`i2$x8nzA&$Io0F<-@(P?PZj4*J*b6os#k1bse6f0B5`q_@WWRB?D2 z_=rEgfHv(H$3E;YTDC*&_}zfTT}jc$sYUzKpP32ZLs_E&kdNN?U~=5mQx6Mrg%8+^-| zX>&7h=NJUX;KuhS&LpdboR+1T(JFn6kMz*H8GN1AA)M=&%0_C0a2_oFJ7}Av+CIkl zLt{$Rf4?SK)i5#(!ss#x-e>Zjx^;BPv1^IXZR#-PE>(5mPGrw6>18{N4RGam($>ex zQ;ivu)5=lIchZ{i*Ei;@-Y2J6a}?(sJ^Vzl&mE|wate2+DE{hlN>~2XSkaP`|Jhop z@^Esj>#3QVlK+flTdecGp-*4l)_@NMAq%I?^Dgml11@1jAkem+v$`ys;(V-eJ~p`D zlBwuYxEK}LUQR*9T>k~7mX?{LF{y$`yPISUVKXmz#U>KiCnLrO6lc3kfcbw=^LnyA zv^!=eB_`8gk;XOlLcBftcb3jq1I+>o90c2hM+as3V~SN(t+v&=U$~jQ;>i$gZl-Zg z*RGDt+bCuXtY9E;jiRmtrU+#6d0_WJta(ezVsPXx;7z4Ar6VB-b zBWz{Fd&s+%3X}A{gnv~Hs7kEp;mV!WRkeMkx0{cv?rZ5)kK77)K9x?(jH(sXCgQAN z6G}gSY~=wcq{Q%D+GrV_*{MQDm6-x z0y@olNz^C%l=o_}y0Zg*eSEAc3h9doI{rRqdqE}oH@3W)6 zwWI}>imSFvKo!GknUzMutw)tYx`FzJ=41ZV9Ul#Ea^gGpbf)#YE6KKmjb_Ej42&&f zJ96~jc@yRDCB=&i-O_4E%?7*`7vc-pExU++xjQqn6#{iuo*pTvP)d=NXiojj88-vU zb}miOuThrNuVGKu&ji>oYxaZmjRi$+EA};8%8p?DWBR@-h^efAg@tE=61@tgIel|w zYQ?2>vlI+^hp>YBjvD*61bP%`)-vHg3!DmJ-Xvr8+@}><0+*1gu^94Mf86ej`Es84 zZ)TRZ!Sl|aOr=E3KF<>7Hkx%$ok3@5H5rY&g_Z^kj|K5$-h2+4UETZ1md#ERr51=-cay*PNDQ%u%kk$V_u=O zL~+zE$8gfl?90{9hSE-zY0e_7UxKG5!DhvFdxDk^kk-IK!D&8?#=RSjs<@1$rZG}# z4gG-^yHO=-U)?M&W%cli)cR65virTI3pE{8L%E@yYIH&^^f!FiPBB4<x_^RQ)?wapEXbe0p z=$JwjbkCJ+!VGc6gSa}}sdzwKfpRD_!Hb<#jBqmM0{P`%6Td8qAYRM-cBs_<;R!f| z5F8?b&SUhT-is!3m$xnk&=9|%ufYihU7PA)^=gERPSKbjs@?wR?@UnZ%w>yFMdm>VO~CmGXCOCo+YlAFMm zay#4hPXBCVB;E>sySF1&_Y`P`gyUDv()(r>Rg@VN< zHXJI_l0o4CLYh}Tm|&r6*rpR5Q~25vK*gd>kY48E_RI80#ykT<;ODaQ3KMn{wI=uPB==KV~WI^XI}*jN$oUpNs;5(`?u z4tw|y8Je~5a63|Iv~~pW&_?l=-d`0~Ml2^Jns(U^bw3z=T}_Vs51M)TT1Up`BiM_L zVZPFe)!~%3W~@Cx*@;)^+~65M?C#QXX4BgzQnVA`Tdhe`#5gaX=Zy@#{;=$a2QDQB zHmXwXk%PCYX;w%fw@{(B@njtl)Zg42*8hNw)WF6BOd6SprEdM4JZheFz6(9&CM@X5)nPfz167nv5G_WFb`WXQR4!4Ws_zvz)qPwsGy;@2_k z)*{RZ@n=XcS0$R{S#{qwfi_l%3cJ5nZ@+Ns^t;Kk68`)ahsKBuFUG2llMKNCM5JI! z6W1AxQwWgTOjZxSr!BoKgqNP5oLXBpVWDz~(PPXZn4b@y$~#KrZ>f>3YY!NrJ%lq8 zQ~>^N?51+7Kn+$F?Y9HJ^8AGIOwNi!(zeJn%;G}HkcU$bxmeH%_WK{iW#giZKoiA0 zBaB{p;%+n&2iEGdbBEVlYzBEx4;}wrX6Qoi4H#fUN!cbH>NF2UEj${gdJJ_L#&AYq zbHZfeg7xW5967*E;TZT-^`*f}@4Zr(LsMR}MX;Vaq>Tvum|<9KHIQK^_-(sWxqC(> zCI=sX6mYPqRdrplh~IvbvR1oZ1~}f5B3(rQdCR@ zBgo*b)oR9#1`yOAseOMuO(cFo2@94$56s`52mLo#9VXO(P+@3U(tU#g zT=MPNy4~emsvW&E3D{i*YPq~}JWJOh_ny68Qbb=NWrPrl1NU&NcPa~6^zh32n{rbt zRm9oE2n>Z`9&8v5aOpiki8x5h`^)+O$4nyB2QuSBkHDf2Gt)a)qg4S9b=@Ck@KL*J zkN#&|i1{Q>rh+SEjElQvnP6QQW}ZxYh(mLd!s0nTH_Mv$L&6?{Yzl;LiaZV#si|o| z5ZSD&-YQ$X#mm2ZiS!P~6xqtSw<_e-%$QeZN)bcz_4*{9Lm62@8eP^J#f=G&g<3(} zBq8RT(*(ff3_sr()%l9o4z!l7{sSq-g*xIQa+2n7?Q^*9CY(1CnF*A6?V~I*TSA}w zUJ80CY@D&9%F%?zFHXeG`yAkBJl|g@s~d@>l@oS*d+)ei5F3Irfn}gq zZGAhwLqk{(6-?kBTJXIVYPSwgWr68F9S~Y`eciox3^z7d=GXL^yzY36YtF*BI`6W-?%*!fjvvDfyd7BMvK&`{h)j zc_lIp6eftuwAXlLjAQ9~MzXym5u!S~sT%I52S~%2l_aa1YFx}tv z_8}^XYa%crfWqJ^y$2fh7e`0DkexQ}hkb%g=C2T)3s`?ImgL{LE7y>KfoxZDHPC5*e{PFcGjW zYSU{eE*A)CBSQ~LqzUg0v?u?2iW*QBqaYL~kG(^Y98s~ zAe&p?PfM!djHv+T{(ssOg>PuFCEz!=K>?{J;;TDb<1+v5XvXgZK=)!u-j$nyPVZSQ z1^I25xn!eX%bwj6?7rBLOu28Vex{SS%~I?9MJ__xCzs~cIo}w8l8RkLXWv2ef0@8u zk$zYRsV9y}TF96}3vpfc^*2pX+ns4v?j=7B6oLd`4ws@8Z~2l*@X1LF{X+78L}a}x zGL@0J>NoeTq|Xaq7zc*xi<|bQxo{wx!!d_PmIjC}dZx8;6i1;2X0L2xP1yqM57&0QJ>w#~Dp1*jI>vJ!zsWtzQWo z7s8_tkq?p&mrw7U10fphr~;Ql9lY>!fGfCGEUg?1q&i85{;gtGdFp*0-gXTvTg&h> zl^G9f+ajBnJ`?pSPfN}cK;9+VH~nfheS%-jHXgLi<^36dR#`yb3NwWz@iBVS(L^b) z199gpREPuEwm~&7hv=O{=j8HoVIC5E>iG~27l?)gij}3nsO(7WP1E=`FaHuX4b(J@ zvL0~@bM`r~fE+wUU8)@q88ctVKgENIG(ij(P_AX~Kee3>;06|C0JLdQBqVgd($TMs zSy-ema`f*$^Wvk_Ss7Y979J`t92CO+kx9)?h_SL9Y^Xt%Fspo?ltC^(Q?08~!wrZp zMv#+IekJFFUx2)1@EKzasKE9*=@c-*s<6ytg(z*pW2(=I!xfi(nh%diI?|c-oKc_) zga_V=3MiN^;6sntLQWkVB!7K-Mx+j3x02el{G9tW{zu<7!(6lJb0{~9Gu6R+i#h-vE_XQyYNRY@Nr3AJ&kKjjJ0*ZMHM*{j z{AF~K`4hOgW*53|g7MLc9~VC#ymofvm+-!Y09KRZcYxm0+w>6!`%t30F4jZ7rVf8Z z+Er;@=#am0!K?QMhoCN+{nh&UV5eZ{>K$CWB5s)^?2$_IA~WQ>E+^&_rsc*JYZAXgcq|SxFbhBDS4yGIeBcmj^yjym2>oX1 z=lF~);~x26rPtjf7FLntZX+>VF6>M_)5uT@0Sgskcfp+eQ_CXG9%JV_fCJ=wlL@@_ z4E>K9j{90Gav>0~&DvLK>8}PVAV6K_!)d5D%xT9)#q39a=6rj@h>ZuIFLS#x3+;Si zW37Da|0fHv2uid9#O&qz3L!g<63U5?^95}?qxUuv$VSEBvf(01V5`;xR4#%UeH`s> zRoqw@?l%ssE6gZqI4O+5@RAme_&65;P2QZ{JNS;SUvlwDfp!!ujINCDs)`7aDUdGo z0PjyulW61uOadK;Bdgv7vT9Edf1x|C8czW%zV)Myn(<*3!q01pFYg-c7h82 zL&tb~ep-7YQiLzNTDU|8ABe5>^*^Vv>na5;R*jA)Nub~Z*XGGx(+*JG~ z!v^{Q24uvSAiWc^blY&>+72tR*m^me737}^@9NW*aK9y>$EDXaA|SEq4ZQ5`ZLFE> zXBuFZT+qD@?N~5Y6;qfH@&d1eqmp1tHv9^SSP9m%%|87PS>b*xlP_d2;QbNl%@%T1 zFcw=A97l0Y>agCzF@}q-iNXllqUj?rTENzz>+=40^I!_sjuxGQjhGkQ zLf*AKLs_cb4zpXYR5O|@bREKOR`}jV?_ILO^6^pcT^MBZypJKa$=rYe9~9qCYPqD( zM?Z10%loD`+5FAVfF^1dW;dnwflz52pjHxzS`rTa~@6bOHgtJWsNS z_rm$`!mntsC}mya;QOIRuN8L-@Swb4_tU{Lg6A6~vM3U=OghF&YI8>^a8rsO|M_x_ zt&u#?8Fr)H7Kp18t!6WSX-?znxR4la85vFHC|Wal66jrZVG6jhKov@qxlHXZJ%P@W z`5t1>1VoO9O!f5eI}z^b-(=22_{}Um!DHe|iho?kRNyh0NFJZh?BqM`jZ8-t3C!uq zItll-+00xh?eTH_3LJ~%kA;|PR78$0c8C1aRk+A(*F|)LFdk5*u>*@#hw2)QjSSB5n^=n+iTDv+o(wBG$7Yhi^t>?D{iwm1aU zY9Ws$J8voPL`hI)kgi~hFUl%~R478=y64^#P5F%~CiFC%5#xzh)?ieeBWvOR(o|nW zMF`0kA$s^zM!3APRQ@S7GW$XD>!SM`3GlZ#(P{IyiaF=8x-j$E?pbTrp#N+nG)4)Z z?`&G(L+62^i>F;bJUpb3otb`H7`_g1^dS?P9*(#@aI#Hu5CL~3wIR>XSxYzx!!1w> zMO?FeV)pWMo-p;oP~)HPErh`CxahF9q8qRBl4J}8GN~lwOLi3t73w9^+F2@{aW9+P zK?Ty3I>|BTPazR{f`y0na+5s2oCJFasWebvShpcr&6JMm#-th_06cJzRq#Sz7G$T= zh7A>rHw6Q4y_PN=nZXC{sRh}6CpzAp2$aq1hKIi5)+u{ODDTP*lS25#6E(6Ve}|%e zdT1EDqH!g}_hCxZeAr}-8B=mxNWxY6nP>oGiC{t#lFI-QQ2ZV{<+ z&LfTpKTdesurZf~vIDH6*7~Gi>ia=OjNb+D*xl2trVl}gT2}~qv+hV?Fo=VH1y?us z8q#ZTHe4jFYp*I(wwc#}xe%k9>w+tH%RShUt+@;6PN6`ravne^GdfkOuDo}^9|V;{ zfjq3LVxv|G{=%iKTMZRACc-hE@cf%SL2qJ#hIfdx(f`e!bU8cajE&kfQr2 zc+XqpB=JB6EIIrgps@eY(<6mG`zFgw_xZ@AmHVcz0}D#Y3nj?#!`G+8!l!5=17seA}R=fgZ7>UQwo z;L_NW$yj#l&VToQg6hzqQ?vZ{Nv{nz^;-ePuC=J&XO#`+#swX`Y+_c})`NNCp#NsC z)0%fuF^!KPt$A*gpz)#7NCXTML9IeBn{i##@i)j2Yn>D3K?EJ^y4zcNDgg>xMbh_c zaN?$>DBt27Rp{ds64g(zggCEI5~b9#0Op}6T?#~{?)98p5G`z)z}kQmV=jN|1`BLw z^oPhWL}QXjFRhbHU$9@i24B2D=k3er4id^>gHYPmZY2!-rL5pmBIwVaCA9d76YQ7{ zs)kM?fNccGqpkr-V}%tA{C>DVNo~oy#-cssx2NH=NL%e=y}|uGLTCvUK;tS-A)zvPE^Di$fBy?54YbhIeAnhU_~tGtMkJx=hp=C zUEDCPXiZ&X(p)U~82fnKjaGIni2}-;RK|48QYLy{;Rh|elYYhg$*AH_IvkfvXF+IQ zwCFf#D8SG3u(uabmarfqr0e^!_2odzpj>>u6AEUiC*tSNnZC-3JB~l=>P33T~R~RNjg5sX} z*CeYijv|!c4XTQGHMIj-VHZ}W*V8bEXyDnbe+v)gXto3;9wBFy>MCK9ivVqg`bG?5 z&lV-Ffa)N zsPTl;`<8jx0|B`8c@IKP=~0IcLKi>q+$!R0NBIfO;B+N_4F6y%8gr10K3)e!fZ> zW3F&}2xFJ@$*=Wap@gjXpl}N5+O(B~^ixEk7ts!-Pq=^Oh;DWB&Sd00CJNG^KM&B+ zwA>vEse%~-)};uzpbX*a$l8;{*xIKqOkw9pTl+D_>LQ-`bZ|j-=%pkr(uc-IqU_)z z8aovh%3W*7>*7LK@6FVMt`*tyetRKLhX=DJs+oPZ=}ZXqW+L{Xu3}m*+dp6~ICHC5 zatJbq1mU4p$*d2{85^0-*fyzbTsIabsQBs3LsRw$iaz2AmqQ^$Vq^Ai)s`N;y-_z^SwV}7;-gt74mTN|KR2tl8(+j;XmJNL)u8tn%#KTIteCE>TB2-#2jPA^|qy9 zr%dR-*6zPv9{?fo5ttZZ_sk`4HZWVvLg;T=)}OS|e&(cgyjME5WtkvpTIe=KnmEVU z2vcG@5^?jpM^*MGgWv)C_`^mkO|2_8Q_CFEu+Kk-Wsx4xnTa_MwLXmt7?J?hcf%%v z8~fW9)1SgM*$be@UFotfYG9tBUGr@?pNODaq#g@C`O508gs2O$bxiSQ>ra|Y0E|Z7gQpts@!>`hLD}@MLqa#~LJuZUpRAy_O zpV%~g>wI}c@b_Rcy%h};|YS^J9dLnnO+_v>NVu1u08g$xO z^=W(!;0r{1^GsMD03N7e4_#l!{{stGKbSm+TPh!9-*)s{EEB_O3NRDX^G zd3gI3HWh50;rbJB_nAB$H_SoXPqV1Kl@+-hjftr9_?5RB2tY~6-`;%FtccyO{S}6p z4c9TxFDC={aG?htZqzEmv7Yjxv$uU&mzjFSl79z4N=q&qXuiwBbQxAM4P zP;T28_c$>3+ge!~X=delSQ9 z5pwHzULSXYnmwNj&5INr2RuyNr}itUfRMUslITvdP<^#6`8+~ceo-#JKIIwck0L^zN0o%)<+Y;xiqW#8YCAZ<$H<#!qoV7u) zDi{WCN+=|ZyDcA12n%#OsH)nvBu3XVw^+BcnyZX|WXf%u&}`*tQgH1CwSdD#M}!n# zsvQtR&%r#kbgjAxHuT7rFbv~Emv%&P=o2I&OSI%jDQ4VT?=24+ey<{f8>_2j@geHI z3cM|6Lja!-x(z*E0v%c91OkxVPfDd_WLU)S{k^JW2Qj?7R<*SaZ6(rctm5jl0LNkK4}(*_$ClP;sq;@zgOy@9tvcd-b1V2l@&Ic zmo?zSEMh; zJEZq)J&mo~Ibhiem1%ZctZcvhyxxt)J>~jeV5N;ez!-!kGGC?%5oAPhFiUSC%-=R3 zsF3TKm`*CA`j8bO^f60MV8M-Ci5NZb*X`MeP|hgcFjJTs>PfrE!pAiTvvmDkJoI3i z3aA0PuihiDbK?!r6M?#@cok`6a2*HQzHT~Yc}D%8^Qa%FLCwd=-(9ESTK zC@mb({YUO+eL+kl4{4~^hLCuJB@;0odSD`{@GIjq(}e~KbbkrCQzAfSYvoZrG}`d^ zN+G*5MbDO+Y2Mr#0v^0j><<^k*Ne$Szz<|HDX)zZhUT8w>}$A&vaGk&1yR}{WV_n) zR>fyNzyc}IwAzSiE%-qt2#Vm>A zI=V?qeNBeSQ;1-Oh;b)5r7R|*ZX;=SxotccS3Smv;2BlZjys+V7ukEZ=OLMob#C?OB zuzv8YPy^8d2U&V7{^ij?gPsA`Q~k`&jV#;VyFl9Lp+57h6HhIZAwlMc?9^(%RUrq| zC=%t1hXq{tG7)^aOkSD*E+I$fYhR=6amfL@c?C5+mZ_dkq6Z!EAYBR&^--~4YLpsF zh-SR4{(v~l77INY<`Syo+enOBlnolYCJ_?(XB~}+!c^?3;{J&Pg}p--`4Hd98Y7>; zPk~hLWY(0GK41eSwerpfEOUM>ZOOS3!mVq%FJp&46Qh{athn{1@0BRSVHp?;+@SIEmGjKSPy zp<#ru+_!YUb?*Gx=Np#EelqBW^=$=25CKqJy{m2{x6`})6mZFk!m0>P*vH)`N03=t zU4LUc4r@>T{uGtl`j2kYyIENfz(<_`&u}3^SFNYQ`J=&Y(HG=6jWHAbTaW>!|J<-&~CGwFM(Y)c3~dY!xTzC-71V1ZQ3YWE}aSAW&4 zR&W9PyT&Q;HI8ZHE^}2?b+_+%C@|__nU7%i(vr^e&J9Wz1dioI9W~i@T;7b9_u}|~ zTJnjxb~sZbp0swA{@O*$!+n!kr04JDGn**sb;m}d_SN2}duOqy!HzFVk^)Pm0=;n0 zT3=M2J7wOr&f%LJ)0&z)Voa@? zPY749&frX*G5o<^m6Lis(y7T*weaWDffNff&SrLZ`;lv(#lx>q ztf$w1^t{!=E}iKNcJbKo#Rfa5Ja^{8=|crH*Y5+Zr_syi7ht=q|co1L!xazpL=Q3Ez??pPS41uUX~KaKN*@ zQ0^89SHU~4oLO)c7YeHVUh5X^t)=a`b{Z}+rmS7EIy2L`ar`gCJ0Y6Cvn?^4`X*23 zQuYmOt=arxevUrZ$??iS&Lx71$@JCfB_c~c*e*Ud3qRbf4Y-uSf<+t8amMZry0iaoqE`38FC+lT$!aSWjfnT#)L8v_QumbSi`+{KcxA;8eli10{0>E ze`xbN#;2p7D#g{1(%}(!u;3gfuoT~Fn0VQ@(|LfgVnYm;YdiA?wf?a(hZnXmHhtx+%FBrC2V^PZR(itEmN99njH8z zhj3w53mYI8xJ#t7s_S@__LzOs{TX=%a?MzGGqREIT{kKK(-g)tco^7Zc5U`7Hb8UJ zigRNaudX4;<770w9&2s4e^dN?9d2(VLuJR-OIz^1vr(SCTGX4fzMP%)h2x|5(yrG0 zymaj`d*I2|QOHFv8*DA-=62aenMha2?Wh}v6TEsq&^#Pv8rUtv2uIaIeewIJ$juu= zx7)p}SFb2xPY4GAyDI^`(t&oJ!CLt@nZ@(omZ=6Fq=?apCxS>CN`yVpDw(}7yX7C*owsPQQj)o+vj$SRu6ET#|t8^ zflJaMot;}JX@GLC?U~J&JR7u;`pi?YTeZ{KnrlNB8-@|N!;Gy`-(>fSfmy$7c*oB@ z*nIFL0dv#*+B??Gt<-}th6*rsJha=$suSZUMcV1KD-9*TlT4N-1*6K>&kExtOg07KEKF;E#pyUxwPk7E+CWdnQ zo34B<&px}UKISE&`jPW|p5@_U>r`Xem#Qb1eOP$X*>OEG$NPs;U+77%(hy34)6l45 zU6|;9u;+W};u`_BDQ@pLvcqjGI%aeyuZoFd1=%9pjZ?#9JN2IJHT+x1*`oPK9cOp4 z7d{XTdu=PlvfSzA6!Rgdy6}#o(RA&|%SW^+2-n8Fxzdt6w9A^JFi)GJO{3M3Y}U+b9qSzHZC=Yh82F4s&ttfj12jHQ><2$r}p{yrt4ep zkZJ~=a#`i|$JXq+$N$|(3SVhohbe2IrCE&UMCJ6~mAQ`7vqa)ljyCBZ&Nc|#{~#l= zy?s%W{^Tk)mL>4Rn$4r3;6$l`j7~{Ia98BvJ+1Sh5}WE(?xh1#)yzEOHiuQeo{wD= zK7AGJo7wHZ6>q6&U7c&>3XrUE+6+NTpRaooK5SAN!^8t(#zRhKZC~atr)4>IR&^ep z>W;#Rmz}vY$670~hT+^ipROK`6zY|AU#Bo!zjmIy`OoyiP%G~o+3K)ot@uZgxVUtx zawq4>ffl@FV4G{nY>r#|3PAka$~NbA-R1Axn)f48cG^#|If#j51V8fb^Td?SV9D0U zH$#>$cShU@d8B#XQ)#nTRFGW$Pd;uh8Tb5Xeq+TiZ7)4-Po0rvJMIaZcUM{d`@6Zj zoPoaQ2ZVxZp;0Gd*jR&2-s>YN`9F)(_INvkox~5WIR;VAfhXyx?Lb|7=SbJV-Cb>+ z^z+yMld(#d)fsuBasArqRnU|3kN!Kadi~nfk@HVv&tD(b(768J|8KEeH@P{oGTLpdZP(3t?7VE7Uq1^1{gJXTyL8O*z(Lyif^OvxddMy=2&U>z zef+$UHgQqGXhTi(q2Z9>O=|5XrauznO!^rL>esGoy~{;HWdE5uujhCbUk_s(fdjcT zGq#)iqr!~bO5z;Bbc)P1H>g#dKn8uF03)2aq2PD9g*Jn}mFa=NtCL6S%Pg{y$pc@r zmwlPN>4AERiZmPdan>1|@Nx&q1aEhR(8!X7elr4gP_DAN;i-EF`qx}1A57JR zn*3_POmG-Nq$0qI3DXy%R204bjHL_r(1zGAWhf>ZKE@uq=p6jhzo;dXLQ?4_doC6i z4PKyquk6pLJ0}ktzJ?m^+;y6K!08mqm(P3U*Xp`BOP5)4%*S7^!Hg?837Y~yONV|w zzM9|CU$FJ}sEdq#842OtM0v35Zqs6PqA_G>OMJkQ<$DReuXE&;?fuWlJrZoTz4_2p zkK zC*_DyIsA{@ooV-Ada^gAo@(n(aUx(QtJo734Wgf6IGhlJn(RIPi*~j-imVx8`M(5|`j-uTrCN3Ob9p6HWSBPU=7!ZogA_yGrNr{GkpT|P>M;96bfj;?x11p?VZ;mqcM3f{&j4&nUU zqhGS9HF2={^W{I@XSOz*IE!vbMn5-P7$VjdWdhF?CwXgUcObS9_)+Z1vY1N&J>i?F z#rsKD4~}ox@O}K^AMcGA_^fNL@XoX?>R@KT>j+d1@|k$6qL1lr0*CWP$#6`HSRu@|HqU~G~|K9IcIs@_4-mpIN@rMf;4S(+db=P zQ)K+Q0U#-vwVsE_ffBru$f}_)p6bl14K?1Tjx1%op_e=d?9PO>rN1?gyU3ki^C3Rs z!v`IT_XYHf=u+znbF1`FE?2Pr(2AJMI6W;a_p2?U?nSeic-Cc~2Q}JRp?Bw1VbQm# zRfTfLz9JIrW?i_p@Hw5Orn&1dv{~ICXPLcc+!X1%GT3`*z%$N(qM}>3E1awlzkooz zqU=w2d?_i_LMYnQc^;6COFRl6dVqRp*T7ef5qd)-#@|(JY#L`nA&^#6&e?UtvDFYo zp&Thf-JW0f*HjFs3%ZVs>9!o8<7*6n%pFTnhp;fl891Plk@tA#8Xhw97Qn4=UY48o zOdtn=7MI#`*Batj(JPE>8c>Mr>z~4~cYxn^&1hFkB1XEH_WBj#-Eg>r0&LozIy@F| z_hD-Qy@w`X6MK~RNEsBjkXE+iH4v3Dq+0ygwk+JJiq??c2Q(Hv-7CX6gy6!wG?HbZlLb`VRY@6L5doTa9bmY)}bT&zCx-YdGyCycAqb__?mcNe)xG_ z^=8)5f$3mt@N?4HS@cljb!uyOdx(}jf86Tp;*C-bdQi)jQ7>~l*;v&tgul#dp>cz&apySdM@h=B>ye`O2 z_jEVz7X;X7Kk4l)c_NPRY^0<*$|xlhwv5B#DIeTpADu<@LtTSS+xIC=!3LEgcwerH z39Z4`G9IKoM)046Yu*;`2jdMV@SA$$l}7=}4C*uRs-v==~FE&Ne)67ZeV4!1Cd-RM(q>y+v<+uZ3u%#W4 z192oVu*>4aC+S82-AFDxdGILnmt7uR;0-D*U9Y|R-UFg#D)M)Fw&z16BY;D;R5=+U zoYpuJli;xyS^{DC(yC)4-Fm+`N;WWeqg#cp> z69;t@7MhePDYR>Dr`i@iYRFxRP&H)%-Asrk{iFj&b`2V*bJ9saQQ&x*y}-o0Ix#$( zqHjB!?R(@8<()FkGz;57yX4LZ@ zkvgS7yN!m&S{{lFt`=}fzhF+^H2I;iYt(-)B)o1#2n#vN6B-Zep|U*&2tqrhNkvlK z|CJ-1r$BO>c2M5dHd2I=BTt*pUMep6I9eplO3Lu2B*~%Arf#=CqQzvaVs>MjyQPu&^L?o&{$Ud=$OC7!b0ue-8en*}l3& zPY_M=^s@H#U3|d`-GYbxC}X^ z3q!x9DBDL+f^(8`bHl)Ysipc9c|QF%|KwnSH+n)B1&;RL)xH#6<}gcwFU7RH`AVB% z>e@*%03W`k@9GqT)fm+c_U+~$Em|3ev4ZlOWBCuiHhcaBKnwSJ&*sD z4oK?P{zRwWHH4S|Jssvt6Q%@A<$bVB#+^oFr#+90F@Y}cBM4WCmfElp3+kx&zChP> zuJNuG*gCtRQMYP9Pq4x5ymz0An*N)mnbdW>)yRbZeY!)EM$gm-91WtzI4wE(#MP+L z$z01i2y{Fpkq8;+YB?_QNkblZb=_5C8DPfn126OfVCkIZ#K$+Nr3Zy!m-OU?LU03v z`tx}wIf%ULb!#Mdyg_qV!n!^%+nDV+&Dmju=bPE?zQhWzv?}uX@GiE`b;om;Z%E$+ z^;gruX?0VKSw95eJ&qcUSuTer?hTJm8RPs;vW4Hf?= z$}|D_Dj^WP`u0ax?QMR9cQ&P3R%fwiSqVWft8@G@I_9#dl99wZPN#@vDKLwUVxmq~1WB1b_bM z;??;fvT#wOCF(a=Zi{%NBy?AWHlWnq=OLqscueID2-sy8s(V-)s)m)ypI}h>cl8{s?M~hHSgi2P zO*x}j6L4;>-kLOmK~S(r*4ebR(QSHI)Lqm(@RI4pIETd`pmP4?ct)&X}zJSB_B!73S$$0Mu>au52zdkl{G&^@(GE^Oa=$fPx%S)8l5}< z0Z+rrr53EjyI$b&#$iKOj=AF1o53tMc}M7^bxX5JI_r_5ZY24NS9df&HU!YlV{=;Nz(XPPrrPaMCFJ7> z%`8U+CmK=JM6+_U$mc(b&Yc zLQ)12cpIJt)<&I0!x5v`6tY5UqsHvynZ$;y0RFgN1As%-tY?pM-PoOT=q1tYTP1V6 zh~F6?OL)auwa>wulcRLDr3! zF_tnDqun#0wp+6guw2Puna}Uq@S0rqxq*SLTp*v&Rvdg6_}r9;e7fPdRee_hHfmot zF_P{%4htfMw^52zo}+3#&0%im(bPYBeYNQw#NXFI`48=lMa#YP(N^%%rS0gM1WbUR`jpeUaEP9DcZos#kV04aolsY0`F6}!Wd1uWQ!LNs5X6HXz`RHjzGLi zln5f;wFUQ5O$;YsU2)`1K)a79U{vic{ExWW427_QJmVraJ}c-({5+7yiQ5TUygSFi%|FG zSVGx0;4$?qPgIT7bct`!_nwRr#3UHr5|g2sz%S1%vjyMq3G18Coq^&Pamul)bq`Zf z%2@+~{aeTBtPl8~SfG9#Zv9s>F9rkepn8{`cbkZ&dN7c5ZW+mX1qds7F8t}n*(@r~ ze=KGihn;2} z--cGtXGb7v;lJ_`)5OmXVb1nR642sq_(uD|bc)tQGvfXWigp+$%y*5k@bL z1^pEn-UtQQj6I5cu)mSh#1WU>0c79n_78e3NXv@rjw{;)|pqZ{Xen8-1Pl}nf zM>1(V2wc;~R}Xk75XZeihr|#PW9JG@@Jt?+1J0==Wrjp$`R9cSdzTj}>E}-7BJKKg&JxHY;S% zPce;ceegv90)rS2a*ene!eBS4QvxULJ)uyF0a@4MGS4#s`X0}e|Dgqy1QeC^)$&oxxhmTK z`N)vJsxv4}%5J=|H4l@bNS;IYn6xPV$7B;vLW81_5A%Rq-Nd2m;F3j^ix1ZtcEQ^M z!VSyeIjVH6jfG4oeI3QN9dCLE6l5?m2x_;nRg@Pq%!bEn;IIr zctofL3R#K?y4L>l>kVIaf<;@RPX1aa-gj9ua$vs2d7^5x>T`xBlH4ZJw&f^__8BTv zp!LkM=GP9f68G+dhlh>>!Y}D4NjM2~G!SUHaeJucYTb54m9Az;u(5j^GNdpwpLSc1 zNc0DbTcg8r_!i?xOu(}n)_R3{83`aiFftIm_A=X3GizE(w`s5BzTijoG$r04Juzx3 zPySwT{lwE)Mv4k|KHoKGt~2!vCVp`9nd)Wz+#RXjbwH_b%FQRH7%_Q4H){BlN9;En zs)cL(*o&^OQD%<*VA|5R|5roim9*A+&_(o(3M8_cI@W1Jo`hc z#y0ew4DAoMvZL0x)|%=nGh2fVC?B|#_WlKRC$W;Q34#gYVcDx{Yb49p6qPeVSGr`m zoZ$`y+O0M0RXUVM7tm{M&y~JBD~Wq8447-5G?72M>I-p$n;pGM(--tGJ(YlZd&|DF zRQDPDNphrO39Z-oBnq56Dc6(|zDN)8fbYIc4NF~6(X5lDEuE6KQ*?Tm7nMN%K|{me z)jwXT7tzRl!S=fh?wpBQjtl{uiLCHK6`da=x+)Cmr)_iO{7c&dKW>WqCE9jGY_vGE zRmQl=Ru#ob(ZtdI8N0`S=?p~>haVSl3Q+%EPm*wvd}j{{l<~ET6kn6%;}=;BB{q(XVMQ5^$bb+`CqEm;+ZT< zoV_G^$noCgGoGnft(It@4Ai5eatQPnEVT^B=$*ws=gt?S>MdoZVm5U_hsC@gtyCqt z=wx{0_rrU**@s=cTx(a>?t6(wa8v#$I~_h$D7|{0jr!13MUp!9OpC2JgdVi`#__iA zstMwfw9~%jp5dsk*Lml5 zy9eP6LIsMn2feD96ImZfdr)xQB-Ov+GWnZO1|>v++_**Sl@ch<}qOV;>Afky`0*yd7lZT+=Mt(EE0li>$i{q->;5ePyn zMR4}WgFHX^sNe$n<3CVmXN(_#u17wr9LZN~Vx?~IyG}%W1TV)zsY_V5>7NgShbU)Y zPUmRe6XBu{5SN97E;!u`dr!0s6BCl>cZI1{e>;xg7pmY{3;J>`G8oX$y2$63tM31P zdrH_cj9;JjZB@18$Q9vP^=>_OjKQQ@Sjbm?a_jSl)Mld%siBqWB2a>jKk2wsZLn*; z074MN+F#M=$cpzmLlKtFIoU|ljH|7*M?^^mRafoZ#63^k~>TwjU^no{AHNkZ9S-0z_RSvT-Y_G3T>$T+b+Qpc1>NSG$No42N4^lC@))hpG`k}GA>9T zEf1==D^B|LyA}mhQTEq%U4Jq_S{1Q(&pWa~Gm_Vx4_j=@1EE67yse9^R$}pRh`@#~1u6@_PBSqM@iU|7pc~QPnuKm7Tg# zJphf1Cnf6rBNf5x(w<(Q_jz53cvrgcR|G~F5pq$7joz;7;5^7bFj`C?!+~Q?Z4tM> zf^o+Kz^sk9T7TCJFWaub8oD1MXC*yi+$Z4oxL3_Sa^^7;qB(`8BHVEW2*9US!6Ed{$h^~q15l%}szL{8? z*Aq@3#j{qW2!R<>=ZYFdDQg$dn@1k`de3v>=sWQ3QEM(ys`PkkfmT)6+$~mR9mJXDs6W#;`aY&683(w7*ZDJb>K!i` zDe}%}!+x)FZLko~$vb+e;r-DO4m^tM2jHL-@n(gD#}*jY)dU}peN3^@h`wi$8&Ja6 zXUX0Aa8$HntzUIW47K(}R5n|ekU%N`V#g{DhS(F$;$HKp?Pi{v27afURTwHf6SRUTo$ExwxRya3q{6sSJS0c7QIrIuDHrg zU~Km9H~Nuf4VbPiYHnx%x>g)aI-U4GRA|uAX3M85E;Y@K`lBe{X`ZkN0`7_h3wa!l z9J2Pfxz`+t!M8_<-z`C!xm#-Xxo@PtD3DfZuB@3dhB4rdUQUd(ar)`@AnvN&@o#mB1x#q9@OI(Mr3Z#Ghf`RxWm&%va>=Rh0(!Is%Y--xerv~3J~ z9>Y0yD^Ec4^x;qb%O(BR0^h0_ZdS82662myNU^btkLS0GywWMmIsp^85E-_pWP^sP z@qH%)<~~I({g%hIn~xnhDBhRQKmKcmPR_59!e3K97 zfCI8JjJ;SGn*pSFdpaqeU1!2D!&S(r>bJNWVIgmMn#+hD>k1TAA z>9Sov$mb9mUAP$Vu;0*ES&4)a1;a{0Lw4k?>0Hy{jrzO$W-GNEosM9y`}6`|S1!Zp z30MQg3j@rrFo;7eMbka7CFC}}Y$mR9pIU9}^hi64tfCv)cec2O!U5c+9&DB5+&zHva2m`^HY+*ZHA+hw0&xm#XqJua}WTb#0Qf(3n4a2SzF3tw@uOS|1Qc8BlguK{C6G$EainJm14c%9FcAXCe{0x*& z+(hjJVZ&A^SF-K4XGt$<{1UiPne!{pQGtoq)^s-0Y#{WT9pd8+YO(oqJ9@{2zu1EG zcQr3X-Z4Uc0PVBr3Z@;(Igdd=|G>bb>t!~*=Qq<2UHhVR7*!Wk)N+`isLC=OLH8`> z4}?HVZfn|#>J=@ZFY>CUOrNaBPE`9S2?ZJ~LWxLADrxe$63YV@gsc=TAYw9rPsmQNkGy$onWeeZ zZW0$i5`ptym`NCY{cl%qr z3aD4aeT@LVTrm2emOWFnYn?Tb&HYKKP&9XR z-P-3t%LGxyUs#F4QNp+KhdfyYvV^+TK|#x+DWh2qu*I3PZ6Yc_mZB`!x|5z&%Qe+? z^9e;YwsjvKm0#vWT|OF^d@u}=H342}lMzNxVJ;oO>J(nk8T@e;RnNaR;Wd3VeK-_W zZGq~2mCx2{4#m9q1=zTkKZ>+7hdG`{FKucL$R_qDs=<40tkh)wra+?K0)m% zHaoZ&$2|<>@)G9tS@4c+CQhSHlI=&5!TykwjO^cdEY{#?KtbU6NYwa502Dn5dy8T2 z5-#CsJ?y->6t{Q==5h`l+b|$67jYK&Doj~{c*#J*|rw05M+M$94a$zy#}zg@DjCW3t;YOF%7P4`miUbyEezCJ)hlzpj(P}czzN+RH z@;f&f6CH+O1L+Z{YD!Dv9zNT&8v|Ykf*Vt6*45_mLAVB&n0VW{mb26f-uxzofI|85 z)=f@Ur*_9bHakq|5Yt`(|De%A9t7bv^7{McWt9=OzPeL#XxDi*cb}v2u5!-En;zZV zF*+^`JOs2b>3{E;Knem|^ACg*k_9ke8_;vkk>i1&OpD9Qg+{&Hh{U)aZs4d;A=FyN zA+GHqu%+y{F#S-2o!ZCe_jr9*VkDlK`pk4gL5v*T_qwVdCzpWV4pC|H_FMzuJP0g(xDEwsNC<8vaVwlT9c(sesNwJ zw~)oGF9Y?B{LeKlT-c$bNvSjLJ9X@cbZI59U9Vit*`lg8N{-e`dNH6yERk};LS@+q z-8s=lR6+4imw&%}lN4WxM~?V!IR1TH#V}(BR4qp7eEvPfjHe2=QpY5fxXcbPpz-PE zH}i7jW}@&6Z)?5mPq7_~_>Api?0aqohb}}7a(^ONDS`lXydo$L3mrkHcbp&6v#R@Y z(v{LE|2_U!EzoZ*p`q}+fT@^oMto*&`e!KA@*QP1cSCR1FNWzee%S7`7EPJB4`~|J z?Oc2MaGY`(=5z_&A-6DaZVd_3v8HODm@W;iS;LymsVz&k){doii1#<@Og1~L*9!F! zi*XdfhsGw~@ejshPM}YK``UCvlPGFUaGSHW%P~xxgAjGS>BqcFzi1WGh@{Ir{djS}1 z3}1UB_|YrPPIU)L2!<=P_cwnws}{jm^1F^62Vz8OjuF0T$m{D=@0E{+Sz_nW!O!*b zct`w5C_PZ3y^F2+txfzg8U!sGa`c$~5Q&c9q~N3RzGy~NA=lB`n6<#E!A=N zI?m;^j7*>Y7z_PVif=UOP5R+gP1RISDd&_`D5#9n9Iywu5r8SRb<9v*zciVIatA-R zYv;V}UP;-+NsRAR+FpGlM_E>)ZT2%+?~e;*4PcLHy;4i2f!tZ^%9A_mVVB}pZ$EIr zt{;xsS*Ltv6Vft3kGwyd-gyRQe~u8m?`-y&cN=lrH1@LBa-fv+vsFhh*#*LpYv_P^ z&AvFeMtsZ+R+;;1p5v2ekU7C=?pp-GhEqX_F6Je$y0ZvDS=h8W*B7ZBm)$g0mxZ{Ll}!W!(T=^KfU*fGyw=O5e zbiTOF0U+#^-=xQ_;;JuxV64H>k#=Ns?2!I7D%GNAH(pgpg*NyiR`g6#1=Pa_z7Zgx z+VGz;^T-ywyZ?lE8|g`xC9H^E_$gju>~I#P$rF8VPM|h6Op`*ELJzeM9gA$ny-1@x zQEJpyYn(c3)+p-yh@mSddDLcq>RrU#nXJpww7-6mWN&?I=*S4%;)6|JQ2g>G)_Mld z5}}8@M?j>T!wP{BmBOFDbE6_V-$#QYB8OfQtncq5SdfPJz!5$7(77lJ*rXXXeK=V! z_wypA>?~Lye$J}Gd_`6YT^;#LpylLZp17ktVe+XHlZPHe=kx(ah1n0 zS+@2u{FDlwqiz4vzt#YM7E&Rn>5u#1M7TcU()k=hz#*OJ#(h|;3kodUvS*l>|2m4R z^h2ivll?R4!@&UcUUvHZN5umAb|1eYoa%V+_Q}@mZ*gB5?+9n*DEwi0{_)HUM-ESW z+<@E}eE#7CI%~s6G2y_$REE zjHa;zoH_@gKE&_VAdPh@*E7n?SQ75lx`}}+2CxoIIicn@wb`L^x^cKvK++(5G@-DR zk&5R(dLP?t9M;H*GB>Rfz1(%}28vTiUkW`&&d$sM2=U8qK)^!FBAx5d=>eW3GBo7# zD@2nMbu8n@z*=tv;!P{^7p{6D)K^YuN=Eln?AXa9mk0*u2{;rEk69)$&Paf6oV$%z zR@OMs$#DJXqpt9)rNo=9$UutDR`TET5PhI$k86qY`x5N-1zp57!N!p9BKUT%!sV45a(sO-R!UGgYF;N($)pbpRFnHU#*+mY(V>(3)VpG`(>brfG~K z0g-G@O-l*uaA0G5`6r)#1)WE)X@JY$+FL1i9AH0esT4W?QslVSP^$^GcQSi+JhwEi zKo}@Z>e5Y?s7Fw;D8HYY>AabnjK1aN?lo?ie>+-G%GY_KZHS$Z}h-kSZ6uOw?raH%S zYKo195w2@2eVh-=(nuoS2?GU6^XEG+(t(cDqgcK80d-MoAGdx+mLGB|mWwLM?HY?n zvwi$lG>dWpZHJmcO0}>|Q~(pdUU^ zhT~(7J}L~D8MtlkjXVY3UgBRD(8RB+&2I)z&pQv-K2WeUS$>E>zonD~tS{3)5R`7Y zQ+K^bf8f6uX4vE{)c7aa3`U*~Qs+hR@79dch0;GMCpfL9idVt7K`EHJCDpQ6M)v#9 zv#`@osQ9f_3d=Kr_}6cN;Go%{^;xliCvMbd)?+&`oozMd3793#BbCAL=>i`l^UoX3U-&i(s7W(Gr97b+C0k@kvj7u{&g*Y%Q#30 z`;wfJgxewfQ)ex}_#(^lwcdPy1OgdY>%qoMc@l;%i-PcxQH7bvHBv5R3#w?ALwg$=vZ z{Z=pie)}V9)~c-TpP4^_Mhxt5=Gd(%o=6{)q1n526R+`BIxiuKIZ4W&mMlsSx9-Ud z0{OcIupJ2lroDcBt?l2ss&ri_~v-)hS)s&c8D z-_n3QO;FCHH0;?2%Z*(hdN`62LhmtSIv72tDSG|!86h!gk+zxE+4ADo7t}$S`eLg< zBrA~rZsl)!?&l(}CvP2O)|-p8-HqIBhQ^$YV%aIPkDmAxeL>M<)<}rg`l}3>$hBXL zLRTItuhE+f%-BqkD9WOC!LZRA)Wggxp3()aiC(6L6?Y$8pE@1Se4r>TTxUk!iNiev zY&1_s@5YvJ^`W@e0$tlJ;#XB4*d)2pNU9xVw^H9~3T#l_`XRZYs6C%Dy`dcXekLQn zrx|E>QQc8j&n!W_e@YqM*dowR{PAgT+z zgJKMpK1Eo-zME0I)Lrx6{;DYoNO*Vf%`opA=xpSkLcxc)-`%mw|9DM7H!9mJefcAI zmj5{dh0^HY6fBmd%zs2#82c!j3MpEurf3`LZC{D1FmiXkRiI2kq~|cwN^zk+5h?N0 zL^W9TZPcD&*jQyUHt+TMtJ+E0!LokJ!4k0AB@ZSkG9q> zu+9{fa#2w=WlB(!-jse#rVb_3U55qX7yo|5wdDiiW+xtJWNvam7n3XhP~QgA5x6YF zxQ~v&k6ElJZTgx1FXz;~e`Ea1}vmYYp8Kt=( zRJ~t#5~Hkc zwg|9QXlMV9|Fv^EL}}~$=T#?y_vdmA3`((W3Wo_vD(iljNj}MXVqwJk@g#qoC~r*Q zu=L{-59^<|{<{pNMdG@c-Ive*8HK)tC+jLsAWx>5EJJwLV!pQYb?QMZGjS5GsPb3Y zXuY8+z1n+Y$JlfR!siDTfbv_KP4Uk+N z3ARM4r>G`}#v*YGQH(~Nrp(77&9=g?Awnw`b$5{Y!52#;g(NT1$dV^PshplfR4QfO z{pf+#q9i-58<)}gczXZhkO*}liR^aS_njykJH$k$Kg>Sv^$$-uCDLP2(8MHt1!~Nzg`+{p`|`4I6r`icDlSuU3`DNm9~CgV4aewDstEh`t zpX#(HTdSXEEIf?4otiaqTC;?_-c{nDNj(3g@u#!F-$d{4(lVX-xwkIq&O-G$%1BHT z-FXFXR1)|#xO-ZP1FLrEwKIYOM}xM|YQDT3Nw`{LQ8G6aTM*G+Iq_-x=C6N^%-oo_5gB$4 ziTS#=Ju^Z5_&2s}IQq4D-mGzm3{Rpa?tsMOKQymzzjkdqjsD(HA>r|SCFb3cfcEnd z!<>Jp8dq*aUCiQJDBlQg(^7iCh|4{i6{(aGWO!p(#C1)(f_eW=x$`t3_N6Rs?s#C# z*5hL0jGIvvLu$hh2QPS#Pfq{lIPP+v57@h2q2lQe)9n6^BWQea zRqUp|ExSadQ*ZJw%>}*O;b_y=b#UkJrhQeryP~$a>CO*nMVqO)IL6%E+Rog)kk0&Q z_O27(ZWNShn23Hvd>WNCQQ{cdY>i>fCCBjZW|3A65R4r+zx#K8`)7$LJXgG8Z12Pt8;sAp%c_3| zox5N&9T%6E!eYHt>qftwKl8F0@2=LChW^o}Ta&?R%8ujrW+^U28HJ~g$ZVOvIrKsQ zlZeZcl95k&YNqv6-Mz#T-@!2(LT|v9xXPdCFM{Su?F$I{Bj~Au(|fzc;iZ3D)!yPl zyp4Ou^pA-g5fxaQKo%)K7>sFbZ^RNH1IM{_OKlUcAyd(j%#CUXR>?WHNJ1 zFGVqi%gu^mC-(;(a}n7>j2HJ_^!MAz5$Radu5M&Cb@Xd<^+nzG0m~*+<$WjPVq~&9 z1}a-$FYS?(KB~a!Y3>z{SKmNLD7&`a@0|LK7+d6k?QBKc`P(G%5q*X9;YS{JA8GQ_ zr{ayPmMHX3+`72xLs^OZq=)3s9GTb~8D=Gyly}r9J_#uCZJM9LWL*=Fefn1xFO)QR zqUh?Z$MfUG_y&jJZx3&8IEemTc)jtBL1)qM)Odb7SNQvKhQgEKd%o4P4_ac6iAujX zz@`-kTWA*Vh{w#6YNPeC4S^lv?$y%N$V@fVe}^7w}1EJ)*V(*Mi6aRrbLml zO>R4`UuK@<6La4__)*%dr9_VmDXp*LtD&Xd*n~(<=4dHR=!kL3>EwXcLULWeyT$oC zQe4}a@A{b{LsK^R<9VY;y-q%L>{YLeQ^$8DXX%pfGZL3a#^`Jvkq)`gZ#L`;T| z;{3HsIG9`MU-GwZ3ur59iNb!$zv8i=LNRSyn)*B%g0EcQSk1EWj((UM@8h3R8sAO+ zU-raXWDQBo{dzk1+M(Ldu$|$41|#HnWDQPIVoiCIyjfaVD{PlT2=&GpAzde$gohvlZSZ{g}IK)%x+{S1Oc>KI@O| zEvfnFAn(Vs=j^xl6ZE5-I=|NuDwS>lgSvRNpcov7Q%x7~IS=;@lYVJ?b zrmnjjB8w%In(zh|R+>5Cg-bIrPV8!o>Mu^3-q-G#h+jz}dfv6+dpWLIDLALV%M0JM zdCP688tacm&TQeeQti80M(zo`Vy|sxmY!nusm)#zRM+p1)#ghtIr9EIco4ME>(Cik zJ5K$0ru#8jx$bDVcW0H}fo`{EC(Pp)&DhzuGhdxI+%qCFUms%`7QUCT(V{9l5YWf7 zU1}b2q-?BFdQ86F>gLuf{=`eKY>MCnrKzmMjQ!saRv|`z0 zOK(iZkdN;t+sZPAQvSGj;><6ni?`JIG_!bpr=5iE)@7YYapJt%a`|!VJ3@kkipAqD%4`)(L#x1Xp|27%RE;;dXsJpjn{C5YYtofTa8TX0X&s=qr zVq8YSxn6g^9B?DqVQb!#dyE zzqy-7O$bcY^BDAH6yQm+)Zw!xYdMVn7VyIoNg+NH%w^et@5R`Y)RHf=5&y1T1M02xWeL*@W>JDT`Dz_)ZlaTy;1~j>5Rio z55woww}HeDl!`7lYN_IIF7_;~fMV6Lg!@tuP21=caH05%jFd?6R0;P}*W3<5{DjTNrU3xIXGwfbz7S>wv=^ zy}d225px(b?Cuf{iIwzF>@Bl8q-j-AO%d0ire&_cl^ANrxqcV(t0Sw2eN`fI62oIR z@#jP7-FiWo$Z=<^3hlo`bpLZ#y%`4I@oK;7&NVp}+J9B@@GyEnU^i0Nv(?NJRs5ua z34Sa#mDQinXFJ>%@qm4Rke0{oW-}k_$<`LW?;9=Bw`9A_lXo=)i}VG3)08R1W)Un} z`nR~)UwOgmVebw2T`;{rXB5Z3WH3H=xHorm4@=7CM&Tn(YRrUN>DRXJl+31ip}sH6 zrDI0KA)EcVMY1lsOq!ECv5{8A!;I+>d@83}6RG4gCQ&%uzODbd5~l9-;%U6R%BI+- zl$*?0(Y{UYC1e+@J&}`=pCKV7 zN)yrK+KySc^exsdlN6JK%@X?t@g`Lrm)|V3reCL&ObO}Fe`Qps76o!5mS=RA__q=l zdaoMOhTi7w@xg5)lnrFnafIJo{oBa0xGp5E&GElCv#B>e!z`?`=NU!2 zcWIDK?VucEmF*6cV#Su=#CTPecKMor3?kCQy4fSjbv*j$S7cQ-t zbggvb&e^k^=_e@_%u8|s14I!Au6f6|QoXl5Z(frhFv3<&n10IL!EAXsDCHs-&q;H( zyi#lCG8@ApIt+y9h1A3w_QfUemm4muM1 zzeE{-{T%d`yBz-WOm|2F*St?5-Q-Bg-H%DUq@_og5oS2uRlY>;XwIMFJ(4+6)afns zEdfMb@~4WqjsCw2&#=j9b=t?+l;%VriT-YCRYA4Lz^vAg?y*_)My$1HvlJ|AP;^R|{fomZ7^*3PYR zR=-D4=}FfmGH-Wlob_p;I5jyGP@)@_#>=zaf^A>clrfiHZZR>dJlKKXec^`MG%Kz@ zBzcKDsY?-iufE-H)|%1uom)2AZfs*3oVgU$NaKv(N%=^(whS+NR#J}ratCYbnP!4>V@`f~8G%Vmm{MQ?f#c7VDqvZ=jztcrRo z;KfVsOwd5lNBoJ>>(R`?jW!3v6lSBA6$5gJV-t^d{F!=k`j6kw3BvyOcRc&BMTI2G zWqAT`3IDct(staf>V-ksO-J%5-o&XdC#W^L!6iD7)n#g|r`2jcFmuP8)1R&W=z?wN zp6}#u1z@rq)V0%iKK`5Y2y0 z|L)_~%2$XEl9(A^ykR!iE0VunJ5SQxoeX(z@nq69=d}m7y zFsJrlP5s?!=GdRsIXb_+#x4tW?LJLyNm47DO9(pMQ=Szz71k|RtRJ0Ie4eKn)|rU^ z*v&s*j{Q9}i5h4@UZBse*eT*f-QKv8LJ zj`mpItF+sj<3`;Sh<=+!d+b<3>pVqkOEEp-ZH+ytn%8?bpnopU>DV0q4o>Nq@0=R7 z%l~?7;P0N#UV38fqi0V~*!I&u%#Q2bZc#lS*!+g=6FjHwTIES&l7D@QjA&$&de^A( z-Wd&O`qej$qi-V*JL?2bWsYtks0EovF+`b*_2km1aSlOkV1BjUl{WUTcP&Ni0L%2r zNYVG$FFBDHF zFnWDnVUt}1Ky(QnA4tZxaU^M40$#vg@^_eP#;5~f{T+0A*O!7EF0 zYzb+0Cr}Hah)8n}2WbMx8k(s;-$^q7FE_-02ENfj^pqSP&N8)F6 zlGE>U*5EsOcd2eSeyLzEr_}j+gz-Ds0p|^|DHT1do^lMLGRAYRS<1VtdV4CwrSz8K zeb!5w;X zOV00n%C%mMtO>tSyS1p9M2wzqYf;*cr+s;tc85dx^PjbNRA7K5K65~wkf2t1VNfh} z*~{(}H`$=7ynlNXJF~y)Niu6f`8s>FvfxZ!i-WiWP$v~MLBJuCc}K*MIzBG$|u6xdOGky^|wH_&;BUm=W{+CTQZ}%P1p>Q#z+@ek~w(0>0poWTd#?-T7Has@tbh0AHjXs+)Wn83-9!2HRrJ}g=!;p@&+=E7$E04_X;S*n*{}#E^^AtT zRP?8G!E%}5;qJ;6i(DDJ^Ne+U>6k${`Aj{nZ+~9-ZzEQF{`?8-apD+yCs&WhilY

!qn)S;j9MvtCbHDt~S&q0ud0IkB=<%G+Tkhd{ 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∾QX6i}No9XH%%25G~i)92lW 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%0A{TI8F%8SfEA-LBMJ|()-by`_MHm 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%0DT~Z-mhmT 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 z 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

?JM_x-RNcLUq|a5?A|abT_-O%f_GBr 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

f-epNwaI4xQ4l{y6I`LjVEGN zLwmZt_)p7=A;SSDtc=kPDO$4^@h45i`0$%+9Y*d~Jo?h4wDuqcL;Um=o;IK=gLVYo z`^eHYDXw*JD125@7&hYp+WfT}54K9e*r7gXOt}WJ*1}tJP@R$jh=9apczz)8YRf4& z^(nh^)hjUq+lyq)1>2?GN6OW44?eAYil<}KQuEsMPJ%%HuBtjlh zn@x+Qim9k1?9?yUf}IzO)CW(YUzrE-CB1$~jpGs9M|Y`_20D0n|6#p|guaBF zSn|rlDA^GXwjeGFZQr5<9zpkDe&sm!PrL*T=``JwzfleV8bB0I`>e%G1Zq z;h_#NeBn1|mPBt>Jr90{y3+H|U5uC<>vowS$z^D#deqIIVw-YB{3?*wxSo72vY8WF zk@{=jhyAA|$e5-C*7|O@KH1}D3~&L4r?x7RB!d-*4f^zFwN-x;FP*_@uaH=&rrO|< zF1_)bt1}OAL{97BpB?Kym;cM@{0M9nsMw6CwOjx$)-+&POxn{ZgC3@v5+fvIqV zk=jCY3^1kJss{@L!=>q>$lQy&t=by)kqJDek5M*+a7q>X(5-$ZU{C7GoTE_yswBaZ) z2~ceI?;CJiLL!dikEWUR9+P%ajE^sPzkCv#@23HuEJd}?nQQ!8_T~t&k5jMlR=)aD zo4YIL{Fu)EPMr5-Fz6YB*^`V9y7bJMpTguC}LJ2>d4l9=|jYf{xGUPrQe{rqEKHN z0eSfQsvyQBivz~YI3t$LNev0iv-*ABFZwkY=}9#SRixhcnaw)T$^(IXgJut-qwqc_ z_gZgG7OgnZ#q5=mmCG#k=CR3+1Tz(vNC#7XThbO>CmQ9X7eG_$?Ajsurb0Deg%c;Y zk7K_;p!RSYo7OfeOkKJG@_1`cd8VpxnR4<`d`2nl5d|TtIlPq3qIUZ_{Suan9+pNy zoHvid(X0*sMh68`X-)2yFc&Xuk6efYrn?&h3%{D_A%jWS1s&s3tvMffKybp;w(IvN zRb+m;7w^HagfAPp=T+2UE>IhM*(+5j%U#)qeo);^Q?qy#VXK2$ISlEu@;PStODYa; zM=NwLJ#tS_19!Ki?#}vqS2o2@sXqqROPu@q&B)8x`~|^YhQ5Gn^yJ{CPo1KZ{m<=j zOQGf3{cS0cEj&PG_WWHYGGW5&I1OwLJtugZeP0>O) zHNKZ+W*wy%O5dRkRditdvmlyOL)O+LtHIW>G@|I5=WDG4+r2Ze^$#IIn!4I&f5Gli z!HDhqV|lkBZiv%c^&z)R{Uh7z)Bp6q6NNBxv8RBXkcnd+*NJi0CN_MV zO0@G-U^+-Zx8n}SLSEq+yL9D4TK~D5iF`dCvR4C6x!+Zqp2EaA;ts*N&x#YI21cB( zvUV3UzO5eyYoXo*H`cad8jzxCi4W*3DtPpsal>7YuYO$Y!0@ET%$d%u9Pv$|H|5L} zI9eyAR!MhGkR`+@K3o@YpQzV7OHVoDFnNE9gXF=Tl^$DG9Oe@{=_e9<8q_T9tx>|R zMl3vsvVJ@2n*UO|rQi|9^Kghcjc;*b{rt;`$edCi<_ zXc^H37j+jg3_r&>>;IzMEy706C{{6dc?rTfXsntn{myP*vHB zhXPsZq@o{xJ!QF=0Y&^e#WnCza>XT3ev9J8#jnX=WS$TbAV$8Ks2Y)~0`L$fjy2zY z*2GtF$5?U%{_>sE1tIYq-WvEb&kb0?L5(TTf76D2i`;nf*4JM9LXN072|z_*dNm90 z%Ss$m*aczLN0raWj1tU%z0}a?NkA7V#7)!tb5n>dVj9*oO zb*!F2HeSW;cJ@+yYm4C$Qs}}k^5_@K^!EQMA!5fyk5<$j%#a;;eTRVe;gn&_7eV_9 z&6$EJIZQ6@Xfe^fhAIs&***vH`uc#}Q|i8+edE?-^6qKbr)yzI$4k5TuQ~kSVJeAG z0@d$1=Kf)feueoswpxQ=3*^bU7m}Cn-w-wW4oKMgBA*^`%Iyje>Koq0)Lf_rdB~3a zqxdGtg&IZWRsc?Q%|YQKe^0n+C4@U<%r~|OF}kq}&<;CkJqai?=vEn`G6J2M{#w^cVNfDwC>&2xHa&nE)qMTF(Agq zeuk#^=zUyuEN3DL1&2G48FNP1Bx~YCx90$wW1kuqPE&DhIP&oHUVYU43~>JxX6S*$ zN?Oa32XOI&xzM%kWAsKDoVCyi`!3_lK`B z@mn&?d7Z%*FE>rGz(VzxG#X1`lxOHX#KiCL^|4Dx&)3*!&sihGEKLly36jvxQ)S?F zWR1~?Uk=6lC*Jk$oBUCKq^B#*QHbW&oT8iof?&nh&HXI^i? zyn)_y{3EMINgu#G%@}w?=YVVp#V) zq#beoxU%c}_umfd@Zoy798~=V?^g|lAu*qIG(}C#&xq{wdASVy;=yc_Az{{OW%;bY zvt19lx*M8%im_xW)Bp`Cigs4?U1B-5e-yj-$43`Fuo7js4~DciPA5zal00)Da-L0- zPgb!aB#L>j%u4U&n_L^*z9#@JGVgwuGkSq}Ux&5)syTygslYsnf9@T8*|{Sw>aMWw5s0v(3g!~ zfbN}MD48{_VN7EN^d_1r6W};$NWEk|i3KkyG~?N_ZFKBHQpq0gKa2K^s`s~i@dEz< zYm_UPW9-F{K>|;`rpI8lf%*G3wKWdo7LQHnY~(!Q@h8qv`%_xn8!9s49yojyUb~*| zFNDaw7}62^N_MGSn5zE{((o<|HlNBo8B-YRc>C^?d|T`}#67tz!rGG_-TRc5M^0dT z+=}3POE{jFiW<_xrurB zD1RP=;Wqeb{{qug)ZD}%U2(0Z4Kh!9txF4?34E0yA6($|Ro)U6EcV`SH-MLII3dVW zudPXV#p9|eePJu_W2$6*RNO5v!Mr!{c)1TQ^odKt$EqUnX2oMDSs z`yEvW$C}e4pmTQgKkNnSt@+f|%ixog$@Nm^8+?dTZptb%AGu;ePv;)={x8Xyzp??-^d`tgq~F$Iy3^QkUl)cHm*geY*n1-dMKs!w^deN zGvB)f8wx5om8vDba$tA!<)-XzC8aG{=NlvnOwJ0@yw2+i;1szW_HO1>U3U)1cK{S| zyO_wyVz$Ihft<^;Jgf&Jw~#eA=uthKy&9eJOfw1S1cA#|>_5C)aG{`H`8a=*)rei%`?5t+UyG)5wZfXMf*<$sc4{cYF8csOJ zl;^2)rYF0;pai;gP6|M45xLJ@o1+*qpvmE~>RW%LnRRr~d8L$rKPd8)vIBiV%nq^t zg%KksGZs2xKjZyJ0V%>oSv`6!qVgk$iW30SSr6Sybb{rG+6Fy$4zT2Ydss-Vosh4( z+d0fC7vjo8eU)Q~n>VL+TiRX8gcbS2j{NsnUrzY#LOS{nq9uW6Sc!)i|L54>l>@W- zX9Yo*!MqWteUH98DXpqOZusl1h+hXlryFf-PPsF}wzTAIAam6XwC|CB>)E}ne2g_E z!cqXi9&7$kr{y6YCyj8pC`8=&P{rfMi5#-WJu262E!AX7WEaqBE;_nF^3ck1OAxgk z?Tq($tbq6*j?Vj$&F^pHW~f;^wxU$6sJ$tnYV}h^?WoosMNoUB_9|LpYZO(h_6$<1 ztr^swRm5JgLU{6h{(}3LbME_`_kF!ymn~mTOWV-TH?KJ1Ga{Drhmqx#qqQ~t9 zhg`C0;4b&(_Kp)`t6F;n^PC%YUcLO{N4D07*b()VcgesI2`MFxv~RH`j(6LG-Wm|j z)5+nlB?0wW@deJGHm}FuawN$nc(!1SoqC3&`B<%Fs>p3P!K1l>^=Z4^N}y-=+BQs5 zrDcVQaK#8Fb@2c?1e~(JlpyD*S=Z8_$FeT&<@=UV8_GIA@!R;46*fKR+`R+1 zic@=6I(Ap8jy%bo%|q_Wbxp%!9#y%B@LD|V`kw4rt`~|H&T27q7Z&(m_#PBK(}Y0o{^ z#HOF$+7Nb|j*O{A^9do1-g&(4&s258L2w2@jZQcXQ~NexwP3_(;V=I)**TKZ=EUk7 zvF(=1{CIjaqOTC?!7j~=O}d+3cWou`5v2!t`^~v<)|gf%FZ+LKtf_{ia0p2VHD=M% z;yVj=?=Iec{gIR9Dc6{+9rm|GFsTHn9XPb__~j>Q|6>G`Zb=!i$$&)46kBr5$tzP> zO;S4*VoBQ8l;CeOty9(1K<+!oxp;7A1IvIv-AM8c%82sL!ld={n?O+j&^sM0u1-F` zaX1=cmgaoezFE^?4W!knTF}|)#@M6P5u(XXu@evlW9)kG8+9v#Y7&CpVmgVCS@{tx z3%3*wx>+qU#bKXRJVhMrUu`GJ3P)`HLaY-)f>{0Os4W;GO=MJ=s$Lz#BiMFuC7tO| z;9pb*!))niaiUMJG{Uv9WouU3m9wS(k@wS zBq|LdYT4kuk<>_9uN-%F18L2E)I>#q;qk}kOA}&ugd$oqz?-&mL%Rj+M2<0WopNGt zKL^Ut(`+WHO>f&fF`;K6Q!40$>?^xwwXDd<8$_3czD87G+$>F}pw=gqT>=yjCi3xYR8sej}hK}H18>N`q;!>25K1XJE( zN$nZ7 zJ%-xpEX0wXgLor}mHWoE-ZAEQ|1Ip-@6*;{09Jxdn(dHg$Vrak!5bvd@k-mREcArYD#c`y6U<&L%#IXVSc7M%SJ< z%C4`9P^jvIspmF0?i_JZsL~>0DU2S2pg?~&^Kz9)Q)lq zQ>t=~HJYf`1YsPsCzyPho%u!7zAO6AaGBzj)eyM}wKnhM4ZvfGSm-%;)9l8jaklFY z){8ataR2(AFLf(L)a{rH$3O|qVHFZAIX0Us}C4t2Yp6JGP{Q? z3@vdOG_eeG1<23(>cYy85cCIjPNX!GQIRl&i4R*!V=phDUngNdv}7FW)20abyx+Em zv@wcbq!l`k&#daIOQikI1I=d+l{Yo_s7<5LIR>BOJ0|#KGl?C4de*qR{LJ z^Yfb@3?T#EyG)2GNh8H8hb9s{Ibyl16=$6Iiwe}C2c5j{k*AtB6=}hO2uOPOlStS} z9d06w>xWnH_(dLZLqbxwTK_KocJV{5{GI@pEJC0*GE zh$4BBpzHI#ib}n)O$NWghfbk{LsdD0p*vF5-ob^~v5^&7Z{kP;VsrpA;m?7pzWD%~ zWCZWRYMx?xFyoM_Xxnhtt{v;XTX!XG42dknvBJ3``gZf*0tlq`TxO@MEKhzYnBaKA zY^Bgn&rP_ZDq)CN9iH3%I8Aj>a8bnUDdbEKT^M{?(TpwxOe7=d4Rdy{U-)y;$izuV z{JKH!w-P74NVxHLiUdctlVbM8a8=i@Yqx0OG$*w!(NwWy*I)54QH zIMLimMjZt>=)dBLiU~M=9#*SZUwcvnD_mo874WbKISoc2F zfLuUYd#Ami?(L8zrK312{`E#IGTGn_&GGQ%DP-ddA^K-7@#rd<$e|c48dWlE?K}r7 z#XEL+^0g&a(~HZ{MFHa)FvjXPGD9n$i3}Q;76Wb-scp|K#thLpq6_`%s=k_|BZ`YRUGIknbW+j=2Y99!_4G?_e&8Off5<$CiAPVR|+cA zw57xwDYbOZ=J^oB5yH5CWK>8q-j_z2d5RZpd9~HT!M=XUEO*4+k6Jcqk zh*e3kqYR)iZW`<()pl|k_Fwe*L|SF!Ni-A!#k&ZuB(cUsOOVgA?ssHK1ADIofxK>P zWvG^VYe_nxBzZxh$oO)S(wL}n2LD=KgFo!An@A?e5myB+G6+t0GA;hLTl`>{Jl9VO z(r85ct!yV+Y=zX9AJG?XAX4vW2XczQ+^?yYQew5E=oi(d2yzNi*V<-kU!6@>sH zTd3ryub*KS$pl$xaJDJy(@}U#P>b=P*P>PJ8e+xBdJ@60;qT5gcMk4>HorP|6-X{% zNSGQSKMdepv_b=kDRYx3D&M~#xPp+9-?X`KI%+>Gwt^9hMbD!F#$gA#i+MDoj5FwNGB9XcP3X42~bR!;q=cL({wm>r^ z5uCwz-xS|x1d86r4BNSrnLTOXMKtjVk=48GLmc>%P4&;`s`|3mbRpRHB^qCoeU5YZyS89=XB5PKHOA%?xZP+A$y?_vaf?iFWYSG zrY};qfuY#Y0KC1UGdaO(cpmco`r~vj4G{E`K988x^&j#s4eM;59H?cYu1FZTEr7eC zuF9KOa_v4eLrXgG`cGnCgQN|ycII?yl+4AsUk`q`B}6{QGOONfu>s^G{UJp#%&tu& z=a85O>Pawq{;4HUH&`dBqApv(e}-2!8s5y^7t{fIezWLT_sjy`akrt zXW`~y&qz^O1*Va6{ez#U7_fk^tzJLdcasCuW{TamA>Mqq#0@#)L1!7Ocr89jB4uI% z%M%W5_WAC_0L#SAg@9WoPmnffmjUF)50~w8J!1w1w>hug%ECW0twUawV|=|9y?dj+ z&|pvHO>|Hi&t-hteOq*~-y<_Hb@9uVS%3c@xL;Z}=QKCKb-&jB=oGN%axb5vln}ym zu1Blw@&MX&6ZShc6SrbIz(qC1n4$mVmD(4$N$OJ*9K$cYd_6jNk_wr{m99F=(f_nT zieTtadn`}wMhK|&NyFZ*Nrw2!-y(T|Al+eye!=>L?#dJdy|{s*7wc0L#26hLo?>cQ zuMXrAZ0q!;PVCx6Lj0>R16k@GC6An>;PnRBcsqZEU{8PYwGsp&2<2~Ny6BazgDuVu z0194EYlJJKcle_-(Km}l;f2mh7e%Lvp^OHFEPFTGshN_k3?O-lxu8R}O`?IoqVUfc zrtp&d;}2vG!{G4i<2aVX1w6zvA9LQ7QfJlL7FGQmUH4hKrhelEAx4S4D)umdy&s1> zFvga{)yQhO%ilhM2@(s`WgYsAtEzGvW8% zj+%>GvPO2^M;LpC)r`=_P@}%z_cv38uU{wB(GKl0hl|4k5TDi&#E`JNb#a ziNhQJ!#250=NRZacE6@ZpWAUYU!doqED3`D#?tWGkN_*JeUQJ zG?O`bnja4xeK97jg&nByk~72D&<0J%7hG+tI_>=w1c6MUSHhCHcL#ry)*pk7Z{K#6 zc$`szf|H$-g$gNhHV`h67!*6Va+_v=@z|oS#`(K3k5BzJq;1U!VQK5!&MQ8Eq;DAU zQSEdI(54{VcS_+2JMciVE#yUGv7Z*?q0pSyrGK$awiaaLE5=E_)Ajz3LV6rExLK`j zA#m;cT^cb~r2EPJk&rwK$eA_zYQ(glVZ$5Lu8D2(QoG6pANqr8ts1PRHDkV)_dvGc znD7EI>8I)KbVF5lk?sM=JC6_4#)37VUjB=ozDv%aN4D6W#XVXK@=DVyAG2N&?whH~ z9MS2INL350@MRq_1G&Xw;MuT!`LS7|jTZ^5o$Zr~@=zXl`!ncH1d}?kVLQ6P7~7~B zUJ1xCDlY#I+b=HR6tmzWV1Y=LP=)ZzJg3!ZiVK@tXWe8gCx@7|VwMwm9=$!QCPKPt zx5pmGws&`PfFM+$HtwW-R@JU*?<)36aiFv0oYZ(f^Ro~&pq=2{&5nfEoFOx|Rhz!q zN zZANTeBIfNvC+ZaDDI9gR1(pWb-f7&vaA-OE=-K~P+-52&E!n@8G-;Os|1UMGSOR2C z9h020o?k3y}fxNaA{;(wcMOywr|{pq*jB#EJH8mTY_BsV^~5oK4)I%eC!+8fnrf!{W2k znp+Y>>aiCRL8zJ2(l5whvDe_#MP%>0cws=$IO4q8qO%$;UWi>5)- z%@o)@HvF5b48Y5qwW2P~k2{0;+~vlw8yz%DmY3`Y+5%$O!?u!)K}Ab{lDJ=p68Wy$ zA$kM@G(Mu5f9wys`yMZpZ4NAsk+Tc3E*iLAY7;0iarUecyM=7{KhjokcAC*3^pcYOE1y{s@Ocdn|j)MY-Gm4YixRY$U%>j5xH6$IO6SZlbjqMW_PkH&*_(M9&xxIGXf)=xUAvOm0;dKPGrHV0+5AtlECWBln0KM++DSJlrIcx~R6-C<) zHePLh&FL!&&UWrepOIyCRu-EP8w;}f9&dZTJ;gG0X$0|SL#Ges8A{gcM$I~a)w_bT zV2l~QJg!x@nKsr1WI+BvU>o?}aX+px_r4Mgt8BPv|abKI$uOs1h8-CEs4Z_(kwQ8C_VFa#QpSyoBq3=&+^Y1Ed-sx~in6Ff)rx(%~1 z@T}bTo}vS|bJ)MX2Sdfes1*yeXV!wl!h=q>r0ImETa8#dvEs8x1wPyy^^Q4bj%nl) zht?;U%x2uw0J8sUomqs1n|5;-uhd-DBGb^mK#+OXo6M3UOHkOe zh7HA>e@1~xrEy`cyWFH13x<$=M>JbZn2Vz247#9>P{be=n&3W)|Ph zh)wX?!ux^C7W~8+; z;Ga?^AAgwQm3Dh)*zX2AS5EkxNRT~S)~wtjk3j$Nt~jneZ-O=7-=&_Ms&q92y}hF61zQnsG;0gkSD|y$qnkgxpT3+0?wBExY`@ty#jB{5fF9$i8G)@|Ei@9i* zwyGcpdWz3&CNVEJJV~Tkmjk}1X5l%2vAcBoG zI6u1$kHYZ%hiu1!<>AtODi0w(tnfTHdpQz=WL6uWZ!=$0gDEjQ5=!CY>BIibw2<%5 z8`e3ya$5{bP%jEF^V8w>Q4dw`7#OpJVxEXPWnhxlQ!zb~4bnN>3zUEgZMW3S+^n7n z)S)~2JUk5F!d}mll+V1kr0)K2d5u(+0p090ncGCqMMA0z*6jjqv$%ajYoV{G3#Ias zdr?nxp!l9Y6T_4DAx89QBN!*QMJ=U)7z19SNzt<@pY&BHMlxgdfzBFQ>OZy9E?c?x zjG#rWhTRl8&8r>InEdmG(){`;H5qamLpJgJ%Og2yBdf7R+Xkz8F5lW_PRyPJ?&oi| zVTM(EGhr~Vdf+6HTst{@(Gt6A(roLA+$I&m#V^^aoZ6GlJb)V{*e>rEjMOu&EVtQijjq%{ z=#oYNg%(miMNSFXC_M)8ZEsuip1AVyQ1u7lZSJS%mzYT5Jo9(nAZ3B=WE=E+QX#z%HTZVnyQ}b6 z1C^m0Sn_({Mk|x_4IeJicGGLeowttG;D8xF`X!GE@L;FgAR6=2pBR=)gL33S(|2{u z{>%@S!xRWBUAF$pv^nwtb-utRG6WnnzRxDZl_vOK9FH?n4{t$UH#twe->G!hHG-w- zpeJ|U)XWAb!w6a%bICb-F5y261}uq27v5Fz2&f2rUj*ltvg}Nz69p8VJ2apdeYYft z@sQw9Y({01G%#TgSoY5S3B|IgyTGJ)x*;4&(F;jNbAC7UYM_K6>lXy1)F*Qk;-G|x z&5}5_7;7f=&8w;mj#PYu;H+uw8xyu6DxHtdqzSH^i!P;-@nF1LrP;u;XZ_5Laa@)S zN*IT%&m|@e;HZZW5XNkw(o;F6MO`}3qT7Ej+&szg%GzwFbI)H7YZcvob?#|$)|ED4 z2X0AP3fgYIju(ea`J?kHo^Fs5YlC$OVC;M)k^yTP-q-r{@Jin?pw?%fg6qQV`{~P?K50v;WTWZDhaSz7vFt>r@U#pKc^ZLf(GGWR|eW z)o(lmwU|v>-8PthJR}3DSWM=hCrB4pG9<-eau^Dg%AEhd)yqG$Wc`^woCZGs80wOn zB(2#q>jws?C!AJ zi3VrS2O?l{LfCRmS#@AF8iI}B}1aK zhDi16n80!FQ1&N)6Jz}vpMHb`gdF3AY!w})R@KPbQWFY(Z>RB9 zUTFs8&yL=l#E;lvqw1xMRDo(DZEh#k$^N7M5CjZUMgKxsSUjKD=3(2>Y_n~%E|g*s zsom9nZS0hKBf)x&$TFpEc#%>??7*pgo8vc`#H~)%zXNvS4gAyctd7T^fLT0fH&Bb5 zi~K*yw$5oHYMR^UV_i=h&SUMJ+7TpU4h`E`to}}3x?@&iIVV@_!pi@vz(NSfrO1)2 zaeDpan>$a_-|M;ADC+w3HF#?LzT-F(WXc8onYUnmMROuVMx1fXiY_RNW3Qr#-;umB zmqiM_XOYT@d@D{uDAZ8jyUMP;e&GiCV$KRE|$PACPiIXT0h4;)%kr~d zSovxEtIJPj)W}4Q14cA8=MB)SH&g)#d^0OgGsGd%b`i}za@Ic4JtKy0qPX=56VIdQ zC)_J`aJ`Bm%_Ih1j8z?ubI*&omEwY?Wo+%`R7h|siD5V>Z$&}DMp4-(XH1u|LSlz? zkvbo4x4lcOZYDJ<`-}7R2~TJ*trVraT}n~~j2L&%gP`m`%W#!8ny0 z5$#k+ohwwgD`_7LauK&`FAr*OM=vKs>V9Lq*x}ofLC6rV<9>A>tT7|r$9GvOlW|^i zmo>i*vu7txas{i_MEFYVugTBs)9T<7=(=uO$c>Fu;l9tYIhyM5ca;7VFr>C9Z{Fd1 zbX^~0?1kZ1MWE|_j{Lwe^am@{E;k}yn0wBCR*l|3j5(CAY2b;k&qH|Ly_}X#Ox)t@ zJ3woD`Cp`3mk9Z=b9gJi5_ysl+v)3SgZAzVIZ)?BR5&@@R$$xfYWUKzi4oKsi>abD z$^T^HC$tWh%s;#0Xb)5bHjvT0>3&9yM)0~->7y7yGg{c?;W=B6Jc6uxP7-&y)03x~ zE+4r%jBrx2vvn(xBh((}N~S~-+z|D4DiD=N!bBU|V788s0$-gs(Z8VR)M~Hg6=LL{ zqUq;L=A<}-V{-Z^O)|P!DZwz><04ggYzu8ydrhdV~8Muqp_%xp6%p@|Ix%2dR;>pA4t;=i7~5wZ!fM_ zwP42H2M5S5?&kgDB*nX+!_~7Kva@C=gy`ae@M@bOni#y9o!c`w(=n@3)?sbnMRWS{ru}byotU*j*cuzCy z4)B~|QlOzEsMMRDu7E|eJ)c^yLsCa}I9o{uN zux03j0XAK?es6W_9wev*W18yUI-rsVNE4ph9R-`^+uR#tVagA_anZW?#|mn-fnLLJ z!}o*f$CjlBS5?1%h{n(@L!Ix zdQBNc>@BY(oW7I_+?8|rby3lT#4#vtsjV75>Y#97oS^(PT9HKDWlh}J3n z=m|*1jL=Td>|IJPPSmrGn6^IoL7A8@0cp^Np8VU$G}s4>h3d#`jtdQ1dI?~d&<7*o zpMQ0iGY(a;h6Y_#m_(*LVCB9%*h|6l7fDJeoh(fe`$AOXG7vpSX(x}J#W9Nyl(zdE zn=Qn!#4x;ep(CxFqHj zak)Gsm6Q+wI(?wzd1-X@_Aez|K?0XLe7|(tw>hd+S^HogYoW}R!&>}{N}QkXFM@j| zPmoE8E`~!pc;VTB`~C-jKp+Bms!h;V$3~!69g{WpvUHsqt~> zf6DPi5)_HWX#F!H^>vC@ z3;#Tt?a;xJw0qa^Pnb_e^f!v680{|Ann|qgTOOeG1ErlRUfk?Yd0;z}$L*NusAWHt z)MOE?HND_wpVGsZ62Fw+KCj41;10Kharop#z)RPS$jA`1War4uoX#Z0(7tfc%AXHu z*nb?LR?SJ48HW(DP!fYe*1fQanG%2z0rzKCz37!N@!qGwc;3S~rJ`7eEGkfyeu(5U z$8fJH3{6mbLP2xK%p1}L3LUyQA+@&h1tN$4!QerGMz!`|x=65Ij2>OBhhO~0Ndb=$ z4!m=`^(y37H5(GuT)-z{HDXyU;f*LrRUQBgWx+c-6p=y47{XaGgZR8Ss>%*(DdBcY59sY^8k;RZA z+lF-twwH!anWN(Q(K{z$0k*t!^ ziGi-dxJ!p+c-RvWxPQDFbUC)szrqj_RFARwG`!UG`IKCmRNFY~jn(JjN@8hF?Er2o z&++LAa_q-Aqpocd;fI+~@umpUjV`x*nq3M~Kg1?4<_(n~n$`fyw)o8Fm;TuzW-_?7|6l6NtEU8&&-23OJ4TPgqhJeeB5xCsZ`F`9|00`-_6gG_d1c>MLAf z%cR;~XggMU%XnWFShXjjEO9XZd?8o!Rt~LyY}3DFO-%h1&A4?7m(7J$n<2z^O6{B$ z1u#c(OP8rm1D0e)qyqKYS!zu`?woc)d?GN_b<$>I>B^{ow&>wyaZpzaDM9>po7$qD z+Fb6rc6;Pkh3wzs>hY$z_a!4+@J{w4<2!mAA}0nLB7#O6*>}h9-8*@k9{5`&`u&hz z`d!8>w_BcRw}U3%^Ol3r&6ClCBYg2yt&RLu9`f9B6uETZXHz3w6VgoA7GRb}CI<}J zZS0yBf!Ady+yP9hn>K#4l@ZRku=-SAq8G(NGje)NMQ(P;s&`m`T4UT;9%Fhft+#6g zOANoV@WX4*efVy>Ueuil0L;9v{RHkjlu=*R}67FYc?O+MwvxRZ%mHrOG7 z9XX_L(!hjmJfpp1`f&EZg*ShSD-sgt1j9mM6WFT5&pao-dgBr@GaLnG%OzOZC*#vn z@9YcX6>-H>;1Wm51=XG&;5YhwM0x1-&nNZ z*x*WruUyJRp+b5uufyS5_LUzv|6!WoA2Qh0ClwR3<;xO5sS28#m0lk>R)1^N`LIl# z9z18uueWgrP)POfzbQ03V;kh{_kKG3WQUvJk|AvQfMIg*exY7eDas`Hy2O06)=KYH z0Ox{}w=i-nt_I9#H@ZSx!yNlwrTEqK%DxLVUrL>tUTHs|EO$vOJ3~=fZ-WfBEZSi= z`pOLuFFOrMu=)&v27OQnrL`S{e0VH03{02Fpt*rMh)~W2rS+c5w>bUUZ6m2wVH9md zq4+0!MUeApU-n-e7ChqJA$c_Hk}k7Jn=<-`GCe0x(8ZTCuX;EX$?;S^S8$O8wi$Ay zT@&z#N8s1Hxs~yXPW>{kMWuAOAz&cy;mdi!_+bOaT$`>c{fu5S&^zBW!-T)k61{O# z&;`<_3tttkqLscMg}Mqd3Ny01c#v+eMnlJ~=UUU{5zgrL6=jt*WcFwvg+HF#>F^qL zfT_ji=^k6V!hDgR8cb7HsRdDP0N$Wrw(2g%C$Ekn-elC+>xm;AkJaO5N#en?QhJdXdxNmH7aMLtxDX*)8`jMY^)9U~X7%K@K3y z`W12Tz5S)6Kg|td(@ywwxqmBta>eisE5BHCrmZhFca+8mL*3C!xy@ON3RY$ zT<)Jfp<|t{pIg~6tQXilBGdaO4M<%=+Oi=uV+DZ@gzh}1^KITlj5_Gu(-eE4`&V%K zw*r);($z;mdHaVi`)}ShMI`ne(xgLuX_poQ{XH@smx+AIdh#Ory(_y)i&vWzu6;?U zcArxPG+msAOPbevz9-vqwKz$NO%>LAo-eC#1Wn@6lcLpomBV?hIq0DPwM_4AkiHn{ zdDHgdDU+aH#?;CbOU-ASBA{N|bAa)ZxwidlC!yMU&V^T-t$&_2XfuXKX{JjE=(V{6 z0`k>J+fS+W9*Ie{3|@N%ne|HP1x>6FUm3p`Blo7!i&3!YVY&C~*}_V4vTt|U_q<*U zhwrkUd?7o7dQomPOV2!zwmZH3TizaTGum~G@eMj@@HHBq{BLDpD*if*%DE_gS2KkQ zub--nJ87sb4(#Ks7YvN;Ehsi>w*9ZuG*)^lyIlW-j;Oa`i-I@AU|-pEhzBe{yxj5$ z`=rsdKEN(?`1Wa*kpZLE_P9uapjslToQh8Cu90|p(kV^niP^AV$&z1sw>Yd_R`>R^ zsdd+TS|gf(`&WdBR#EjF+6HuQRmH12diS#xc z0KDAOo8R;T%fCyU{(bVuNe(VO?5h9b<5M74;blXZ-xpE+^y+jJcXNa0+XI<&Y&|Ux z?!1*cs9Q>}(&ge;BH0PvG?+p1kdtszB&Yf&9gGWFZNznpr^4MP(-;NqLSJ<+fsb1>-i(tLy+>b5}?z;(NG^G*+{R>d)!v^#E>i()E z+%Rf<5Pj&#u-@N!p!K6QMtrMdIHT`ZRCt(WMZ^t9$&-#_fMm!5S%&)blj~f7a0u^7 zf!Ya4`b-~SG*`;DhWL@Y@UQRFD`aXBt>r%i&7@$+w1b^L(Y}Sff$oPrF^S>h;klK| zm4M~!EW738Nx~*8l)Z3XMoDH1dha~>E4TI6BPPrfkVQQ;##V4IO|XXy=Jn>OncQnv z-SqNAR5ETaM3WF06U7C3jw*^$Hy~0c~!=q7gTNnzC9*OADW;U z34m%C;w4^c{T1T0mJRqp7fSDy{-w>~5K#{HGi9{Tm9H?JF?~&-2isuBHdm^u>yYQG5*V!v~u~c+*+XZvFa8 zAt2xes+6Q!U72Q|g3;%Dkaw_|jTru0^(v2cZ$r;mMx`8+EA~X3QAM&P^cmse-7dv#Q(ke^G_%6H=RZ)qtvL?Y|0x+lp0A(D=eUCf#Tv+qxXNbFS6?i^qbt*bIX<1= zTZY>?pNA?H%v@E?9iAXQ<#T4TUr=;eUY5KR{H&?R8TRdclsva>_(BzIGNAHH+LP4s zdFF$`-z!Ol?^eHd#DAxDpgy@(KNviGw4~CoZ*Oq*!uM^-BfuHan7 z($G1e`y08Bghbtur45hDy4D8QY_;USfQ`lWh&#GBDkkHj9TLO88jIA%uh9P8pB&lR zA|@Yb&-zULUaxad=kb_a2!%w(#5t^<*EI!wnsDN_;-@w(-I8 zMK5nY?wTHkDJVe0`LZe8+!f+_ghwiAtIWJ_NjP;rYw~!HJqgx)CTs3>rVPzy^Go-sqcGNa`dFb?Vkdp1a7Vn04SYTk`e%wQC8PR>uqj<~VOg=_7 zR)hDlK83pLR0yw(X}Q8NvcX4WN5{LO<=!_G8 zVD$BH`#;YX@xfE(u0hVhRPjClkwG$0aUh9doNCoGs*0p8J^U7s+R4E%XsAo``)9P8 zt4YSel$7U>(uhThCFI2qFF0Tgcd4|r_Nj!;QzK8N%YXf+8Cjio%&%CwqR>Rqg3ukc zE9w&~XR`H&ru!2f9hIkqa#m!+DLgBc?M)R333n&6tQsIL&77aohRpn-qqKDddA z=aUxa!71sxFp0FTdAe^^nw?J{aQ_f*nXVHy{Y~xpb;ljv*#k6qvu|Gne-z} z4WZptNm7!6%PUcUfK~fMjedc%^$%6p-K|2_s}jLEm3z~@@CQLhpxmGJSN}Q^xp}NF zePUy)!gbtmx2u~_WKM*({tV6P7uZLJoR`qldz94W!NBOX>MZ}!jl`WRbDeKWdFvgQ zVa@XxLWWf0Ihn%R_OzwXLP?48_g{UBlIvVQ*f+Ya$Z}KTPP;d_aItALDR5S@5I*O> zx5t20>$dNI*J3CYR;Y}Vl;5VGM9x@G+^vaY&1cS2n7Ps588R%Yr+DGzlU&49eLzknn#h5c~XVS7?f4(U=RXyJmb~i1((5x4-=7aXU zauT`zQZ~40KwqpX6upA#ITdzS&+BUbM6q{|601iUDBPR6!ZHc0^)SN5(H5_@bTB3iOLllq_G24BZF{j(HK$** z)cu$xfNQLj{4+jWHSX4$o#{OUxaIQ1M)!kk&AcCuDyo2CuiKX?98bw9XMHe_GU1zF zzpSF8@8Mw$pBCHa5xcZ*{JTF0L}uK%a7#?Uw-C&S@zTfsjMW zNBtn?O1eDnv#qI+e@7I&C)I}PLcZj7_@rMB$ zTRF4?e2Dmpn~$MXg~zGL*eCW+*5SPQK^gN-7b@JTFVl}G?e6T0^w7uN&T$<5vL_Im zbKzpIMV)=A!(}ty9fHIB)#k3Llcv7Qdha#+6rhqdmre#ZTR-P*co-Yi{P?1A@gqd@ zvUH&JRnwc2yZ@p+^|5KuS%SuFeyKe+q8IgJz7dM!hp8QwbvXTn*T%V9E$AV`+kjI$3K8_~lXu zwMwbge*eXOj#aDi_H_KM(YmtgyFaOW4@+ApciQlOjQNQBJNW4@>@6rBP|W-*U%%L4 zxOug7I=h>dwi3;`5A*~tjZaKjHG9wde#%AUb5=JT`t^CiTpsSX7QZ34RMlp_*G%s# zy3lnTEl6W|v{lnCw6agb>t@L>yD_U^wHIgsqRD%OexLYBtLiNE{W!anUxzOWSF=L4 zIn-LsrgxOZ6dt_FUAa^{(GOm>RnIu-d{hy9(LIAZj`c{(PGV3ovfWSC`AE8Xd#VS( z8f$<#)M%U>FTRo(TL*S32j~?xgQC1ap52>EHv+($zXx@@85f7mL=pxMJo~)EPIf<% zW6FBT?kXvaL6ad2)$$nk03g?gcklBA9=J284zf*zTnW7IUG*A}nx+?ioC~`aqnwb! zqy`^^h(Pz44>+aYs_Y|uk@cGN?a9uqKcLiEVv8=s{?Jv9^}aU~=Gyv?xUkROcu&<^ zEnTVeMukdNN=9C&CutlZ^2-nGhYoIi7CQW#UW)bO87`6;WE!6Q82b6i$$VkxJh-N# z&q?(3hE_^z0XkkwY<-+k&k$@aQeDH50vuC;EwUtu?F){3+_$T7 z(a_ih%peVY{?l}W)Qc;SBCY->zizN7BXNhbxHnt%;dXodG0@J=-5 z$L7{VLf8oM5yd_#F;k3rXC+7bHH8w)B6y^ss%3ZUosVa44?QVIn8S#m{^+gRP(t>dqK=1_kID$^zY4Su73}akY()U0b7Oo%C_C; z%irt4Qx{|mbgKa(JD6>oG>X(1?=Y90(bJ2!#Wi01?PQuYaXu(jkzT=j@fpiE7xgOE zb-F2eZzsH!>BKg2@j)X`j&M|O=Gp(T?K_1e@z9Z}Jk}rGtC6t;D3a-{-Mvk^TEiIM zQMa3pgu$-QQfTtiDx5aK9F>EkB7wCdM}BLU#Br%>q{3}1>u47_gXlCCrtm!RtNxl+{Pn7zluF{q2N!&x!nD7t zU$;3qKli>jrGNfbj#{{KUA}q`M^);l##-6LoWM&a1Bu|4c#P!}u%Pt#6SBY2VEr?9 zLuswkF0quNVLn>DpsD$o=CLE&kJy&S12*MSAAcnJF3}#(k4(g0uk+%C?&)Mz1Q(Y6 zO$Bi6j>6z#)ST*B|C#GaChjDkLB?&bf*D=Rv!DLDIi)3ap2 zpHbQW<4(3-WkHQ3N^@ueyY+GEf&a(Sc?Zrd* zG^h|~eClG`GZmXM^&Zxl&RCxN88C1+qO!>|3E!=sr+-tf!Fp4Cl6W0DC%+gh%#QsXGWy2{&Vc;=jhOX zBX`sicV0F+X-hX&Gw{kAe{ZMjvBtcP-rZ3Vo#EnxgPDIN(?!F( z++yG9RqQJJ=+!E?SvJh1=3L2Tnclmy(p67ZdF#zep9>w*_3YWNq~=?-%+vY%f%4Qr z^J3Yd7N31*HU(r@s09fLEH7qWlPV2uRxIqj9m}y!V>Yxk#Qi>#-u}3Dq3_So_Ya>0 z#+I5e_r2*8Vf=ft^67R~n(@cp0~*2ZjmDi1*BNVz2TiVVHD1@lby+sr=7UkXQAp9l zs`@XpBM;WTN|@Mu+%cMN_pe`>@w9DUm-TDcUsLy1)t}FfoZie+;+HTG`}41j;Il>< z`h~nH+pvromKT6lfIPwOg>Sse#Gl%pDBOw-c!8( zTeDLSkE);2Hd|TqT7ce~I5;4=!YAdn?Rf5Mj(nk}-6Ov_1&mCpmR0vFrJ6_TgL8V{4Q`)^vxLN=JS5Dc@H2%lEyS(*dhN z)1YT5`Jwvfoo}V+p6S?Z{3b@*(X)d0hG=7eVjZjcUiYs4^q3T8j7WH*KuC{}^QBdO z(yI%Thvr%G)~-q~i>%^PkqrqbldZXTj9wwN;+S#a%#32il}}R-&zi~eDpqW?%PbEP ztmR+3{I#<~<8it1rJcoLv(M&9n|JW#IOVL(txU8(lzH%9Kk zD}8gSpC4PAzs8AKyzhazoPWtPn{@h>y)ZT6*){<4vwp3DQX{iQlZj%S)<~3UUq#>2Y< z`6tUxOsqI|REujNQ*HRSwd1Kt+D5M@j_0&X*4qk;OTYbQBR4r4&w5lLSDRnVdN6`+Yhw;285~o}Fv&rRpwbh;7*PtLGCE zoNHdlTnTv775~Pys)7+BX59AW^K?bf>kB?v6NlUv3pUt}D;0E>Pixhm4}JZ$vOVlb zy=qLA|HGr_0^8}Hp;ymjG)kY| z5XW#kzUk)F`xQe{6O!Fu*2xb!-aF{(#oY6ii(wXi-6qh_Y+5yasad|krkdQu%Z$k4 z1rN>4M0^6Nl~tE};#ukYMSk{%@b_RhKM z%EBOh1d+=U3{|E?-b%!e=emD{N>mGUS%N$;?TK z?i4sj>x@_;{^itxMDb_41-{*wn{D)|rui>=(WmzEP}f>_t(?vF#xxsQy-*JNTyU{a zNYCcJ#?HN8Be_F#Z@;j;W;A*$!G7nW(UT2l(gR~MLyi_pxiMC{@NW-~%bF_~IAf~* zuq1Xfo4m8ihjnv#(d_YdLSPcDg9zMs#3p)*vL!1wy9<7X68Fd68cK*KShpL_n$2P1n&4Pr|PYef}G_z(PWS?ftZSLn()o z!=mQl4H2eiR8Isf-x=@ks`J9^V|@6rs4t_F9Vz2xb;>|lG%O2iPngrFJiK7n=aFp+$Ff%iB);k@RDOB%pv8`l z8d{fL51dla{~>(^On&EimZk5`0byx_7y5Wxy>@moAgU> zpN!_pwz+i@Zx={%8i_j8k2pLlJ=yzFRJCBo>yK~I4z=&$sdYzLBg5fq2w|g!I(KI7!WxEuc3_=4> zXoLi`b9U(!4+@vc21Osb5&l_j!b)TAGDEgYueWW7L)mP=+M4e8lz&D4SX0*zh2{PW zyw?BhdUN2%Cb?Hh* zs+CFA9LuSY7qxvk9^FS`ye#$yC@$ovT9zG}x6w;O&!t6p!>_42FM5eZ%i`)}<9uzC zGXt&Js+zN3HB+V7i$_XdCdJ+i%$W@RH*Hw;_gmWOmgR}&!jHBnT;=nTKJvkHVdMfK zz7(@>&+2If-HDIHIkJyl6geLoTl>9{D@gHN#g%l7fXo+nN0nu=A4X~uzh$|r=9 z6`p$w&OYGiGzl46)8&8v>W$KGA3ugoxw-H99vsy4*Fr{2DtCJG=seTFbcz8$Bis~;}qq!M#nb9?xfBdIL&s)$G$3)b}Lw;Xar&#J!qkP(B zbx-oJRoI!lOYXe#(|%+k>c%xj*aW`RgP`5bx4;{4Rw{F zk1VR+U;4T1_^cq;NTvEc$!UGV{!C$ypw{Nksx`CEwWclfEkZbz&@6NL4ep)LrX4Y&>`}k;RN0ssYY4s}mA2<7C9J+3qtVpTJ`Tc^^ zEnr-EFnICW=x~SDk@Bv}+EI;)JC=JFroGtpP#{S#`$)8wbCB_^KI|g9()<=dEEroQfT=J;K6&;+&7rjS=}DUyf<*eQl z{>gFH63@|km#CSe4>Db2?_YTLL!#b#rC+Ku!=?A!$m9%nlF3}0bH}+QlA{YN#@<|A zF;6RY?K!vopSN7#wYGPNRpxk0i=m~pcKu%1`{CZXZ(q&UKG$N@cZM|G*1mskpG;cw zpn8~oNNGq~-_r$6f$m|xy?$f&>$?BOtmIENSbkxoqj|WLorkU9!Ho|+ZJ!TyA9de4 zBlXq9f7<<}(@fw@=e&lYM@Fm`w>{q^PXU& zcs4B0&To_z=WwzuaZW0upWUl+|Gxmn;lkVWv015puD#|l;|?B9>~~CzkAL~$-}RP} z`C(+~gUNh8^HW?VKI2wVJ5Sz}b2d!q&%X8k(j@Ko)-(FsZ?_2795}mj^wT5n+A*8< z;@Z*rfy3E7n-@C`Hv}BK=J`Zf+B#I|?WvJ_*RPH)4C9-0`6^=g+4qZiSLx(&!xAei zqx1cm(;@TpZ|Uc=HhTviR?(k3yvMrgeOY2%tDBQVtlMIuUmK~C-QPaWJd#k@Actg^@ad$MsiYN$?f!*{*X5-_4&xJ z4(;Ey%|_)V=lYVzH3yPXrJwEjdn6+z5{PI1=GK9Z*p_wp=}$yF+7~)qv3xe zOkHN9pTi=#@A|6VcUYArpZQO{*H7uP_#ojWoi6frNb}4oL#NiBy|K%; z`P4`@To(2`bIIbJO3u{1w)(F{fyF1wQ|*@iphc$k5OMhEImzOicH# zmQ@~NglB(JnviI}Bti{J~Ea5OpSNN7PF++wA+dZ=>}CH=T!}b%l%~&x(e{Z!5Y} zZx)=mN_!?6bm--f_jp~R?uYT*m*W28SDhPX=ZmlE|1tG;G9+(Lo6Xtt3H}~K?W2D( zD<+s}Y(2tU3Awv_4RT&DJGp-Z89BbGyX*d2whSqd4FR`0Ww-k|> z`g(d@*Yc7RkIT5lgBJJbc?Gzv4Hz1{WXSHp5^+6fID>1?-sOG!gNDSeRFpP9)A1|q zD9O8H_VR^&)}!IEQN;qIuL;f}4^`{xgoZYVPaXdmV&ywBU~ufh^Ob9+vO}ZU3aq9k zIY&1M@@DsnPgL7Sv_1D~Kea`e`O~S!=%}&JP;CGD@u+_W=gMAGZTu@F^{U88^_@P0 z!+N7;R+HdN*WB4ld7l%P`E+;x#q%oaUYL@+x9}(HRmGF<3DA&q2%YpF-z7pG5D{ z@$_qZI&bukyj01)bIK~{8S_uuit&K}rpEKX|CY4RIrUa(`TgD1sp;HRko)Ut>GHYc zlJ4L*pKp5OJqf;ftJWp0>3^vmT>0i)tj@pdD}`5U$-K1xcP3cY%<0tNu{W>kY{mu- zI4^tBr{^D-w5uzr`i#ovrq+p#BDKEqd(LY9Y4kmtW|BT3t2x)V(@CyH^Z0_80OJe! z2Tq&`D6ZgeVWEHWZudErvyU%9d`aJn=jzWFGURxK86TcB4UhXCI-?oc%{HFPI56B! zzaA*~t*hEy;X$P4PT6+O_+4i;eJj}orDNzXpG^7N2KDPte;OU1sW=w*=xeI@#f48= z7C2A&_U_-WV;1q$@mLNc%0jcb zX>`BM?p&E!B(}#QBzJVOvD#mj9?z{b@v`QgNSjQAWlrW|<2fT6&x~m~hr4lE{aeHN z4@`TdUU0s?TjAc+pp&zC&Z0(%FiB%We+TcS=A|}+hCM-b*S-4X(@hyHf+}etD;PC% zYP)~>OH>6II1e`R&ulh|2vV9l<8r*mV!B}enuTWJ3fy- zzv)qZzl-_0jY>z@=JoQ=b@UZf+Ru~X2%B>nI>HEiL;fAW)la`;j=CvwF8Hw0iD{ZE zUb?kIxqIy1nb+G~x5wPnlg$YibiVaZzRfXAUp-B*DUag~%}rvuU3SA?A+=}K)tOa` zyUTp14a{$jp8e}=^|kKr_<0K{zUIeo0!_Yqu3~;U2H6T(_}->@?EbjxOFtS zZ8g)TM_YDeSExG+SZPO_eCprVlv=AKpzM^by(&cW>-Wd~d8YodVI4DFt1FVFv-Db{ zhCdYNRGFwLeNC!mM2(oN1nx=)8#3zDZZg#(bdK%j4eAk0 z%ZYu)!TQdoB zF6q4fqu_n1%LC)c=>_^>yleZ9j*a$wti4z<_T^1j#mM@kfJH`Mdub*rbpa-$JJs6M|$l1%9ftA-&uLTD}@Xz>Z?r37$+Z1 zZZq_KxrbX~+y0?#t{cUfAvLpX3);qSYM(f0QOwD8yVP=9&v(W|xV38Ox`D==u`KTB zZ&QC#yd9X=Z3npLs$874=WPB4y?N_ZezyB%TY0UVse=fgii*U<@4!QU{=Is2pZ=`i z#UYOggPPf{vYwImQf&hAAq$ghLIRsRPsnt>ciAx&sA7L_g z%a;1Ba;=%6#k8H#-lSVCQuVwuWza1rR%v!hvP7=3Ql_oA`cAW;_7U}Hb490Fv+!Z9 z$@;5Kdvs$p&sc{^Zhak6RR6?o-oCyQiKrO|R1l6DRUzaQq zowYPO$k;`2n`Ws?*S*ldZG|z4mxcO$c#_6O8HE7`Yp1y`DMq-JS-*XtYxv`|MNOc^ zwlg|5H=FK6J`KC^F{kQexA2=~hPN42VJgP${^JK1TEr+9yzZjw$P9)YYJOgr74Uj+ z`-@vKoflfWXAUf^?aRtv^Co^v#Yobwo^qe6z2!_s$QhYfA;D97w`Fe~+2SFWng3nL zN$2Q*jup2rGfR>qW0zs+#KnxP)!$?JvY6$^ZTqQN$$*%!y_QaF^NM+K7(eqW=*(%a(T z(pR)1?(4Vp>Jvv?3Y&d9N{7FE%*Z-A&>Jz5;`CVOglIi~uw8a+=TYTFg)y3eD_B=7 zjnn#izBepSfOF-_yFRO!56qf}wwU=g9JuW*=kkF*aMHA3V@$GL@GgSiFW- zr^t2PNX|>KV#QM8Ar}Nc zR2XIT56u>{i7#sqdz+GRhOhaBk!$fZ!~I}~tHVrZA75vA{d)<4+OYDN!t?ectZDZG zisWt-C4a1UyK^ZlJUH~?fI@+qo-vmT!{O%OsfkYdvodvuhr=IUpIxrd8_|3^Y+yl7 z(DfEMjVa5gt|}K7X;B2#xHVG~FlzGg zB17Vzdsvo9ZF8{QQ)_2b5k zPj-am8F0j`WilsKWv4zoNXaXETlJ`C>iuJnSHAyr_`mH{iuQJb6oCHJ-d_9v_P-cvHN zpOwR12RQ5*!^xcy;mL|q^=8pdru(-}X)HUDqFm9rgjdk+U|Y}UL9W#BqY;{$(}t(g z*f;%J^q6P8M%9IzVN%8uTL+9ouFXBTv_WYuzG8V%X;Qe-oLVy zRfm6@_m25G-1BKwocvX_=fVrue>UCETeD@B3RmeD5A~CEw@|t-7`_%#t*Ko-BC3@emSC3b?%i; z%il#TF?q#dxm;uRrJirEuAXzQ;RNLj5z<{+iueq_4%)?HRtB{S*UYCBfkGTfnci*1MYm!xaUwl`cp zyuIVH)b{$zcH5gzE{OiMg+21u*0qtpY+WOzw%A5WZS!T%c=PVOo%D;ItEumrE`NCU z^s?T&mzVwD)m;{N_xj}2Dw$Osr%y1o zZ>GpUpZ{vh(nvGKrRUwZxGP0Jmk^a&m+Qk7Ev%yR^m2>DeCriUbyNVA!x$7?UQu+Ep&UV1#v zO!-ut*C1`i=Lbi0lZ5DM%iO}}CQn!9YKuPJyXA*kw1%YUYMWeDu4s)dKQ5Z)ZW4X$ zWjWkl9#J~M#dAP8{8hrSb3Zba z&L^5T-=LrSRb+L=?ZOr(VUCI|VZuh`QS;CJkX*7o-MslBz40?Lt#3)hXZx7dS^l{z zvdr{~TlN+eVWS!5g8}m%kE>iBq(l4C?eXT#FX#$qu59&&#rBq1e0Hu`k>ww?g?H9o zar*t$t-&>UH z_iyh)9?#`hSZ~-Zk-sIPcedqV=vgZ>`@c_@2!F~K)JtjE9D4skl>b2Fpc2Og#%AG9 zHwFDuT3AExr$q@!zS-+Vvb92w#zk%Z5vizjd>c(hQGV%ro3kyFig&KOUBM&otD3d) z<^kcy*94nVS{8;L4UfwI8M#U6_~*DA9)~lcyrkZ|2q77sp+{4qh9uq`*-aio6_-uN zOI<0lGE4gVbgl5d(2=;P+70qQ6qjgOhb~gOF=jD$xx04XMWX5-ZD?HdiU{R%XC(L+ zepnHIn-Q-RbU34SJ2Tw}z$s;|3nmBG3)@b+9$Z3Xa>zRMl=N3QaqT8|eBXoD64_wY zmz0qSOOPdG4kV{#;}sSDs5cyLg-bytCa1rm6|RKbbe05cSa~g#&^G3u@(v(<=OQ#( zOi6i2UC0ku6=F{UnzSdD1e;el*||fWGOCT5A@dy4S0UY=lDd!`RHL_7sYdUT z5BO8T`Usw&+G!#~w>2{qlPD=KJDf)T7!hME$;gRo+TDOFkg?pijIO|XWUKvSYK&E9 zs4|C?YE9~IAH`FZpRGca@N+_uuUpAZ(xq&iuTA=5R8}4r}Q?mt~ z!fc&a|C{X)X3NH*h2r<yI%yKMB=`w@o2mMvfNlW1^O!LMKZvuziWA3C{vc1Y#!)Li>T4aMYwefB% z?Gm*b_fqSk_T2;LPL3&`Jq`DMWu7&C87WF<7Q^dN%UaH39vx+#rF4aj`szp$(hYg{bm8A~9>|El zR$jnnY>{({o79)FN@9^C<{V;O5WAO}O#-*Vv7YK?jwA2O`R_>S zYY_72lid>1jIDJ8In@(B0Lv3GLnD?I4%$#NgmY4X^3-iAVB_oK|9qQp8yi?2QWv{n z%h7jkx2TJ|AC3?u>ME=$v$o~lerWW`m$Lg+4hzbLYrfml&YNvHvPWrWOPn3Y)!P+zOCFpz*~u4wo$+8BkT}0R4N9rx zqtZ|E!Ee*F>jCTw&7MiMRO?H+aH)11bN4DrCPMN6Rj_`$0{$`Lr{ygoGQL-fd>|wH z>wxY!MWp%L^+i_uROolNQRy7{2ZVXo_uOw?D)r|c)o6!22GJGHo6rtOLpK|xqdFJm zXz#raQBnLGNYwkYwTGEjSMO2|U1c~nVag)v6w;Jb#40zWp8}*fC9#@3;-pT^sE={~ zox_b(J)lmen~rhUqOEx;ZK3dgJ+GqYg$)>6OG!-0?Q8mRD!@Y+0XNQ)^Lj|(yc`=X zWNWFb{NR9guc>zL$ftgSsKz5Z5m!+Zg{wFv`5!tT=sgUXEMfIuFgwy-c>i}*ugO)_ zf7*!lgOs!+Qo)~m>Ox~0W)Q#(0}CL}P*MVNGbNQMnX-FJPV~C&|2hVtqvImd@xx+D z;?h6p5-AeDo_ug3UN3Sjv;i?9<9nBS;OfmIqOP*u+uBY(6_D$xjlOa~*nZlTC;r~= z`2C*v28HFNVmxK1e>*-D5l` zBug{{14!RAUQVXJ_VGyO#y>=Vvsg@NFz!rL$X8aD?z$aaxtVYWC^wE zK}pJH02#0dfHEbiRei`wYE_ex7*%MNeCQ?97@fs5k(4|SS%U)_MG$GeUxBX5t0?J-o@m8EYQIVNy}N#YVBO#=A%hqM+tm78qt2#@hzVgd z*2L&S?Wd{6l?mwPfo_#&Af*xXj_K)*%h>uit?~j^J1|)#`M;ByAIlM|^kx?|f$HhX32n%Pv>7EzQmcHB&3Nr*3?VfsKp3)sl2VkUST0C!iWQ?IwJHEPj5orJ zF(i!wgoun^8vn@Z+fw=wd7twp33A+XuIE<1%DKha=CeVjFR3S)Jj4yHyrYrushZ-l zRtj58_td6fUD%;P>CBk&w}<5Ye3cO&xBNfCfh^WrE+ToGy#V(vh75xgRf2S(BpPns zN=ZdXAIS5RO@)$6u)2px5gz+9F;~g^e=Bq44vb}c8d8^%iI7E>SdE4JOReS*Y5soq z=Cw%DS>m&Z)oUX;W!87hcE}R)17_csLH>|jW%#TL?N=oBI?kd!Y6fVBL zC1bp7D&a&>5$-B07b%kKA%!SO1#X43#0i@u(DoFL)x-g5r2r{PNoti3vJtC7l_{y1 zUONdRZ@00-v6Iiv+#1wew(I{F%lY5M?w}@qNxmW_W9nf{W$I2capb~`+G)YRmkTf9 z5)6=7mM zNdw8Hmp@7B`K|=1 z0lDNXX#My+M*PQ&tFQJG_qw^Y9~`6LUkAVaDablXMnGQQfYpnT2dGu5*FtJF9;-Wu zw5k10J}Hw19~98blalg~MwC>BWEU2Hd_4xkds>m^mHGXZdS!-;u`dU!kUaSog5(8L zH?O>*Q@i>29|{smbA9*i7M0{gk$Zx+3y`lch|>u-I>Zmzg_n0o9Ziz0$QL){C7Hyl z6PyDnXz7qXPQ!&vYQ^6q{{cX)Ewj|@n+pB>%IvXvZA(Lj%I{O;8=RYrQA1&y=^jo? z!@eZ(wFIbmr$mTNn;=(Wt1cz*6-`r-pz$Bo$RBL-?i4msrxumrKbDOBaMB>K3@K@8 zkkgR6&Jt-}n{T&dG{M9-flR6Tm-f6~m~>Fy$3or##Qy3Wu@@LZE;|b;40(r=Qjmu! zDMm?jEf9eGg{}q0kWv&NPe}?;fxJk`#gKNCzIbz!9d&NpC=^J!3WJ)|5Z*Fkns zlDYsUr1S=?J|$B5Tkn5EuSZL7C4{f1j-QM(yKbnkocsDIYYliySTk|u6^AHk3b}`p z){teCw1DI!Qu$Zj#JLUR(n`{iwTY7FYwa&=oBnxk(dc=Tu4Uxa5P6VJKERWGY3!%r z)u^yWPzU4{w#D8C7|I`)>a7nsK}i~<0Hz*Z0C|Rz5|EoIiL=Ddn@_#I6@^GNut7L4 zWI1(Ps+dwCCh?Wze^e&5MO|43w!RJhcR542?qTZnki+E+YsCvb)l8XsMYGuu(w9^) zr$P@&solk#5{svqXF$ls7cfR6m8!<-ljKJ)n1me`cAoCJxfoIgTRoPZ9Ms-P0%(9RVPwJOJeH3gWtOf7%s+CRDZv zwrcsvPgBUqHppi3K{hK8xwS0#-CCO8%Nbr6s?Q?J5_j)x^-Yz|SWL-{kbO$V=WA3c zDgl%SC8-Y5=&(KQr#BLr2NCYQcvSvq$7UEWDKV?gFVUxK;>DXxCa?S)3#1?==Rqb> zk^|D_8~`22=ChD$luVi0wN2{B>?X`pZAM}hs?^o-E}G-QzBh5@#7U7KWMF?S+6Gh7 z1~L|sZVp+mWJ)gM#O(v;E{|E5ijbd)TuUI|`o=Vz6+WDybc?~mWf}1*XD^@SmT3_y zYvJuoj@EM=trxw+0&{_2?>HKsflJ3v9y#GOa@t3)z z%5v7!Jgec_F1qEs*V9a6i>POw2?ux@_E}zivs~?iu+?%Gl`98C6P`-6PImeFWghbD z{yCJV>6%)X?xOWvv|(?;;`F+xy^Z@KROaU`CrjoBwdA7?sHE1ZJ&jtB@NAjc*Dm@^ z=J2Y|lTMbAZnE_+ckbD-tfefps%yNex<9CSRVYoJOPze5#%DKH|Cr-ZZ&DFEDSj&M zFKHgiqztTRIcM^r@A6{$UDY+4n7emHq%t1lv%$hoEEidcdL2&IzMGO=Uc&ua@!gKk z?#9Ugaiq;7(630cAA<-Zlu#6Q6c2dX;LhhJIiFcDgb@0Rp!GqtRtIPgumqqN1+oGZ z0Vod83m}yp3~&d%r%-k(tNpPe?to zD(i{_?S|7f%vJwapZpsu-6x2cXBtiorA8%=qiisPysM(k9wOQ9jVY| z^PF}SdeoP6uX?QBiTx8P+@DhS@Db6)LQ3%(d*(+0_Xb)AI{~>btJ8naBlM+{#f%5q zEvY8{tOPPwp%T60{#3N+_NaJU9B#5X_2TGX$?A^sj^mcGR>Oq)L%M0sk~b#-a7I6#t?D>F4X=7Nt}Toc3Cr9YavmQWedK;$ z49Drl*{eCREsbql%8Vl#1S6Ze6&h~Pgn+iPOdDFjunvctKMp zjvl4Uiu5c#LfZn`%tDs3<`HTY4_RyFz$4#k^8Pu0uMT7@%r_&?sLTd&k%)dPlG?Nc zMGR#piXO@y(1oUi?niKOBq>dth~q{&jimcxh<-EB>p&-hP6O?aq5=9hCUD@w1P1?2 zpbBbN_!-9S#H}7$B*9I9WN8bM)IFHq0cZS)fX4`3N8&f5n>Lbuv!bw*B15sfge`;L z*9fz4VV2XdW#XLr7}^V#&iMfO0U7`l0C)*tIlusbLlj67-Rxd|EJMq27OO64>*>o(S3dHGj;G+;e@96nZQGzz1Lt0AHB|&N$hND?YpkRT6Q0dyVcO`r`>(m)5GXu{%zrj=~4EQVzn zEbn2FgpxsLIkH>M0*;HQ4pw6GN8?0b#vtaA&Cl#8+@u&$tQaz7k$D11{D7rl(+?-j zy}3wA7aKws9w#60H0=|y7eMaMXPDf2PZ#;cVK+5a7^|BWP$!rn!@ui$M&4F^Ni&NO zE05S3#9H8BNjMV?XEMf_LUE=UC4edf^b7J)M@wT&8H(9vaJI=HgzzFn9Q`#hn;?oa z=qd~tN(M}tkpk@pS{k%IXm5bs6iCI21C#?83XmJ1AV3;G6M$hzoD~Hq32-@zFKBVl znx;0)QpJ`O6AdEb7TPcaJ=Tyiu%>xMB3TKPUeg@X!e%txv+A-k-B9pPZ<6@QY{sYp zaX7J^7^`mp&H3XcZJ2{Bb;T2^A_Z&KczlURTRh&3Ag;~qB9v^T&m z3Z!B&?W7#QP=MS31p(3k8UqYP;tVydB*5h;zM#cHYnnPU0~K4+Oyr1|`bWO2(Y>rm zXZT(A&ZvXnCnbdUPhZmT$tuRE5dp(k{*mvd*ASZLM+41-J(PSO3{nJ3$`ORJVIyn$ z$WxBQBP)5#nNM6M{3M9Jn%L&}ND?yxK5GIz!9K_bmf##BE&I@oSKNS8w50?>S*_kgAj+7DV2 zv@_^x#Qv9!Y3JsH-T~SJ*=?Y`L3dL$m5piV1OPe$3rT<-wB3Q$i_Y?IIpi@ObkidECrNT;wk$98+E?OKeBE zNYWQ8_?DoEq3lG_L)im!AInM54W zv{VKbPFOa=vICYpShS&5Va<^2?qvnXPt;|0V*3$6qQroOf>=N{?{lLF0%Ji@N9Jnm z{zZ~Bu1Oy@FE-M=lMl@~0jdI|PB#e90AK*XAqwONi0q@90F9A23y>S2AV4{QhDZ#= z)sHL%s0FYGpfW%kfY}s?#Fg_vbAeU^O&xR`v^Z!@&`zMq-*PoAQbA>7+L2|Tb&)*) zS|7AG=q?IGb|9u5(Ew-!a0Z|tKpH?}fFVenqNY^=s148sr605=Xy=Gr2G9Dv%%2T)1M!l+^$! zpCn5*9E8f|AZtgEXTya@Hu7k=khlV+NYKb}V*4tIByDuagxQ`dc>QFD&AqH9cEFNt!%nI_;`i%mHcV-!}DBCrGSw?&d9Zx#Y~&BFvdXs(UsVgT;}q)t}? zaF_yl0P+IV2S{aC0-QnOBtRNKVfI>*T3a}nv62Ke^L}JK%fI0vdp-6%* z1Wg^Z8MHI#Dk_#5l*(QKdLigM&~3;r0NqW|R5qra(gP?9upHndz+r$U0K*W=pc1tJ z;*Ik4CctKZvAD||(A5;pvwlojCOnzJLxe?~`IxdDJ6W;gUQd3!Lc}K)QXW3pz%&<- z$k<`D{G7>FtR!HCN8)A9Qg+2@k%fDe=vPFrq#TjVh~Lac)^wlZF%plg^y zFcuU$xcsm?fD`MVB(N6;v3=%1b4fJk1-KI+b-E6K0RV?6kQ1OZKxF@L0vL+K8GwQS z%Q`wmIj{rbNfWZJK0nz{(0}MstKWbXMQGVD0bOTs}(hS-ebQOo| zw8-fvyP4(?0|kuuwv!J?34d@vERqO)JgF~FHF?1Pd`x+QfVHd=4LK{>2(7$VjsA`A zRG*ClAR894lrxV|#yn)L6L~p&c;qCHA5TNSTsR9qg53LXm-#lgbq}OWd$sKu&Bba3zh=Pf(F^F z#N3Rg{ZGj*FE|fyvc%>ijy#-c2$vCyO(T?06n2V*i(D7U~A|s_BXSTrL1{N#*|m_khQlQc;v$) zCwU})lh%uzULBJ0@+WNCPt2K0ZV|+%hwx$~6yvcNk4=cr;)j`=m>(Ug7W|;OKg_n z$ita_MiL?ho8&JRr=JT&VW(KQ+ThRUCP|$v7()mXh@kmFG*<^`53mHF7X`8cB$sY; zOdOyWKq@;J;3U8yfQCp6T0p?D1pr+DDk9MWU=lz%3PfTMUe7U6fQJB@qG*G51KkWd z8*~K~i$TYPK~vcWK&yh@1-byV6Gc2LoEzrUM1yHVozJ$^M zOByT%lm$&I=fk2)EXTQsTFp(?E^(7U4VxJrX2@Yy{by1Th&-iXg>u zVhs2z<&epaG1kLojpmt8NlFPoZ-8AmU8O8QZh)HsIsz;QNM#RGAeBfX;6E0CrU2Ih zj0Kp4L>fS2B!n*ym=YFbu+YXMpSOahn#&;(!@_3Gk{HA{`m33}7=6qBFx8hdRv z5}}}&V$v=}MEmOHg^l|lxJb$1JUFK8`^1X5UoQP+jv9-0;R85QPi8P)t|0q+SjoNt zD_QbaAi!@Wtcl=p1s+9_b9?U+`omUa+>9i)CKm1KluJCs*TI3phB6<8ixdN1nDtNrzAD^Y&1IU)*Yx+_Qs=?Jnp0b(MY6ak9m#2E0G%OR5;W2}eG8qG7Gl9UpF zUI0Jibmg)DxdCnl=m@YJ;3U8y3ZxQg1e{<2XbNyGz*vAu0ObH0Au(76AO}E8fVKc{ z0^~+%2AxgOhz-V|6KtT@f!+j~%1#4a0NM$3C6$e7C%8acgSH1<0&o~$7X?zW)U>Ps z*8;Qvm;^8fpfSKu>eb~VL(EVkbAsNqJk6wiUSprldLqi0uW~~8(;IR^WYg^rYC*7* z(#hsKono+8gMLhqfO03x7%vqG9mMv1Ou6}q1GATtKnxDDG#yWV*KQ)A<9G1!w>;0N^kMBD-1~APtFO z0NDX@0~7>E189Oox+npe5&(SwiU3pwXag_@AdLewV(A!^iGwnNK=XnY2dxR3%65jO zn##tsOexTQprt|UgZ2j9O@UM_re(?j3^r+@dYgoTJxzH zvsdx{S(8;nY)CL;ih4XDW#Gv(@>3Zir12!S`$f((R^Dmbz`gSH1QxYk8=>&%_QPyq zV>e%5pOfr=;~*h*@c>e=W{t;}c(ldi&4_4*%4T+wktINEE7?hi&l2L>CxXI@qKYC- z$~QHb+hO*FxdW5*BcX{(5F`nu7{f;%v;ycl(3?OTprnEJN6~=A0ZmKUU|9^yGFaZj zA_*mf&T?e;0Sh=TqTb-V$I&7+EFRcexRj6>x1?N*hPU#{#XunlG=GG$xRVvWw zo>BK@{dA!Zd+Ao@kw;HXkGWlC_d0JPZG?S^lhS?7&*yb6rRy#E@?1oUKHfhUKxZb( zc2`Xed#&e;y3tk-e9Dg%( z|NW4_#mcbYUF99Wblie{4-~$?Ag3(UY8xK`mEl6aWH?6 zTWO1wp8f2@Rl6^i-N=&Fi>>^AH1}?jhtHuKt=XOeCpOKtKWsnLoch_cz0-Drd(f$* z?0VVD5dViEbnn|`W|`EgjU<8a2Y{M7=! z#=Hlrv)T(P8~yDDKS%x=Nj)gOB=blbyI)4r+pNuA534r^2L!u6KH2^`p?TtaW_bhi zw^^RSk=Kt52EQ`DhXj9o&)PWo@AXdK2k&XcZfUFUh57p2uKp-}{`IhWQG-k2Z>QFd z>hj+Le%C%bm9!5vO?G^CyuN3rbD8O${KE>KwsUf7sfE8%UvJJU_qbh@cVy_-pvsJ+ z?D5a7_MaEV8m)X>Hdih8oc`0q;$L-(lp15la8KTBcSTT#eu>=AxwaCo8R@yX)l7%g z%zBUTg3$6Kf8E5Z90jZ`yZv@o7fybDXs}#2w9WaWl-hZA+gFnwCPiDC!UFC;3f{Z% z--#5u)2ClUZ!NqI=}7&YO8c<)z1VYoJ*n5Dn)!7XM^E$Lefs!PUzL0o*N3^b@$txG zaefDjoSnGuwl+29f5^O7)CK_ zM+@$<>M3cR&qORL`O{ZoC$`^l8pk#zHNt}4CL0dZ1K)HZwWz4T3Pv-jRh z`)I{_N3+5OI;1}?j}I@s6<#zrq?UhVuV-HCQwR0|)x|A`_SPsZ`L>3Apto#QhM(Ez z?W?<%$Dh4%Yc%G60H#1$zlrgtM)5j9Z^{j?ZFT2NQQO}kr_$~8MNm2uZ8&Q=9**S7j)Ro(XZI_6e&Y}X8X zRUz87!-cY{J6pGosV}>}ip5pg*!5M|tBSxLUlUo?bUnU~&Q)brQ;_$m226$CgVSw4 zyK1c$8(6!)w^Y?UUAw!rzp8Ayw%Z$4^-I?-na_PPX>e1Fv}$ovYIJ=S->Xuh=IqxL z4Oq*uzA6Q}P64Va*;yUOn(Zr(W6cFROA|?|@|o+aOrR=;iB?tBa-I5BRnAh^$CRq9 zw|3L2t=*>UGpve8u2arX6^&fGckkY+BBZsPKUC!(C(XU)UR`xGbJh1Ly zYcHDYaP7`|B$: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%0A2%PKn?u$VHg3!2pA^7YZFF#KnGW5;<^v-__83Z z*8(_AvBTdC)&hVB42NJS1w(7tl%u76*wH>390ZWJ7J2&t27vk$xFGo!a2)7>O)TJc zC1>fP%!j+`oCi%;j&hr>yx2w_E(i@5#mADH7h=HJBNsJkLC=9mmr$a%B7z*Q(#aYQ z#H5GoL=~B>=KCV(0qVp^P9_NU0UbyZM;$jh66jq+QXc{pti)1N6fP9tG>6m<3K&oko{I2P#GNAUig8zryJFl;z~urk zjzx?!0Sp9cbYvSn8~`K02G9VkIe`2;*2u+mSj1^8f*ue%C+Z}fd|QIrZz zAm5$~A?P}&zj7%VP#CTiZDJ;9MPNY( ztO1w^%D71%#r$Z756!RwI{^WLTq?>yq8%``fc)rMN1)0; zn)KUPhpQgOOn~)61^_2jEY**i6BCn< z7csmHLpIp(!9fbijYw|9$r^bT=t~8!pbW3T51)cLK_qVhHUL;=uRS32NGhnDeR?2L z>qD&ip?_l416j$#7nX&wL3?tua+DxYWw@SvUv4-)vWuww`G{a5v~uZ}24d_BlQsGW zZOFIm4gfgy;be^LOTY|CT4Du+5Ks&usp5beL%@8WJ_Qz>=mBPck%06tS(C^~P$f(% z2GP(23$PvF1rz{IU=JVyr~&lAW?((Rsvi0{16ZMtZ9ocO4^YvyBpS$vjBNx}G;D#5 z6rZ4xUOF%q2l530b07ezUH~;92TtLDF3J`5va7zx8fc!^@9 zL^`-K6IXRsSPQ~>Er8Q1JN(UHEdY4Ha0rG{FtmY96ov71^Ixj?tOCGH97y)j8jo>F8R-y=S0aAb{pic1bL2N?s0Vdv$ zIWLL8kO4MFkbE3@&jAl077n?{Ys4$qgOfEJ%BkV8niuHs5;pLM0K8cE>k|LRs4>sI zj(s5V$C?5CQw-#xDEM^QWdZg9eWeWqot)Pu#W!&f6jXnB`kI*t7HoF}y&@_4{GpGR zSPl2yQUA&hoC1^xPV*pJgy51YDe(sb!A}-M!;yLeh<|xv<Kj=xx*$`5E5e<1^B1Xtq0A7MiqR1Bpr~w&q5(BsaS%3|Y0E7sB24Z|~ zfD_OI_5h67Mh~D0Q~&|MGC)HIT-b&v3+YOPjzEopH0i$)ec&zp=7;|}U@x!+xCm=* zI3xphfH=Hr0CDVCAFlcsGZEGac=ri7sbQ)9)SQ_3_a+!#h9Mhl_~0Og>Sz#pH2--1Zq0&D=V%&+!SL|W^`bYOhso!3Cmzd6^IVtYUnCv|z z!(W@|xRoGXn8f7-3C@PzUbL;2`i6LVaa|VQ*5gS(tR1Bmmq8%z#h8KA;4+49o+} zz*XP~pn^Vb0Y1T`j~Fx)69V8-83Box1Xd8=1w=?m11StIN_sJ(k;dhraG`*NIi!B1 zfB_ZYsQ^y}+$rF$2zN!eE5hA4TwH-MEMklaU?5PXBircW02l!_fCgaAzUxQ_)9bK^ z(^v#OAciKj&<72Ogg++*6plb)5mZv8Cl1PM0Ty^|z-BiA3~=2DSG?n4C%EnesFN)G z9boN%iJM@Z0K*FO>boAv8<4jf*aX-kSps=e02^Qmhg>A@!5a49WDOfD7I@LWofNNo zd1~b_lij?S%$a%aSn}|XtL{&33LRN^d#UfMp7?rA^5$Wxc%A+oCP)lMt~gP z3g;1^yPCA*bA{A*pb&ynFo2$ha5sdzA>0k&jt_TyxZ}g!1Ui+%JYSeeAJz=OT7ZRM z9MN1l0w0J?k>-ajRG@eu>E)s@Y5X%3x%2>L`$9uL1q|4W0ejZ~OaME8I=!gVi#nB} z#HAco<%|F~z(%k{hm|M-T!0iH3aAtOK8S5-pqJ}d1P!f4Vn9*!A&TzR;jf0mYACFR z!hxG$%>;)X06%aHUKasYz!0v{KpL#mU`?Hx;GYQpL`Ve*d!CzHr(ZuweNud9@QtNoT`!-I zZ}n}T6c=D6aH4U~M~ATxblPqS+Q3JY-$IG%(B9kX$Dle^eRyT7-Ae$cKAen6u=Wx# zLz1Fc0U-nwLrCg&-~t1&)S&=#9eRKnAPq%-W+Be^ISERHNkw-W>GUkxJ-Z#?1rz{I zU=JVyr~&lAW?(%*zaIKH16ZMtZ9ocO4^YvyBzlYw8QTb+&`8M^Hd1^9!?$594&)00 z=0E^ay#Q)J4xGXPU7!Jw1hN5FU=c3efC?6&f>RkzTvy08ys^?*oMy3{S-gi?+=(!e z4)!yVUM}-v5p)ETYcQ4p`Zx&K!CwP-4Tu3X@Yjc71Pmi!m;kR$80i5WT$zdMKD^`0 zg0Nl-;55k&e=}GM03I+Lf}s=)tzlD+miA#s`)F_wK;By9?FSeD>Qmr?XZMV>XbTL7K<}E=N%4n31uJn=rXYY5Gr$9I z5cmoaXHR8Zcfh%lNE0PX{3z$aiIPy$>A<^g7a{L{5TEk^(q^l=OD2_}8SqM2BX z5sODED)Ev;tsuS&h+qp+qBXEz8dreAg#w)BklH~311iE(5uS>;Q^Z{{?uv0& zjJpZATmZ(gh;b%>fk2IpY@>$*U{ zC5VlIK13{`wr3#8cZFV(=g({Kyamtkc-CP+rT{WGBFPX*I*jDfT?nEP+aoJR7if5< z;aLFB=R!!~`^<1cTX*;2a*(>D;ZhBk%(&#mr5O{s9MFUPE!cmiGFc%?fhdNo!Egi< zJ!2q^X!9X*7ZG2yI1P^xv~Ut=p@WbG9h<{)4%uc@T~#Dx zHdXV3JEPOg+mEd(mwC)IjRtFK^~6oD`dV}-jIWBT*Do$iWcz&SX;0si+w1>5d*Z`t zkGZR7d#3PUU3#z8ghh|d*zib3Trr2y_rhTF0Nw28=DA+pTymp5^(Km?i@sUH6%X6n z84I0#+`JySc?DcLB2gmF#N!rTih-NmH_R_z_1Dw6yzYpu-t zW_Q_S<&IvEbAH&q;`=3TywTjDu(`c?@8a{M+_bcbgz?_8tolV?m9m-p^iz+G3pHm3 zU1rZc>MB0-hhof}*T>8(6E3;T8S~`iGDWwZXO;2`Y1JR+X5~v`J2Jy_#)F;p=MHEj zik3d?ZFKQYdhTa?Szl`|)4W1?o~F>-qf+oSRA?f1&MQYjXg;xQ*(6$dF(%jA_`9p; z#oFJ4_R)4@;c)YcvSf#U>n_R zN~1O1dv4E!S&x~}v((0}ICFiQpzPz8N+QBu@xTYeM!>qTt)led?r`uF<_0-cBRUf4rA@I+Ma9>`r$*IEI6SyAPVZx(&&aBHZd_L6xw*gnMjMLSr5_dFyOlN9>+I?p zRH5*wK8>u|y8TOQwncuj^~A8NX9tV9q(NwGcB52r!nkoy_>y~T>6d=l0Ex^j=a~U3 z;fnAG`ySPvaKDB@6Hi577e>a^eBt($2=X%KM-w7^3xm8Ws@xu(`>}iDNL%&rSk}jn z!^51OH-beSZ_SoS+wM8wv1@UZvGk^IsY=XD+a$+Kuf^DpoR0U$cCI`tFEiXTV?GQ}4pqo={2e5$5?yMMY4U{1m^d<}IeVdn{BxxJDTIxFwt9=8h}+ z_?doD9SpJTJw5If>Xhqbm7Mi4ZQ1&C+(XfEzf$LGpT>KaI)}&G+AGVY`*j0moRf2V z`CIaOyU6@L9lo(0YcnQB6vVfu6{~#trYbyV;#FTb{#0?sBrUtE;*h&fe3nFzThN!6 z2j>2r^?DHFQ^@&KKCxrpu!!S5#=_KG2QN1b@ie98!33-JX0z69mD(yP&&yKFJPJkF z11j3>3v(h8c-(xazIXPtKDO%#@bPie(r+RG;d7#XS<6|mGoDhp-m}@GdwZ@O{GY|s zf`p|C&3_{jMknJMZ-+Q0FU%}T=6`L_ zZph}1v)JuoIb~;F%bVR_I;W^Sy0GI~MOJa^(=8WPd|CPDk5`qKmK9&DKl}XFYsp&^ zOSui<=Xj?Kj$H3%h!HFJkot(`v=VC}-TNv})0Bf-lD%odTPOdIho|Xece8&V9;x2X z_Ik>8&8^*Go7n&Q;8a~%nj9*>aMVpaPDPU4LiL1(N95I$K2mX9=KKk?9gZUW{vGQ{ z#!LDJJKh}G)7)`sy9+Y)=}4+jgzd+-EE8YX)2-#bM#Xq?=AdhZeIPa({`unK;VGU zpvNt)Cb4vX<(~?wJ(C#~-5YfWxP9VPcj#88%BMJGC%c_{->7I}?m4r0hh+YTz-`~^ z(sJJR`v;DQp1Ol>NEu#%9SqR;}M! zKJaptwR}5mYQFz`-kaAsuD|k<-n_m^E>r(|^3BqHU9>T=+@AURgtyA|=_WRSt{C+igtc=1Uv0Tm5^M-wgS1 zE|+&@ah_1S+WWIy=#Ktv#iVwDItTJ|G&*E?ul0B|sm_Oantt?Xe3e&8e&*3`_j^%I z8#}uGcfI`L@1NCd&X<&?2l7pYuYFZCcdXiwJ=B^}*jWFQnfKbvto-D@2JL$1`G4$P zM?Vd5-@PjsFu(PznECnuwTXKrg4f&{gi?pq5+%;}+y4Dfe_lR`$9ZVVt8nMPjcK2q zA2xi|x2Z^Txc0$yu&lK4pO*Ot#$x7HU;OuE&PrVLQp+$3EYkdZsa;6iX7Q~6@3o=j zX8$uO>$AfSs_CD+xJK)#Kxv-SPyeva)ASSX90Ubh+SdDBSMd6$Iw?aef=5^Q;$&uO z#5(&dPuZ|bmw#>yyHx14RKwSh(G$`rYUU#m~093ogPl z{@MZ_)e#SzzA8s&ymJV3`aX8bxIXPtiF>)$o(h6lF4Zux4l56StqU$aOJZ|e*7w9j zMN0W-xu1`TMVh~C^XI)S>c1&$zx<0-(}qHSH#Ix0?`C^to|8{6ElR(#xztlDTzGQ# zCi&BUsB7w$nBOsI{i1WnVCojPY-UjUj^yz&?K=h*xymldvzC%|=~@;^VZP5gR}3{G zUZ=O35|C2O21B$X0$%xEA(}jjm&)Y(r^dwCB)a-GCl#1pzu6Mlde^5%ZHM;akH_Ed zt6yR+mf9)UTlp=ssBq53hGjD9mqvohn1#9P`7c&K`YIoP5B#ky)XinF!;8J&zJ2Ph zs{B!-#f)n1&w+1WrElquHa)1AP+(KAH|g%1+V6LYZE6SlnQOFoc7~KI|IV{7_531p z`KO3)y({?~KihoS>jGn^1uy&fqQ&^8((vNl72ZP2+24Oj#!D)7^hZptZNKc%JNXy+ zRPR4&&8)0m;Ddk2}f0+dC%*}i-$Ngn;;Y06X2WzHJmK&2b*iw2c z%l{9=Ks&!M@pUHqlvg6Y&g7u-`oejuMo$}ZMn>Op<&2D;*E+VN7h4$CEv-f`wJ>^U zo+>ZRYvdWd(ZcXNXO$Po%^Nj(*WkGuz1PCB_6Dj%ek(@~Wpp&wP3E;F&Ma8a(smc|*^9c~R@kmsePL z%aEK{UiH(Q;_@a7?N{<+c_Gg0OqMLKn3?b0Bf3pm8MD0bWm`|5PI~$H;EGNS-q7V| zEwpuZG&=Gkllcl>&TrNg=IED;R%aGQ4rbW$LXV?|=GpR6jU!sww!DVo@I2?17edUh zLwl~)*P9nE%+Ha%*uwBU7nc_#G&^bcTNqWO-12&YBU;(Hyau4T=xjaO1~YYe;lAN{ zzAi7ichxG6wc28<*^Rz>BQJ6{^iWh~s3PB(*O@9`m37Q3M76EzP?YE{YBB9DHLvM_xw`g;WFzzXO6F>DwG5ekF&Q7XiA?WHR$=J^3cptI4yP zdC8sOd2TZ=pflu%c;GN6YK7U|*dJ z%?rGY9-0x&i>@-Ijx;Y(GCa?e=H*3375UP`<&zIk!+;P<>pK&{rQ&48cDzWPtUxeAzxIaYS_cembB!UEiBVJ+5M*;oE*)$aq%ooMd1vh8R8 z@f!e33f&u4*8U5AsKrwzEYH4rDZ2q^-jC)^EBnglzVj0$G>@WrT-&IFSZig!{e@|t z)hF%)OB<~0?$YbMTHFejwp-Z;{^6Hy(c*rv^stqE>QBD(m==$Lr4v^6%9bb20C!-P zmHpJ;9+(R>H>0`D$`1e3@1FyRgCGuDSvuveuWIoWh-a$evbQeTI}KQdE-Q0)K0X&%iGC|PdH8eJ0nG={+-qgu{OPshfaa@cUb3=7H+}3U zwBi>)oVBvYzW3EX(u!{fOEazPmp(q?ClsgMW@Vq7@%`Tgn%mIaVP#{U-(1qCo&oWk zmAyXg=I6BdtjXPE%x=~m{9`PuumxD71ru(IhNn(~kq z9|cQ?tgQB~4?M3`#U^c)Em${Ub_vaA(0on@d^3n`R`&4?9~!SW-34NqmA&_4kH4zL z6(D-7Y}Mtjt<@SIHz}8F{)DOfwD^?HsW#vH2yi`aw6evYS@x|GnlGXGvR1wm#BM7K zx4!=KdjB}EbiI|$`QGU-0@HB2mCYLcuFsaR2zOanhvr5rd*{fnoGGDs8qG6W!6pz} zt?aiyFkF`dp4I`fvfughzst1v5{Q?rtm_N?OSJl>U}=SweR=%E4{32dSlVP|)0chv zD?o@bR@UBS?HPvVb7;P(m2b-vYAn47md;t3^Yr_U0?pUa%&hEFhu^wEYnf~^TUpw5 zX;}%)V`!exM;jpSv$6^AS@I#^2Ha$2fAPM*Z3kvxwv~OY=bt`P!eT77(A0W=Sz`Gl1{^(SBZHT~Omu(Zp{{^;w|{(TAa zvB<(2th2JWPVReLYZ?#Y1}l5dgj@EMKrGU^*Rj~6`^w60J#+aEt)&E(Mq1f#UFhrp zdQi8r4_{jP*IGdrh+Zpu_u;p1(c%f69xJQeyiIqi{#RcEUmP%ws-&a%Ox}qqIp=G?lJkm>@S-8rCJSt=kndL20z5WDnEgER%N4_J4cjA80#wfTdYhR^R-#zF}ydMDw)PxLy~Q{_+rr zN385Uzdmqy7@E(c`I26}Ul*?S=OwW8vX$NXjkeQT_wCx8l|BBQzISQe>%h`REBmp& zquN=}2NGgcP9YyS6vNtkM7U-^wOuK>*pXkN6kU)u4l zCv@&Y5M#9G^K~Mv?9Am?KB%?L(MwkLzVaV_MT>{E0V_*>-ro+mh^=fwa?5X(&^(3a zv-Kl(t+)NulK(Q;+&QBeBjlOXmK%!rB?RFg}uUi7n*yl?7cU>YngWRS+MlH zm0i5=Jx>7baILJ8I2TKphlLgzx>Nq~)Q~|2C>h|-nsno0WIzY z@t~D`fBL~+)#7mwC#~$)PJe18P{jl*`!63u05l&#^D!&?@E4~Uq%GQ7SZ_Fd*d0k6Z_Vz>b{sb5e-^y+IhJR*2ib zp8fWZ0L`svZnv^%Ej|X8j#$|zkNrp+6Sa5%EFHA6_K(iDwD<&AdeX|aO+Ndv67Iq> z3(bvaZn3gIoVwa6q4_A9ht#ZRhdbln;EBlFG{gUY_tyLGm%G{`PwiX{&hsetIe{=Orp!qPGk6PJ@nQK1| zG|!{?nw4F&_tmr-S6wA5>weEK8}(D&3RYHm_lHht@uV48%Z~li7tCYsWeH(rKX=E+ zCu<{{)JL_l4R8C8Z!h6aEU~a2%}rMJrN!eGl+Zk9#u$kQWR;b@w#!{)u3!+hUY`hJ zvX#BG^1SIikJN=`Wyz8Q^0>5ET^uV5zxTd2EpE|6$I6y|@`E4K;>%#^f|Y&j!dK6f zumqhJnw!wvYGq&B`_c74^AehGSlO79!*#4mv2I~2`|8An1Hg@#WM!)_Jv$4SrKjhT zPpnecb|0D#S=o2Lw);yZG|!;~&wB1eA31y<_?_BXuawfjDnv8~*7J z%!Rx|H=vdM=TCfK#7K-(`~{v<`+HiPr0%+vP5Os- zn6B_4^`@=t`4_&sNQ)D|(nKr!-9Mc3HZ4vxT*}HFhsRoQe)yls3 z_0kA^rV8rW{Piz=?N_vTgC22Kc6e;}=e2l9ePSzn-}-M()8bh(f}MTjcTddK;zf0l ztt`IdxU-DW=aS>1M+tEZZk{o&7l+C2E4 z)Fq?K)(19XFYqdnZp&W*L9#R4!jwRlj?wX)$8 zPpl}Rc>>L+wFmdQLyeynW9l!oa{^(g$dvP&;)>ek{T=355YfBNOe z3~i&;C@Xu={{8|akr_9$hh;{0aTG>yY_z$MJ`=x}H9r(=0 zz7E{3e)xTj@2MlY8_j)I_T}u(_F-tgfaY0!c)Plo+Ui*|{Ga{Nd%mqcJC>O*U1X)D z>t^U|9m~Hk!ICQyt?Wx(zx{roxdqMpt?byGkA7L78KXC>k z;wf5uMHju5E$n`;=_S1e;&m%~>Ce8Xhev6Qu1PCv`_}I70K@fky71$_^@}Ae#9|Bg zp}E1z{^O6`{wbjOGMX2x?9~@Hnkams`rB5v=;z;OdTn>96KrMs+J4c~;!$hVCf+puICgpTiI7{ zp49+HOHut*u z%1JhL^3T4acHO3k&B_M8zssaARwyL2vYFpq@D9M%J$PgH#h=pWj;JeUWpgWkZ~C}1 z!O|Qn>-x@?KhxqIU1C=DiFf>$1zLQ>d}Ac*o%Cy6T6_{LJ!NGdJ@lTx(5l+hv$nEB zvnJ}9QcBI2U9vTw{XHYbL*}a|*@H(ur`WS}LyCh65A-m{`z+943)udP*jrVjg8i zWOI+0B$&xbn4Fz?x|^uOaA!`J3DHb?z~u7Gm1*)Zru$-&7^cH$x)kR5YcgS`mu!L+ z(>F9JF_RuJomA6F{C}|epZePabIt8%Vjq(kGigFYvWcQig3&~uCiP}+e-lxg=*9F| zOiXQV3)4Y0eLeGRHgTWn>zb56?KGY?~v z8!~~3c~F@rvbmG5U9%oZ=7DH>`6gI04;7Q7G5Hb`T$^W=$<&zqj8VWOC(P4x$X+Qt zH1?TvRDN`OKJe;COjgnax+Xd|jGIWpT(Q!JZ|bde}VvO!mQaAI-zc zSUO}IdTy9!stG<#654cGO$W#1W(;d4|7Y?*CfGEODATJj*$k6qGksDMh?sn+NnjhQ zP43uaj7_v|(hw%KVp7p2pJP&{rW0lQ>n46N&#-(a2R^golIg>l2bt+znS_Fo+Tg`o*RIdQU@c7d@lRxQNLJm0$z+*Kvd`pJ z%#e~vubU)~NwS!Zlu4eN#Jw41%lnM^P)VLzXpJzm@;R-Tzcu33>dCeeiLHUA*0@_M z%4wyhTjO@Ev~FuutJSw|C9GTN!B$$JmAG#8K3kcfR$8SM2)EL#gUO{R<|~mHBTC3ATn@TK(77s7WiLX$1?d#C|Kk&`M{whLBq0T&)CBD~-__0cvIZ zTTw$R;n*6xYGtZhDVw}|-by&N5=N~od~4LNm6>b>-mP>?Yb>c1nh&P_27BFuad{CQ z775LwUs`0fhGvP1L~xO1%|p~8cQrJ`FUAJ)(Z3?0RRlIgBBerpNi_o=* z(~CZSk-jQ2)J6KY2wscuxyTC^Db^w!ErxiC5tkxGUqrpdxKA*ILh*uFex03F~2yQWAQzS2o!IvWV zFUDVrBw7)+7m;N#<~B6vmq*k^#-T{h7en<$a<51S6{A^2Fj$P77NclG()C54U*u<6 zV?)KbWs&JCQoO}zbMX~{A|NhC>5K9CVtk|+{3!;tiZ22bBWlIKb&<|0vZqBxxyURG z$%(dxqKYhAk!~pRV8!rH5nC4{$i>)kk)0_9X^R9+G2B}uI*T-Bk&P}gt3^tv7!4`% zC`J0K$P*PA=OV-|k|V`fQt|zOB9B*m-=P@VDKe$S$W}47Tx3m)d})z%Ek^N+uXhw* zohSwyi}XX02Q9v`QH&!O13g8~p%_ss(xpZIp~yKGgE~d#t4QG%LtsTlx)_ry^8H2n zrx-0MzUfd5jTR&7Mb@Oq2o{4n#rS&hwVYxQsTct(GTOyJXEBaad>5w}`7XX;QG5@f z7_TZuLW&%0FyrYVL& ziv)G?y{Y000mYDFG4fuFlN8ChV%WLJ+!x<-DZbfIDG_v=g*F92xV`n;X+wVHeE!hi=v#7i0gC=sVG?ryTg8I3 zJ6Ea&HTV2rxuE8wgOz}u9;_GiY^z?-3q$G!ogIAFYeVV+_{)~jmK zLC1>EoOqL>L8pp!pC2q5ba6=0pv%`(@@c#3dTdLJtG(^UqUg?)A1oR)y;U@*_$}(T z#n%7L-uu8;bySP~v(Eo>5<&<8LP*0Qgb+dqA%p-Sgb-*&L_|c|B62xNPDp4tCp}3Z zAVox^6cLdkQlyBLQi_O3DVHK5A|jfeBv-dus>ifO- z?tSmw^7&MAlC$T}nl)?I%%1gK;0#l}t||iv69aV3Zr?yxvq{XLZ?86M)Nl~jj`s5z zU3+A_wd=Hs@OGUui9webcxADGuIsv}yLP#{9rxmUxVxIfpb{G~sKQPRnq(&iRmBm5 zniVlfgPq)~tfZcG_Q$uxk#^jBP1er|&H6cOE#abO{oG)XVA*&By5BQFIf?h`rKhW6 zz<5e5*cVT^)GW^g)oRaVJ7{Mb1COy$2lO<^aqukAIHAQ1d}`|s=vl6V+C3WtNv1wV z-`c{!EjIIjo?Q%*@7WSU1YpBOxTW>+h9Asc-T&wiY)gP*uGd2|HTI^f!@C9R=AOGww2Da!1`giQb6B2 z18(HoWb3N$8EaR4dvsTQM|4+xCv9EzozrZh>uLymx8)G{?pokRek<+9pTxisZZ*(8 zUU}F7=+Cl#r$`Quzry<42SjJZAqb#ECiL&<-@I;{Sq=CJzDT1U%&Ic~HB`-hg3fKPLElB`^vu68_6cATOI zN=%Z^1p8N~D)>&Fo#Ycybe_fP3(GW}XQig|tc|1dY*cifT{b$;-Z*kj;HVw^5xAfj zErDA~2?8J5=sZl(d6G4qr>l+5lNnFvDNuBtvG=6&jMsFY*)}?lhPfpyk#wFF@pPV5 zHagE1P3PGYPv4&%iJ6MnQ>=+S zrR|A5m5SIi-Ae4K(!`!dP3&p45_=Zgi9M_0i9MTb#Gbuz#Ga$7Y3JJ$dlD~Mi9Lxt zj@Xl^i9JD0?8&tedkPh?r=lIP=K%%UnGsLysj(1y6qc^TGCQ$nrAh2jESC=3G_hx= zN$lCH3OHmV_NZQWIBO&Jbhyxt*wf*rjo9O}5_{4#u_s3ndkQqMr`SsDDO1Fr@o~7F zNj73njUx6m+lf63ti+y`aYbxU#Gaidv1d;lvFBhsvFEgU=$f6_lXTNY>~Sb!Pl_h? zq-kPLP!oF;d$VJq=INAZVo$mH;{zry$8Oy^PBn==b&A*%vk`k*RUz7I>$s{Nv1gqH z>!XN0d#uEsgJuitn8J={ZN#3A*HlS&kthlh{+Jh&`nyv8SRPv1h!(=TupV zJ#~uMGglLP7FvltOKmMqUK3|zlD8;Bk-T5iYLXAf(`u4WsLHSCYOY(Wv7?MSvAAkF zrC2o0Pofg?!v0NGNP8&=N zQKx6f^J^5X+Gzn1b=qeI5goOcbS92#)aj~?OvELbC`E5EQ&MbXqLg$CnJ6V+$vma> z9%Q1F%64R;l$mj4qLc-)_bIDwWTKRH7BW%FHanRpMclTVAdxO2{;(vo^2;Ev(<+RE zjI&emQ7U#;hU%+md40oAQcDy&K*lwz$XJOIIJo9J!@R~#Zyjx{@3ZPVi|U%4d3i_z zOBA9;LIh+er-A2DVRe!wqe4q1E<(X&Bp+i^=(SH;m=r29Oh*1H+=m1c8Ij)NTDPPO zDI|u34;X=*0{8Rq4d+#m_n>vOxr8~)C@mdm2~boq%`&V~;eI6j=aPH(F$;3!x!6c9 zW3j(gd+u!u4M5=-^bK7Z@H=As``@?FeSWg@)6asu6wUv#?S)lf5;}}{a&b{zf{G-y z$N)VVR0bU+`{HAt{H}!+qUbL&+Ppmb`A=Em7ytUy#o->~et%qizvW(~d0R||i7HN$ z4E5#Zoq`~5jw>dU5xAbU`X`^!i~vQ5kb#~GNF{^R`xHI>0f@i@i5?IErswbP{-Tbg zHrPPwKDCB&=u6Nt4p;>`N~n*DBA2o0DzZ@qa2wa6Lts_BIsIPIUS#t^`JfdV^#R4-3R0Z$KkX99yDMQm0#!8ZW6oo)W zz<=iXg#DIaOBHFZ?|&ONl_f7pkZfeEtr2Uj@J14+pu)Lj%<-U4cY4bbx@m-+3J%QT zcU}pAzQC+GFvc}z%|MZ{$_DH}fLavXLBc6y$i9juSGX#P5Kw3i8J?vv z?u5Vd$G^P*GERaIe5A`9jkHiyBpLIo=qD0CG4o(>h6NKgeqOI)Gsso4LPVH~1kt=T z?L;Z)Tb6v#TMvKF0z3Ib+cRY#<1;iq#kf?jAyeuux?)!-+<}a`RbjRgD|YF2YseBi zZ@@ZK9E2ut$Qb4MU&A1n3^>K5kzk@P!qekS^ook?m$(hZN0X8L3XmnSK&hR7a7p6B z6fsVQA1mybL`x{}g~W~-Sb@p#kV(CgaqlXOThj1U6te_G7?ISL0P!arc@B_aDVhi& z1ItxJy(Bjn+!jGPGRPJJM`pld1pG@v`V0cq^L6hIv>@LOeRbh&3r}cs_Bv*v0&Kg> zXz&&I?7hbuzat~s6{<$E{uE3?f`=65M&b%??V0$rMyx5)kVJYZNRY%(Da3=sP#LfZ zfgXHf!_++%0-1^mmxurr+^@q5B#b~2)g-4xvI!L5NwKCRf5#w`SuSHspu)e-NoF8} zgLFu_B`|Y|sa{G!szqZe(nF^5~*g&7qQvFau=pmBO`MEHE1dA&?v*gPbSO zhF^Vn+OuE+6BT;O2yoB<0tq28pdEtqV$c!C!9>QFUf=j0fr7*K9h2lc*w>bP&E|(Mi;VV&1*@-BDkW)R0ZT ze(tme8Yte6Bqu3`n52CvqKX7?DAbfB!TqqLrK5%aqBxZjts~iH1_w()*d)nKA@?Mt zPLd@JhUMIZFP_zGDg)7`!n7oQM@3}u+0U&MWC0tU&q4}PoD)epRQMW6L{q>jiA_@c z5Xs1iE$bBp8S%z%l)w8^FcFN3fg({O3YjShb9XZ*R%j}c!MhZYOV{rfxj;tZY22KS zKM-2OD{B8hQh*If=-m|h0dMpG*lU`59W zNx+~&y-0+Y0!c}3R&m|&w+C`KrOUB;qMjnuG!==#C^RD(Z~{*%}l4tDuWQEVOolbD4B!`7bYp78*hD#6KtOvQd*Y~ub&U#suEav2IX-0Z!(c-46!cfZ1{EDsQVtEStw2yJ(4fTU zDgL3P(HY!60clRFd#?&)*ff84kn2GWhEwz{2~aIw`o1`TkTA^OUJZ#&w*)RLz_7#$ z8f-Sf2sHRif+F|Hu3cz@C!)Uvn*(GJHy^a^&&xH$RMFfdj7kC8BvH|T{|I)Y<1gR7 z4l>A_!-#JLKn8I0G2`AyGbv|^s;2{$B?eUy{3K9J@s=fWRgoMeYRy3Sib#ElgHl9l z6<-H)gyxFV7vZW1pI96vh*eFPUvb7^;9O`a5}S#Sxj_F(Tp*gp!m|+;Px#zIDaApY z*td(b65*i=4^tc_i4|5jK4N42KSq@?hx;cxKmDxGQVTMMc5-3|Ar^dLy~HV%*tCmT zAvO)dofM}}Vv34AlQ0`%8!HZNgvyHzvT&P(9T8iH|F>{LFFy9k?}}}z*qjQZBu+|& zp%kZ>VlQaGhG@eod{MEl5&I*tYyBT%51AuKUWhngvH*U_sTL=s|A)eS%+}9&|Iw4; zDEnig&;NZyA9ENlxpyBEd&YaxcFga+HSI*H*s_SFTD%9a7#{N5#1XAH^E3K3=&gsp zC-wzm?=DXNgg0Ygg37a>|CBgW6Nj(=KUqPvNPZ0X`55r?G2rK8zz_Qv@bfX?=VQRn z$AF)Y0Y4uDem(~L{Lce^KK}QA)IS;jTU1b3P!|r>gk%33FhKu+|1+p?a6JE~sAR~X zkK=#;Gya*BnfdeQ&o_V>0xzR}9i0~m#}pr^CfE|G3CFAf{E8eT?r%mT!J?o63{tLWG5aJ`^S$Y2#KSEMN%w$)o3 zHQFIU8*1yTgAJj`td`KMaBx<1Za5a9E||eN2DC&(CTN;LTu=V1b@StY|NH;7xBvg^ zF%bX2{+A3MR1$Chhn5Wa*#7@#{QGage@J+T7 z+9Hk_MNDVgaT!R~c3J@;&a0S^w#y7$w33P=K##GUinBfup+u4im@BJsY^SIYLCtN* zv(#@eAgBT2oXdzRh4BnL#_DSvTS3S;e{zwejDsfw(-6^xHronaC__kKr4EUKH7XATT+~YfBV3Ht1gd)bqOa!;mHq3bHtP%Yqe!gIh(sf*miz{`gob=A5c(g;R zfU$KXM}?D5-YWE{o3x%>YJ;zbE}ZF%? zO+E6`_FsX#YuV-8FFv8ute)Oh_X&OX^CwHb@w~W`G^_t_zAW!GRUZ1;&p=-AYItR1 zqt4uY>y0nv>!_1h-j6%#t6zD&)^?ZIfsUn|*a70b5&!k`Q?fwajoSYCaJjsW+c@d? zAsGmefB#pe>qKNDq5!rt!BJB$~0BIs#E$unI+-MH-g@XFybllt(?6}RPm zwBI{-IdoiD+2%bYB)n@xP}^0z@<*P5O0yP+?&AE{k1 zQr;f_uU^q`#pKLuPyw^ zNAyjrU*4Gas=V_0@88B>%Uj=`S1z8Ew+GYjk6o76s+<1sQj)xVTXuK;=jGMZ-@mkL zxsJ>!zW(*cK)m4ON4?Lyrms9Ze)zNdWmdN7OW%85Mo7H$==uf5Em@c*CXtYws8VRI znA@USAxBY)5Tj_eC`l-jn0KNyp-@6AgfS3iKumKn(qayXehOU{S}bP15U}N0>Yh++ zAqiofgnkG)i#a34K}<)>i*uU5Vj78|5wl3lWHI4{VG{xpc1`GjP;oKEgrbSD6Qd?( zx6n8-azf$6BoO){6jazLVP=Gt5>rPQ3!(19&N@ zvqKm+VW)(B5~?VysIVz z<3AP`eZ2qwPx<$EPH|_4A}yiV{9sXGVe#KTUTqG?<{0ORmP5?iXiOaVDomXwkK3@| zgMu~T+WLrmMm56#6we8V3|8#_MERj6qXOfgw<%n0JUl4aTo;SB%&H3x*BDvFe3Pw+ z#^Re1e<&V}1;deM0c37zu8YR%7nm^e`0ol-eSK4Kme`L)X4y_|8^W_f4XUiTN?RgU zM0K@zR@EA*5%7J3<1T(#PoqIq2_#3fAt-sj^mBvJx@b)N(m3rkcvAY?^7K8_tg29F z%90vnGIz~U(+^XVn}YR{MLaJYQ*h{L%;ZBiM-9xf#LLHw0SOAGcO*2+LV2#PQ;kr> z<*NB6aoNUDrV8o}K{wUaH`4R9^|j6OgN@->H5E9dFsM+(Qj*b@=BDOQM4*|Q>OwK& z48Fd;K3Elwglp>sx!P8uwMFxzE%f#I(H5JMgd!GHGJU}|u;JEltlEG*TaV#|MkvLp zx6A<}LxD&V5T_H3v2blT77N!He-)VJ&?8i^73uj8u-f`I1-M9=o{@bA5=#p(#$o{^D+xUxTZc7Y@Xj3ZVJwe z#vX|mx%eG5Ow4ql6BZD-Y%D3IMJ9rw9hw`eZxFAR+P^6ZH3@yG4$1Kk-7D#XVK;Ps zg?b^bP;)bxVa<&f%?m|>;nvWc#s)j0US+jtYz#+g>RW?V;fClu1J567uAgf>+!Q~8 zqQrmLMb$imx@`<)9Qa>1uExkJP*-1Vyx|Mi)Hg?CG|yt;x%KLuR3xh2M}-?gRneII zV5Y0EX*e1Ga1%}OP*X4zoL3iZAbr)0?|Y5gN7^(sEFU2n(mR#8!uY-DeJpI?#lwxE zn1RNpe$$i;$HEQsgAMhOM+AGmJ|YHLykb?j|44H)hx}x2oW?50}qg!d}Z8Mp_sKHxrYN?VrT$-ci zGFvMai0X!xCL@!G*`kkFOrv5g`1{t@vLutS6BW0I*TVp zt(IP`+D-GDn#0n&s&8s)36m+V7K@4clNd`QZwD>W%8O_xOxu83q)30Ssc))oX%dT{ zsA-NcPf{z5v2JM&hFeuX?VVEER2_{pHP%N@d zoBNN@qfnEOWyn6KED%EL{L;Ns)8y|cpG`FL>Y{Vv>!=Gg1*^j0NH7+z4hw5pHQ!!C zQ%jX34>nL$)zMgE)L7BTNwZFpBEyBSU)#`LM2lgqrJ~)_la9B4;ZSv*O$s!ptL7Wk z2pd5DPl&vj#&C0sVPzX?=GE7PgAvldRxy|7(x+f`T_`py9E?ThhZ@MCjD^F&`dBPH zH)>qCXy4@Ks3jj#V~p(TJ#2EcR_1qTm`}>p*btg;{-QA)GrSV<3uCRd?hL|qqi3Yb zh_B%z7rnuzz#Bu&=8ob20%^-TH=(9rQ?r^RMh*?>h{tuPIZB>oI9L~&8#eqIBO^&I zc{0&pQ@EkQ_@DYYjnP=Mkvm0CM#IgMPLFaZ$m1k=>w#%(Y!FvqA`SBm!I8<3?GQ*( zB`eVb+QqfRNlY84NVq!O)D()C4!gBfm-#*6T!ltr)}lh)P_erQBRUOFE{rLiA@Ni;!VNOrswPR>z`E zO@=g#yFxAGIT)YCt*D03yr!1=X4(&iXBl=?CN)t@b({tIyC#L~tkKja%O=hu^7*O@ zO3E^0SJ?1F>{?{*ailUUPuMaGYSl1WoNt;wG$z7PmTOE6eU$p6g(q*6)dY*+iWWGD{Eb7Fy=~-JErDMg1rnV$WeGni&rNy<~RAcUqd7gR`hv z)C$8Pj)kl18|y`Ov47MH>*YC%>_sj|@b6^${9jW0zuB z6RmYr5^eQng=Jzo@w705W-T(`p3bah?)K#Ew|k6%EZZ`Cq$cJHBGs-r7ODx8b6_mh z&89l2=IX6UT)kR)rXymQBlVzYLrqk;4UOTLkuoqUbv~xkHno>LX&u=CU4u?&7t&k3 zaBH)1GcweqOCxX4rqOzi{ZsO>rP~VyW8qMZUUl`M(mezupz7k(OqvEU$jm>R;NCUVpDIgL3P8XLquNw()6QnOyVv`FxtwOI_FO$FMeHLSIydTWSAW?6D0 zw)040X5*AcVxB`t7qor9|4|M>CD{1XfrdjXwpV)QTL;KUsui>5-U>BXHa4`}3bi!G zO3d~7_q=)=s;M!Kx}x(U_tX~I?SFE{MkBMD$a#qh2b^}UH8tT#P0JkRHrYnjc8FlN znnF}+ZyIX2w<3!Z1(9ix*0flQp&UkUS9oxNN1hX{1>7 z>M99YDQ0+z|}rrm!(#EVEP=Z7i0? z=~J`iXmM67Z0M>|YT?3%Tf@~Y%HuP89ShG2#f-D?cxSdLSYj@yO>qZh(Xg?DHugi7 zl$y=apgQCi56k?VmVzX^K4^S0(lm-puf{jyj}**}i@vyDD;geZiblkeCI+absX00) z6q~QI4cCO5s$=z4_T#U1hh9qc)z_q1y_Z!TYE7<7n|tGi-TeQFumasjIVYR!jcd0* z)L*lu=QOnPe^;!5xff9dn)mBu-pY?Izq!7-!Pu+Xb_?-YP04)yqok%{D=gkwG=v+- zt#4|KMw;qXM$($Fv1icJs-ILFtb|bxZ?iJ%YIe+ zY>HOb>%Ed;HRTH(;j38R=-gv_Gt4S4TRKr|URIGNTRa?z$^#{tfz)yk|Iv9x@BMP@ zA1kJ@jy45r!VUFR!d($9nMHffhWWaN=3uDWNTYi1=on$GY?hg});eRX-1~`PYM1u) zUa4eN{#Z*&*^f*hE48?~E<^$d$8=g%vE&&qqT1PP5>s5NRg$n88H)|Gc2&)=G z-BU?uXGNnm^XePqONgfCXk%k&ma%BIi1ebN`i7R6Vb?3v%aV$>szII;G^xbr zGF!b&Z$X+Z{TGXJsHDK;&TDxHL0Jpb9wW^<@~Dk72g7reN5C?lu<^1uD#RtNmeD}c zB-??pxwp5DrBO`7e07>6CZRD0Mel2AOIRC@7zvE)q7C<0I_g5PIStytM&{e>P<2Zz zrVsb!!CR=QDI60UDtSKDPQ|L`%SBi_rKY&FC;AWdh?-5hc@J0XzxsyE|J(np@n8QX z`F{ru89L-1`G1ELetiGyKjYtji}8_j9!{%#x7OK$fZVW+)^t8x3FpA`mB+&cbz@@rfri9g=yQPBD@YTNVM+8 z1#ZfC_+ork)3&QJg)U=|V7W?X#D7^rrZUW=ZII8vH76X380;e%vsJ0vDnh=DP#TyT zpT>8Firj%_mCyrq8F5`O+yKTHIeI;23g1KqK6Q|1kY9Ximt_QbDN#5|wJ019=m@PzahZZ2(t&^i zVPb@ewKDJ+Z)yw{3aroQDiGlh+ghoyVm7oEKGy*%kfe z>t-;Q^ELyIaf#80@-j&M& zmO;RrYk;msTgA&5xW&~FK-W43P;W~Uyp@4lT#p2F?X;vVbnTZtbse+iFLa%hVTi6v z4BX=744~_(F7Gx2zrILmNR0WOpOnL6P>JKYOh3f&`tLYo+5~Yz!*l59a25xah6VSbjfmdGC1au!@;22V9 zpK9H{EJByxEqV>fU0VWZYY6 zw%$8Ix8D1Z&R<-`z^^Z$0(xhg&n{Ba$-A6^$GDOT=v~dAgYBdm(7VZ=kI}ndrOx!8 z)CrBxT2p6wFRE0=H|;5h-?MbY=h4aGeW~r!c>6M~-z`;E%s0-+VQ5}(1@ui48rncg z}=uegk>(4c!v;75@e)@}aBm5P1sr$z(srzRrsrzfKQunI_ z(*8xl2pCsw0sTuYBJr=&Qul9R;MXs`0R21LkAnZO61@K+g9OWUTtNR-RUew`Nx&rx zYE69zFpx}c%KS!=jhD>&ylCtZz<^~<4;1O42xzk&7-xQWl1fTEO|RO<%n!`88o()r!Wjl`aXA?< z;i_0-)!7kX!fgg_ae@Sx=rad9F;x$CVo;|8&eN?-EV2%1qKf2CoM0K!#K|hEY}Ok5W zpg>>09j3Oo(jBTq>GCcrV24FgH#@8`RItN((a0zbe20xvA3AJf;8PckW%6O8pB)aV z)WpZ*W?qL=s^1;7XVc-P%@gZzN2Lt*=`}4W$vSnC(&CgRDc4k*q;gYflEy1*mo$}u zS567Qq#2g)NA#GBY{SMiZ@{Fb4E*}CH(=5lJ+?_(RSw}@wET(d-+)Pb8MqqiBaZ+k z9Z_F6$-q@5@Cr$1827!(tE;dP}ju)leo&ro1zl{r)f!d3@VN4 zm=`CMjzwlCJB|}uDsdtU*l~(A1-G!(9cxUfbzG#UTE`VS`|(<{&W)-?TjNHz;|}|} z)A6uw@>!d%bi8QUj$_ALDgiLlnRz|By5uC)#pE>An&e=6B~H$jLQft`*0@G*WkuFu zl5+A4J=DoHX895ITx*;JlNXs1OkQi1VDe_Oq+QZbB=2X?!S;R{F!>NEk$OuFn0!Gs z<(6%$ntVr1i%vczLPIv4lBFfb}4*(0Tua@d+xIpvt}I4xJ)0aMP=ZbR7Fl=q~-Qc!iO zv&*I!ofBoboikMe{m$78I>rSK0Cvu|Prc3)l*`w7rZR$^>#Qzc=SEq1=f&+C*?E~9 zw9e1Ob-VL623{F%0ND8;DTk3eyz@!RTHE=&_7yu{v+kNZ-)7*I5eI;&4mG(`Qy92K zU;Z~AjvS^hETL4U*Ce=H2wiI7#qwG)WT#FK>E|i0t zx*-aZ+L+=1n_p35!tuwxtzFnokQK1lksoLCkap<|+ zC7?#EODZk?t$HlGWV9ckE`?eOT_#8i)Mcu%zcE860J}^VJG9s%B$qC&QXE~D*|+pv zR*HF|LMi~eY?fxH%U&xAsmlQ=*e)k@4^G=>ZI|=rtnG4B`Vn33Djn>~43DC#uiYBd zHPP55s8>#aU31J;scX4jmAa0%jZxPrO3qzt)eP<$i8E(i)uBb#m8LoCy2fJ8x^9vx z(shSLMY`^m!`oH3adou*cRecmaH@SD&MwWKmfZb2ftg2 ztsmXeR6n}qF-Wuqh5&XeVvuOP%MI9#d|Ts69$>dxH6Y!ZwMuncXz;VT(FRr=8US`% zAv@G(j6ey%wDAl)Mz{oE+C#EQX|?KunAXa` zuY)N7(^d*?i~y!>Rx_E}szyV||0Y;*S<>2Qm?>VVzz$)p$|Fo4}F;vA9glT1gXd#w~`_n27zj7S*3 z?ya;FF|MNmc3)xt;|AmG%~;dA?_dxxLuLTGA5i1h{j_1kX;0EiEQ>^XnVgjA<1HJq^htX4O`pL41_P$onF^fVAO(=# z%D`h>1qV#GamUhETD3QQqZE7k4%M0T-KO@Y@7KLK8mGPKCskcnwDzW7x7GBX*4`ej zcX5(cJ1x4RBCUJ^%m{z zvB|2vJ$7jA?QtMpdwU!;`GVNvqBPz;ZqSf58%C$cZ8?w`OiLugL(*0U(;3N@VaW(u zg_WU5dl|x*haREdjb-4f3sL(sCh8NFjEB@qr;I9EPikvvJ;|7@o}VjxC|VaX7MedU zW8g6YlmIhUt5MC^WMAwuwi@1r2w4Km*sqi#Z=)aZ&4dhHdOK-ecgE zVNHPdxfr;j5t?rIrOI~Rmq}_?CmejlwgOK~jMC?NNmv6O9YkQ&}h5EoJweX8_c? z_ZYZE2o>OciY=I_4xTduqFFkq3NTZhQfB6w)X3v%{$-vLa+2{-fSKnk0uRPS<>oa~ zz~ExhC-SI*8pRvf+wUN1uv)}4_-FUX@l3bJqzBk&hlPNZ2@{E zYoYcsk6?ObnNDgi+J)(GHo#uPqz&ygK~9QZla$Jtw$U3JzjjXG-Q|zo;dZ>FllsJ1Q ziY-W(ewnJo(L0lYUq>$i_Rfj(A$pe@GpzR{T@G2iIW1&3dQWHInk7WmTW?-^H=4(_ zy;~Us%+No;-b>|(^xmMGuvw2t?`^W5y?4irNbh~-i1e0|zV~@e_w9XA6?;w3gWfl- z^PsnO`m%itpb9WMS@kG8Rre@6!+d+4ouj&xUBtkr!VCekOU<`#*%Rcv%zj8b86#Ve zJwv>Gl;MYf*|QmVWvmZiHaUAT6cI3cl^(0?O}a(f7SmA*la z-uvWSGW&GRe7Q!C-!`UFUzg>jTwk%*4YxJ}_U)?R-PkuM7lXcey0v`^Wo!GEs`I_R z740rk^&O{IzP?qe&wU%>deyht>{Z_tTAqE^DP8KjIZl`QZqrKM_n`F!N#A3#SLejZ zg%L~&*!P0A7kzK4hJUDp)EDCwE7xNk!|V%5zY3bp4aQrbep4*6 z>Q`lqPQONrp!zjyo$I$m%dFqZc-nZsby5ra?bN;Ow@0<+pl;2PxL)=>%Yismj zW{+2*qmYi*Q<+c?#Z>-#Q%mU1ts(pgIDpgxvy#D7|^gnNf zs&bvBsd*a>!R2kYo|WY7wBKdOJ1F-yd8c(<=i=+SWU1?p>_LBhnBCvUz^6ie0sALw z5e{n@mgaZTi*C&1bo8^Tq!!ziwPP6;3 z(%)EX(UATdwOaJwuJ()l_bBhS{{iv-$`V8j*#C&_6-)n1dW+ltw(cvJGc4bwW>|iJ z96Axg2$-+mTjtvi1M+i~UCJM(+|>LEDz&BtFn^rBXq8WnQ*>SgFn?OSg~_k7iY+OIF{pIm`!5J{wHZKND*DW6q(P}k-+(a|t0&u{R zcu@^lZxz*mUF7A98w7v@4w1%FwI>Y|Z3IvQ4mczGIp7)tzYeAb9H1wCf$fB|AfSX< zkgf)`AWIEuL7pDef+97j1!Z)l~>KV1YX5Eod;GjA z3fTb`Y>N|8!9F>j1t-PjN*TBfSa8PHscYt8VS)C72fE@M{eg*UDh+Fh9* zSR^YQIHBD_KX9^~!UN5|4!lQS2dbB|1NHl=X^?8kJ4 zb@C>falBTzP_F@n+P)R8j~j!+&6cyuLbVkvJfinBg~zQUTS$KY5WvDK#)-JRX9-w% zO*IvbcWP$vE#M%RB@M}-pwjq31>*Lm3T6i!RKmb58Ur|Jg1vWx9#X?KsG;4-%b@1= zR|y8KvY%iL+O9@^&_Vl^fk8)Qw+Cteb&%=54!R~+w?Vgc!#|WWtVr)^iafSgQ$%O=42^)KxsqKv?M0~YC9NP|7(Z&XETmJCiZOl6(;&fs);icc~foM%d6 z@GvnXGHf4kusYlt{E%h+8$3f-(O@=oaI@_H;KizAgO`bL2qVBBaIh_Z%;3$m6scf; zz`?uJ=nX!itli)f`Y3(y83sND0RS9)QBR)1_M0bz?}~#Vi3I>GPSnPuIM`nGi}M&j z31IO93<749KVb2AJ&eUOtqm`p&A?R?r72vzKsgA-OU?H=#VZ*E%sbA2#p|Uy7VlJ+ zrg)E%VetWyVNARvC_ci#EieLr#i!yMbV0n75by!O;@b?|@-IUia+roB8LI(C2Q4~W;Y;qe?Zh2KQWRkR`LuS}=5)G+Qb9BgDag3uN27p5rsi`q!EvKws9Ql zG1s{!z@cfXn?uc8Rzr)F;D(mzw|zs$#=nmlI?-NKt=iEKZEZi1hAx)34Th@gltZ79 zN;GtvJnIo>VZ*A#@wGxA01k`kT9?PQdDv=I!PdAo58I*Ie3)i? zeKQUGuw&}I=dd&F-VF@9z@TFsXaaE99a+Qht_-{qIsrI5Lru%!`G)5+yqIi|#!vtb zA7;JdGkl^lw!>$rjnnX2vDs}9cRz`~_g?M7?H|)@bjqG-50)0*C=v+A5n`x*|SOrF6CF9+Ym=t3&A?>*`Q? zkO2$>EIk>gH>Ial!L;+x0px(CH|4Y*;gD~;NB9`H1y}=cguW~~BHgThM3(w$fqD67 zL@@)8f!zQcQDMD4K0>{t7$I(qiucJQYV3MHBEleGLOB3OEU|@8jaVGZoR%#${h_k8O0mi|#~a78ZPIHj zJH)_aph5u4j;hkns2NsvUQL{`%M84dRsmRc-7f#KyQ;F0NtPFkBfHvy>PBXh7hnKJ z07sUHTVV<_0yy#k+2@hd<<&Wj9043zWvzeYBB_ugSLuqy%B@f&fFn0D@Jb*F;K*Ha za7G?7jLgWR;yprBO9M6RxGL_Po*E-BTBpXyYpQLuZVT%=66HhX9yQ&|6P346o+`gz zo*^WqXH0pP6mofq{;s|*Ts~Hti}DF}371b*7ZS^7E61%oX1zjMzCe|{!YZor)oKXK zH#2ZGi1*y(+f>_kGw>;73Sjv@vjNA+0hag_!16P41K1*@TYi;nxPejuELS%XM)?>d zSgT=+y1Nw5{s3W8_2DSxol)7*<>Y^HrqR-x`+RZNWAQ&mi{TiuG;#@!2=v=xi) zu>e)*8?+VM^nh0EQswWDJBO$^WS&D*oVC6muDGl$LB(ygv8lK#?lVQjVm#U*m2Pyh zc{6BqS2e(+vn+R?M&}t*Z}c$I$T0n;OzGxm8gmmk12|f}qaR%*ZjUt@7l}sK*E16WxkEp}z4T?;D5X|<`G9^bG^eVwv$k&=IM+s!sJPcUdmcR_;@stvsq8I%z(1hMFTzohmO1)v{u904uL4>odk9 zpB|HBDSeEJcOH|a=KGjjb1cRb#?QksWvV%2rs(dBnJ%aB7%hM?v|kqsA>f#1sdZx( zo8wR0`!UOC^$|cGz%lw7!WfzbbbPrbwS*Y_?RSv3ZuuWn&8&_!Qm;aBR8t-qP4fS`)_3jBnal6(lltiCDO3 z^9ne2xs>+Ub$SHHZnP`d*sZGVWA~dC9JbA)u`2F+?8P|SGxmzTzhh~o7vjA?N!ERT zSMwz0{tPAa`*Y*=5%(7|K-XD*{P%_BGLPh6M_lZ7;o(H-6a4RxD6Qiknx71Q}@xw9Kb+6<8S;valK3@JN97h9RPG30CXH? z{MB7c7Etvo0ljM&pI!9xY=AZt$PYgKxuoR|K-dWgd#ExLsgL>hetl>%bub9%&SCsu z=R3I$K+kkQPZb0790l~8U_7ZP@M$W$8PL0c@xv2e{tcB@3<#xES`{GFG5#tX=uYyP z4G4{l|LS$mryPKeVuPrG-s6DYQ;Z*L>n$37P#`Y&kiS&!1wiLgK<5g^Z~xo%zfhA( z0NrJbKe2!A8&q{3ptq3m=LWv^I7xLUpl=W3vvR-IMAEqq7`VmwAFjOg8Z}@aARJ`e ze{uOGswfEP$YDJF<#Xp~yp{vHRx$p}PkWpOK+hr(kbO`gj-6!5AMf>Dxb`9yb6wVvN7@Y68&5wIl?_YsYk-LZcJ` zbT%`dbm4UxCFf2+=N`uSphb(QkyA-|825bFJ(fN$1N5w9{K3ToR?rwe1n8Z?`1RSh zK1ppp1L!}`cxm)hPpW9O$a~Eeg{L`y*NGuhAz6The*7@O==wl6_Z#Lu3<(oHAjp=~?Amdr*Hdc_7$^ZkE zjCa5P3tQ;pbihCrg7|-he z0@(%M52y0<_(NwmXbJ`MZCo>Fll)C=LFFZsXpek6QuV zI~Z?%<2%<#ZgT-W3mL!vl`g|*o@D?+76Wu#0CZet{L8t869C!{K-*!)_XNh&QX9FT z-tae$xxYds`ULBPuPB&K`r$bX=()gnZs*(Yk|6R00f4vmsBWf@m4NQ&5?SZ!aRQUsdjtTU`T|g&eeCQXSAt}NwK)A#Bz}lDog=SX~plcZ8 zOXmEEl*@e*(0zvSPp;|ENbT4qfEIj1@Gs3W@9^CnZ+t^0LT<~rdcp|^HDp7`uH2!1 zQI*R@hIBse(ViJp#~uMj;k6f^{w&q8AJBb>@$hra)2WWhfS!jKPo8*UFtu+5AgpG* zF?oxV)NBW!V>jb#>R%Jj)&RO^Gd`;Nr=OsYO9iNeKleoMqg2#wK+lJaf7)4A<^c4T z1A51jC0ii?7W`*3?~jte#sT^#GX6sPu0$I6M8H4_2uY0?EgQjXSU_w{M`=8xH(>x&^Fd@kJHSZ6|xI2qP=5=1) z|FzLnZw;V-Hse2keAE5(@gz+umBLu$cmCYh*ZhW>kPheyGXBg5?`|YT4FdXd7{_Bj z`?btq{qypcmjK#sLfd5z`tz&$}do$P5$JKz|b&O9P1AXb^Gl1T0jDIR=FW0)Okceqa0q<%#Mj=Zzor1X(inf^iA!Imj}-#X!zawo_>hE< z4d~Bf{DW9%-#ibF%s8o0WjhxK3P*o@Hv>8sF#dSK zTV1H!VFJg&PkiQ=*XiSjfB|Iud2++5bamUPsl_aBa zfH0Aa-V#7qPX0qKptFFHEddOqF$EH6rgu3bh z^dvI=+=owopFY+K9uZHS|AP>HtON8!7_VFR!Hd*}1AxB6j6YtTEwrYHMuhQRJ%nWs zYy=EE!}ze>$-kzqE)xVZ?&|!BC#a}wKu;dytzWzKHWjrV(6@>4{oTIQKvD~kEn__I z;2(E5RQ_iS6axlI8E^UOH(%1J&bX8g{7c)Ec| z)Xks%?!tVTYu@?#E5#(ybAWJ>@uBxEB$f5wruoEp&i1iC25379Z6_H2(g&a2Kwn)* zBgFXU7yORqw=)~inaB9?Gf&gPo}n66yrY_6cc~F3Fvx{@zN^?$b0a%3Um^`)OO`})TTRte$M#UuY7`Z$+;HL zxqgB8$^eXdF+Q!&>OWAy z#eyTnzYL!iAFl!WZZh65^jozcGote`C9l3h>NFV;9%B5yAxnQkqL={aoXq%_KJ^>Y za@R#b*A>P$j_*t6(3=bBEnxh~UtXc9?avZyC%(j+Cv0afV4#5U6{8Yf1(`>kpHF^z zJ3!lhXgkFC*T3lQKqA~LfNK2E2Y(>1!FLeQcZ6~8jQ8$Ri&g`|I>x{D^z-Dr_?iKI z3mAX!$&Mbckp^F6u}9}cgT9BZiGZ#tZdq(MsX+wL(ad<-{wpr(-4;OacE%^C>?X%CkPR5fV|>qN%gDV==n9yS!T8CB zzQ3TNQUD>1@y_4uo(j-*2HMUuJ};?~lpb~f!fwVZx84?e+iQUCn~dN5N-6DFyb;>R zGVbiqu^TnQBfyaSn|DtAiUwdZpz|Td|6G3hXEXr209|_-fBX5bQBj@`0X@k0fUnlj z{PCp;co;twy(G-#3c$c># zKwmoJea6iap5ie9_Tx?Oe_}b6{|umS8{=J`x=I$&KUT20c*5taUUC{q{LyiWmLM9A z<$%srjPILMaD@6%Cg_d)YfC@(D3vr`u*>+0)^F1q=1--)5aaj1okvcuzfK^%_~n6n zDyiZ700RdZ|MzZJP65k+slTLBX8<~D7%ytd{W^(b6`*G=<0CdcBNnt$+J7;g z(s?j_^mD*~gF4fb{FT5sz`#Vt|McrG_ouGzpqb8i&6=BEr9rv~=)S^u)s`XDRc}{7 zZwBK-UvQEC<-H5&V~h`9*+4cRFat19!}up&??MIxt^&dh#wWgCPFoDe1VG1R#_w+F zKwg4p0__MGebLZ_`SIm!5MKlRLW)WoBJ-V=lNf*e2QT@ls6~L@rHpULt0i0N-wo*B z$9VRhOUq~sRta_{KQpHLS$cLKp!*=>-P?IOe$!c{JkzVG?;XOKBHIN}6YoMlp~I z7)WRQ&Ch?1EHP{Vgw2faeYhcyn&}eIQa;!-?LVlCNrF8d1AES(gQ9Dkz;yEIWgA|gty`HvF+}fjK<_HX$Nb<4QfcpbK<_2S z|8#ILt#W~Mz(A1k-8plo(#JE@LB@veYKYZuQv>OVv0tOZ_zWme@@{a;100XBPZ+U#gS}Lj#&|Sj#YfayyJ*)c?p!+K0 zjnywxGdza{-jRP1o)#Oc0H8OS@ej8T>*@gXKV+c!Ffbc1(8%};zu$F}H02ig35?%* zf3VnwUIh%?VEiwRtR#|Zv4Fes#i3Vf=;K5IbLB(&iNgxdc|gx4#$O)(FJjGFA>f6) z@53vH)5nW|zAKE^z4q&I03JwWJZm-I1W*AFFh1r>qn@T#Rseb*V0>0@)(FtH4%#*{ z{`;HTKL^mZ6WaDL{>0%E4jS$&fN-7h6W>@*>gLD;bQCiForlMgMGo8n3~VK%9UyFE{QcG!$&+_Z2Xt03{-?9c$@Dun06I4_p4O%2Nm7PefX+Ll z3^N6vl|TAaggjYy7N9$q@qc?~^1o8=Y6Z=a?;H0c@>U=Z5DFPjI9rrU%{vF^xXAc| z4bQzwUoNNp1LNbp(@9W))qsI@jCcA<{^#lAZo&8CPn`QnPbVN`(k`0p^hH1it(ML$ zfX?lVzcKwsL&wqF938erXLOhx{r|BGyn!-jITKV z0r{|=D}bKsjBj4^jjvF#n*ak_7*GF2f9jEQHQ5cuU;X8uCs30z$njwO#Fiz!Y0Sp~ zdM7fz`>)NkpYSgO^si(*_M@lv0kj>2wj+%H>xmc0V8SqJBjcZV_=WBy)~SGw>5Tv4 z{ck6^03A0BgfTj&06M494?6&zyQ#US0R!h4KlRJlR%+08!3gGc5B);8Os8qmGyaPo z{!kpm-T-vpW_-_I&x_sXG(eci_^Hz?XHum}fR0qg+m4Q$NX^Xz^k*~vqfULE0%%(b zZ7Ucr>$Q~pAjdX9$4EQmJdAr1)30^@qnI5j6czqeu9+ZB%tpM?1OWt zBP#(NYZ$+^XQG&;9zbs*J)mO~<6Zt%+L1n9C0$_r!jGKgB&!2}&clos-CuHs zN0Lc!?}0YWXP3rj+K>swxkIni;GWpa#z=R~mTl?=N&%*JH zz>)L%_3!_bG-DI_3|Sq{LR^q(rk8L7SLt>gW4az zPtR@!^loL`xBP7yCtoX|ZxQ2(&wT1T^lTO&xi$(g zHvhuj?$6W0T|$dDb$N*(2=m^dj-;vH810c6_jb5UKCEv7pl>qchf3~>1?w=N?-=91 zDR8Z%L0wOFjqx?Xf&zfH4bZlkareA|CA3Ie#`xG7UnCpl*$wE~$M}TL?GwA>Jv6F}_wW28vNY~z z0NvXd|NTq5Xm0sa0R3rGY zqw89j8hIQraEkGF|KrWQ0F^L~@ur~{#S5ZZKxkn6=4bDWp^p*4_2*69zD)ZA&o#0= zjK6aGThux4bb){7Z~pt?ChBX2faddWIalqV_GSyRIRBU1Gsrf1t^;~*F~0n7Z-^~; zS3q9|b%~6C8|tIy}-qT^|qVoW%HNuQt+Z=sE-FI?wo50w0{BXD0&srZE2X zKTRfY$G1)}mid*%X)RRLA;7><#!p?(T|r&gED{XxZ-4cPbM$e)NHf5n_~x>|Qk$0o zI#w`#KJzKkPv=2E=Mly?EqMA#YV$=}BN_i`w{J$Mzjd^UWPHbpWTD$GKwp6IeBal| zWAx7e^w%(6_UaB&=D<|Iz;wpPkNDz)0BvibZ3E-JTfcgqYMlY-plu8u6o;K#0UbLS zFHLTy&4_C&ZIT#2_V3+uNRGQ`yPC$>tfT5!&hl~sTZ~@SBnei|F;SwpM zFO%wGyehOs7=gKfzJ-jh{kN#tpxy!WamIgFFlQY=+g*`xg1`8s--*p^HlQny@pbRd zJ5C)a0CX2K-eX_QAL-GlfWGOB=U)weoebs)Ko{+ZT^DExWPHsJ2mF|xDhG6rWjt%g z-wpt@or1QrjNg4{bSXJkK|shMgL#S84RRT_lH0&|&f{;pX&iS0I`=WYE&vVa;ed7*5_in~}-s<{i zdUiITw~_Hg_k7x&_;LV!`HW|EdE_v4JQdKD&iK=d3dx$fI5`T8_iFsHaE(?0y4N!P zg;%an7d-m`J%#qRxKfw6+ZzX=8dURH#?%=bYO%-o?f+G0>U$A!UTvCHV zKvxN=;Z8u$9>$kFew)^P_c1{CNyc-x_acA8eFo5dp7A%r|3aRnZ!4g02jjJaU+hnF zZY*G6JmXi+6uw0@`T!w`@xL_mT}&TO0K#d;U;oqFWZMG!$yPA_)b8#;(!B!cyG}mC z0f8jv1Ag_wvoyhy1Yw(ds+ZAjADRJS0pmqmKS!GYS67iuf`9Y-MW3VQO$T&WF~04I z&a}l0+@ehltN zEM&YP`X(7L*A_t6cE;0Q9!UnLk&G^19Pl}h=5kOxvtj}Czk&09csW5K-Y4{5B`7b{STOwW!e9aU+4Uvdtq5{fn|kd_$wI^A|Vl} zDIy^$A)*o@v%9nG;OxvcGt0lwh=`C#4Uv3|$cM-W$;^<%RV+*LhvndA{Bs|FX<*!9B#m z{l;F><)kdoodv|sB4ek2{bN@WEug|8V>|U~mQ@bDNleF#{m;R7u9y3)k!mt_&&b3Y z`M8!iu-@2hcYplf#cFmS4t0@U+DsgpExyTk;_yUc{gYmYukbLm4D8>ZzNrsU!4i|t z*styCo|QUti2W7D{-t(}3~~P`V*k~~KJ=Nlo|H$`h~+f)+kd)ORs-Ruq^*tp)QGj+ zq+w~-tkYLvHl34$eddp0B^Ah3-%u+!L2SH4j#&C-Q9SY_--pYMX*!s?IiOcL8LhKuE>^NV4aR>cpi2YUy(?A?Z8T;Fd$B6S997`OW zVC>go$4|>gCJqIRy}mkfo!oOVv9rwB&wec|UNTJ)$KKel+|^W&kB#Ev7`y8sr(Aw} z8{|yv{fmF{5ov-=5TdZDU#=D>Dq2n)tu*#i+s}SMK0W}Y1AG1Fmwr#`Fh*Q9(Ae+X zzD$;%{xRak7@L1(H-c4;5QmQ%J7(q$z2wDb5Jwt}ojB<=nd_Wm#Lh`$%ir&c=%5pj zGqOJ(`7BOY_RHjGZ0(z?WEBK?YZ0+;sj-iD{;Z4x|1+|5GWI7wxkM(y zphFz2Fm`?RA8=V_5l5PhJ+OM~KjgVRiJiX2KDx77{3;p^*(W>r*a0yuflHx?V_%%` zeOayr8;OHyW9!FVTOcZIrNVY&JKuTZw}}d;sBqTU85?evxzo1+Vn+7${rfML5;YL} zQ^vj+y;T;y{<*~ddyM@>`bRR}f>Xq58hdG3Xp|Pk^R=)uiYuPS|AGyW5>Pn(}(2aOT^(F#?IKfMXYV)0r4-5ef|0S|1Nhv zCW}a8Z$Gj?tV~&exXdwj`0T4Mkry8&PO!1xd48JM;>dln>@zm7?)E)0!5k!x9FY;T zjySr(*umF*Wtxm?$5rg$=s@D=U}HaW`Ri-tSqF)uM~wZ#2P( z;s((nG3Ulkc<50Xp#C+){&mI%Zw~aAJI^N$EHw7@kA@J3l_CyijlF*Imd)~UJ0yne z!Jl6@P3~L?fgHQ$x&yL^DCX1Z^e-#$i6PZ;~mvDf_c_t?gIPkee~ubrB@h$ujgu^*i2CzEv` zLL4YJ_8Z6FJuVNq4VrJZ+uv^eN`RO)$&yN@?11>4(y$|4^(+oPBJE{tbl9bLNZEJF ztZeMcML++F4D^x2A^BQpHF0RIv3=@(2UBQ>oshB5Evp$$G?(r%cKq4nf0NO*K#ZNS zXQPiwxqQcoeeW52{DBhzAF=Nwu}}O0{}iZ`S>AI$+@%;8Eh>31e^Funuu!3yDKZjHRzU)l)t$gVK|I&wrIHg2O|J!y}CKpSg69eB4SL z-frweEgy<0kK87v%-F?0-zT;^@+cI8Z2FON;!{OO5JyKF`{LA^NkkRY&DcAg`$I%i zWI`LV>8ci48LcNKnecsA68px;Kkg&;tu*%F@fmBS-met*K7-s~rGuqg{?;J8rT75QD{p`NmAC#HFS4r%vHnwcid*T}UE1)iCpZerK zag5)AIMBt|UzCULl_y?J92zT6Tt^(*U~IoH9{!FzaUpSdiLt#?-~XJv-X7xUeq#r0 zdtTZtForlV-q?PBzI&Yvz%H`XH8%d8*MB5mzLGdP#@J!USBpCnjmYe5?9XO?<-786 z8*y}}u?IekTra;h#AP94AN=r-UnZJNQ;i+GV+z8qjzLAxzH!wbr%3Gk0V?R9d z=(|LP!&G?7*pp?C1jQ3NNbHxHBru*hFwxkW+2#KxUGV`(s@Zj4xnF$I&_v?U6k~fn zUiO+icM@c~?C%@D`b(*#Rm9QN#yGeqruHA}Tyeg~yB?G4N)YCTKb_ z)f>D0hF{4qeK!*OCL8;acT)4D2B#AHXBhj1zh5ObGkTObdcxRF)oE-cEhi4HF!qyQ zySGf65`{EBqHxWmlGxn{IUn$d#vx?YRZS1Gc zT`lf>S%2cPOO5@{ilOkHb`sMbW4{-n3G#6_G3_&UPQ&`N*r#s9zMj&`4YH&&_LDXr4Ga-`L8Ii^Xl*L51DMj&4jL%H&EJ zXvPkhb<13-*By{svzPW+*@LJHRT&%ZUpF#H)2ZH2in3x(_7eLJ$UjaI`_78NT}JGG zz*r}GcneYC7!^($J8b>ND`k4vNK7)Kd{PD2E+0W07%fBVVdCH#V}JXjjUA-aDdJGp z*gl8PBIs+e*iK`g{GA`>`90$B8Dj(QE|kg3Sx4+_FgD(0k@)z&Rm8s4#=cqe3HcbF zN*tbH?CHA8#iR4DCibs2_SZwYVJ$j=I5^4JolR$+lIn?x<7MojJ|DoxUqBpPWbBnc z`qZuR@g?Hu4r8a?5c!t0=}=-CVQhF%<58dZ=)@#d;G0P7n_}#{U&Js7t%Rzd9X&ZG zecPEp>`XHD^XacXFE?37OdE`y-8_7~)XGj*im)-s7F&Upnh$BbEk$aXny2V&$(-!eeoRh@PX=6jn zzWg(J^h7AQ+U)x`43YAmB_@{gFC+FnVC=P{M?NBNy@ELUu(81tU)?G723i`<`7af87inGy6jh-bplxZZmf7>NmuT^mQQibuspej{9Yu7N{T&bTjs* zVYi6y9N9)3*=g*i*Vp|@p4lvZyRp5$@CzAvPK?+YXl$3I-~S#_;Uy~UF!t-0#Cl5s z<`dIGW0!PUAS(!81r%NFtM4?*)a@Tj?4Mxl*r~6G*WiB%;-NN`zfRWkw3?XK8au;z zXQZ^0OiLkSJJy}RKH3myAKK4dH0~YwxP>^p&Dghp{5M&-L?+1;ZS20gPGCiTBXM-H zvFmoO`;&ZJO&nco>^GMFK~^YEh}h|1?5_rX>~?wXjl?wB*uXy?>m(od64L==ch0^- z>caODv2Ta5`(OA#>LN6qI8<+Jzcm}+67MDs>@)WFU-|P#Wj?JX4mC=_cN2&98T-*( z@54mhsHd@y&Avx!D7aGGFJn)h?2SB^zQmzH#{TS2lg`O|JV6}UXzb3{ZXO~fy^q*g zY3zt@du}DVO`N;WfANm4J}Nv*g)P!MnAjgM_Mv6ZpuW!#`>e5-yjVbF#3|z7Sz||i zv&*Y;^J(He8GF+`zmp1%E+mdFG4_og{p2S;nn4Y6zkLw1wGE%`et@X(7!{r{cE;N5 zO;Rxx#NyfPoPY5jWu`k$?0-+p##AV-+GV}}^hJ52H;F^Xj2-%utoWAE6U5O|#%_9f zKlVEE2ZNIsq< z4xTpl-E&_O8x|Tu92#!ytyi9Pq}qpymu2j&@iFp7&P&A34rBki{MuKD3QMT4+}QVD zF$m1t3Nd8+@pWa=A40c5a@20lJ|~ld?@eOgF=MY9atL9P2Z)1*jeURQdw%)y0pieM zW2bHyBkp^+ia1|YK)C^bb}h-sU#*WEo(yewZcv2V7qFIPPL zqPRy3iG53?0XGr*#OV(%Bo4|ICrj+iHMZfQSFzuIl-Pelnr#YkV4AVNzU76{a{VN+ zU!3&7bmBn0v9DJDEIoBVluy&7Aky8PM>k4HcXM>7(j_6?+|eK*-JJ(W3n<6YEpQ-t zl!TND(%=1Fzkl{__nDcUnVsEdcXppyWqMNNYRSy@q?8~#IeVWLu+aoLQwjHh0H=VQ zf~!mc*4+4=z66jqIDCpfiTKToH36!fWK*|fN`i9Yp@_d-*gkG;G8Od8Gka_44!Nq6 zS#88Dz5VN(OEyGT90L>nYnPKY28&C*!`|c431|M$V;mkgaYB_Mp{t7zmPP6MQxTL^ z4+q!x;c2!KQ4*{qray>OJ66fcmx`DGCuLu)?3;Pa{lJ;~q~2Q%5C!c*G728AI0>8N z&{wS&nNp9G-!y)f>hwi3>S)kjP)Yv2nzrA)pmWd`@L%ftJ}dSCJnYAt^i=wHL*_3w{dle5cQ>SSX1b&5-q!w z6772!{9=M0do;Le*jeDp_xOqZk))9HgXhWh>EA8B_qr^Q*X}3F{4@cz-@bBspI4GE zfucQ6Qp4{duWyI9Iq)dIWchTXoBR%{M!4QloF{ea&NYN<7ak26c=_8rw?Y9o(mX%)M3V7ghZp3u zs-Uqypds=xj_UgvmUUJnNnf6X#1cGb;0`pY^MwN(ou6bWx|h@1g+D`hfc&`lgc^Im z`=z%-EW~$dqhC{oK1Zn)vd~FSDopVi*i#GP7?D2j>hp-L=bS9<eKko++8 z+W^rTTby+Wg$GU)>Jx8@n5t0UwM`jfCw0ZcJK=DMWAnFLQ1lKcT72BCh%IGWwBvjy za+K^l`@~y&VbS8WKelhVz2Eji2D&er=_=D1BKq1N#->Q}b|*{`TNd_jCO12jWcY~0 zSK$lSu-DuJpXM=OR@M-TcpVlni!pWY!UEpz+sMQ2Gm2f7^d-?==Q9VWx3sf3IzCbygsTQBXem)5`npfPBAE$s2# z+Cy>rfHnvI5>0SgzxYQ^onOfh4!&m}rF>sQd`C@+i~^FS+VbEO!X3Zx`X%>v+MxZs zu+yg>E94iY{)Bh_i3Emnw1-Y7JwIL+G^a*KbUzGLdk`Jd6<}Pr1Rt$6d(j6Vm)z5E zt4)oBWpdY;F7e;Q4N9{DLh{r`>pj^;zXh$k)$ZBKg4d@b@v68xq0VXfL``K zwd61HRK}z?Xo6ZoV!wwbt9Kn$2=A0>CUib?Yxr(D*lhystF8O459UuaX>lpV)PA{oYGT@%C?CUdtl;g=o zXdxB}*|hE#>frkY2Bk}6q`l27SkwA>rRnAQ6m&Z$2~Ub@%)oC+C}=x*t0@9{aGX6RW{P2TAMmmk6HMi@M9fKQnCaF4R=bnV8TTAfPcKn#uSSSL{D?zKd@~b%s_@m&`t>FUnW>uQ zBJAC_SFHhE11`VAKG9af?+Q0@ddncI^*7_+&=BmdxhLg6XH=(jr%4VWsZ_mhi&)wmwnQ-QfBts-W3=JG<$W$=|EB-_9YJM7FQ_~?`BNyv zEvMc#A=!pV$HAOs>uMRmu1@f(j^Eic&_J3_clYAwTuUczdx0v%iA^y z=6*S7KNIY+@u+@YXpRdy=M1wF-44i^7{P(5z~1_WX}?QvW^acC z$UqQp%6XXqG`$Vx|8&?uciFE1D%?g+wf423SsowQ`XFc8R^JSq%6>xoEqw-jRHwh^ zc=8u>;k_@AI4^>{{(HTtTJ5{R>YqYI^YZ#V6;3}Nv>#w^PnUBqD^tXNRVaM5O!IX! zGhjvQWb@6*Nb^}(W2%HejD&zi(5@KbT-*NXo8L#=^uac0BrnXVUk7W?_&ex9GC6cF zhi12ETK-42x%nNhAgc)IV;tpOx7_sv4vWA?34uxY>g#~75>0IQ$ZicD5d%e2uB*|m z+n>yM{{0)$y%^G62+>ImGd${OAfDgpI1aG3=8YYQgNskY1EMzNCYzFy(jeho?vpA| z6ClZ*z5Z)yFiU9iPowu1q#_poBnW}!BidIOS7d9T@B3;tnd~T?=^E!p*!qxsZTn(#w!9RK6$2J9@S&X{VFwX3(Qt(2%^{6S4i~X8bgD11r7}&E0vxHE1|RlPzUZ;Od4TC^Ykj za-Q4CEWcFGq)zOcnif+JnG`t3Y5UB05^|}Wqulwo#w!g+1sV~+Sj7k12pd-3MWJP{ z>P-6i`2-6dfK*us98MGdz+M$k=-nO-S$;g_hx5_u&=$TuiCUatQ4Hhv{v84NT}-!y z!_RcZFYfQMJgTL?c>o%M!MQr`m3lN-nT@`}jp>Vd_yR>_Y5kJc^(E`X^=!GwPKB@L z9mCCKfq@@zgxDU&;}v)4pwk}q1a#7!pqiFAoMQ((0rKcDa>&36{GdN zuG4v>$k3Cp*f%kKYR-7M3BPMiuf6!1?3Fd7 z(`WCMK~fn)F{FF)B#4Etx;urj;U9Up!|Ww>zGy1V%QM-B`}w;n2f>UW5ZJ!;ZFvJm zw1Feovtgl@_Dzp7!e#@$w<#l~Wc6fM+E=+c;eKoJtb0 zV;o(y5Gk=X3~#*)Pr@BofTsJxitfI*idRtL3mQ^NhS+^3VZ4%Nld1eRh=$l=z8Nt> zZ29LtEC?x42cM02L^%3^(8*v%1%K!0kPdNgn;giD^nm8GfIy`CwgU2bq&dr!`&F`p znd=Eny$AB%ajzI0q^f>)$KXZ{K1U34*$4Dz=llux;`cs}faLSv+Np223wgh-gVdK5 zD*)lob32nh_u4m9uLa0>zbl0lubVwYg{;Zbt~;G%bI9=3yH*##YelZ!giha9kRiP@ zsiIe(%=NJcY1m%8u@AoU%!xG#>}hzL)JT^VyDp<*;gs|MrVZl~0UZ(@cobd|1={fwse;A4|%W$W>74*K8100&4 z^t1DG&fkegF!YfN7WUrl3jYG9f|0Ud3L3crvwFWiIy08o#GcY1>Oa=_Bul?l4t*R+ z`Z<{+#eHT2Mi**A=R(LX!B4A4lq$+9RpODGH4}e?s^hB(ZgD`gMnD4o1ii=Cv!x>wqnseQwjsF@v)g=Ue;59W zJEf6d=Oo|rWC}ezs?=12)?MT(-O_hX4qaau7Irvt0YUEg5XCDeFqtaRnz^5hE?b4@ z!3Kk&rNS_)c0|2?&Nc+P{Tx=M&CL$4vk~IS7ZN#%ml7 zjh~6A@%|`rn+*|W2-nHp(ME@S5W2cO=wJqh(m>i4;MF=$+9z?%k?9pxM);+sX3G+jl7ch z72djvCU^3KMi7eK4qA{W0`JJ*(M)^Y$c@Fq#M|vX4BkP28kwcL)qM;^xmp~oP9R$2jaZU zk$9I1IT#PH!-C{WNPUNYe{(9UI_QJ=fpgeNVC#v=Lkn$V3?TX|g0dmY_%$0$d}FM#R1tc+G*CUoQUA)Q_KofLI%bl#tGEJVr*lD4m>Z6 zbA3XB2-Sj#EU(Xt&is7ogttw5K3G z$d(X$){GWU2r5;grS#!R=ZOD-5B<`e%JW;Q=S8<7s0EXpu+&|ZQ{Il>L=04=;zfj} z?bZ5X8poe?mEs4idL|TOA}r$bpk|aaS{K?OpMaJssq!pG>xB323x5Lr$hFnKA-DB2 zELQla27?AD^r)K5QdTj1jD2|NXDBb2-($aVlHeNZrcDcKiG*`Po5b>5DB>vdJME)!n&xEkdq#JZZaoY5hYF5t*$LM(mUzV9heM- zT}S#-ya5^tBP2Z8QU_y{nQgl9>h-{dKK>o%PpsgX>*{9IDO6ke)B2{TkeX+~o1+YI z=#lL@1(l5DI9%G;GYXjmPEKxuwpqA6OrtCk%Q09u1L$hRqhH=x2mQCTIRwVVmm;!G z77^4~jlTyt0r@1A8oVO0Qc5VG4 zZA3aTNSKBBzkso7OiIF{O2ET}c33ShVm~88&M{VD)DXGhj)7q@YcW7_KWz_ZezuG{p>T4-NQCwq zKnNv#4N80EW@gV#5Ju{sNLUuDz#hTmeg6Efe(*0si=aci(OS7|wutwP3^x3Pa^0g{ zD2$k4R7S6C2${?n(pe;gs zu%;bRlw|Ko>`J%qcS4#fx(9mhhe%W#0yZ1(XBnO%y@V@fnJ~$?FeTt}DT2gp>738s z;G>Gc-4wIwGBzVz6@0j__=p-)+(I4jA}esru+XS9Xe;rJLxaSbp_b^g=d<;`kU#MP zgkG{lawuKs+13(Ca(380@4vJRT%tdhRAy(aTi* zqBb&ugZY|Z?4_!yLc}W}+AludSXW#2ShuzWWch8Qj_7!8pD`a{OXzKcr4gf&=P!s3 zV&5)5+5ZuG*#beWH;Nf$@t}1+*=l8i;5cL0~}^EdyFdhF*L|9uhg-PS`9}L7|MOGz33(A z(Ceu%NOp~Kk~)hYZ@)BeLvg!17$+HzNo>T=Ga<&wpJ@k^(<~DMvCrRnOT{Pxp!6sj zF|ukW+o*<7L%xG4>8;T~F1G=u10t*&?2a!hsPMY2zf7*!G&8DQgZOlIR7AxQAg&Uf zHB;i|RgbHQ$y@kqls8J)=qZCJxeB@hLb^oPNy%my{ht`#ZOU|pqD>Q|OGk+W$-a)< z7>4i?GK?lFX<`dCd8gsui`O`p@S=(bf5{*ZlR6KrNMp1n>&8DdvHbSC`cYLG8b>#8 z?9HAkz`Lu2=7@?zt}DC66(JEji6$^PScI~PA?PSEtGGs2#@)h@+9>G7?!6StBcUkH zJV(>VanAHb98-j_JBhKEISBI&7bsalk+vqW1RKRWKKVt92$3c>n!{Kj7HxQVl{x=e z(cL_I$xVw!w6wLNI@2cloV@CVJT;mSdIV7UE3;jktTd`U=3hQm0CO6ho3)!SnrJek z#7QxVX-b-!TL6m`ac(1u4O%exL{O2^`h4n_XZRhZ7zJTd#9!xucmJGt{BmdU~_n6*tgyYld+N4an`*#bWd?oI$IP*K!vn!CVb?^z=>C>1D=l*s`2~y4Mtp}%_j8IQ?Y*>#_ zAYim?(D|7SA!8u=cEXlUL2Wl%*M9ZDW^4;#0dZ6;8mIV{38N4r7dc0g>e|RQ-*ifP zU0kL$>n3wGZ6x~E4ay97h%>FW)C}d6I?!>{5cQ6%=2u3q=4Q~jVcJ7vzT&;erB}LT z3{rX3Q7_)3W{tnt*e3wni_sY2$KLFB^UH}gyN4S<+BUdJCv|! zZ0CY?6rI#^K0;+8j~GoG3<{RzAwLo1p@o+T@7ytZwhM^4B(^r0-lH^l(tbb{jQ6Es zow6HZK=mGls?6hXs*bv$KU-UeS$6ua7e~Bx-aH@b_^YyALtv{`g2Hl6Nhm%jo{sWN zFXk$Ar_H?HS{8(%ACu+{IuZ98ClrYO!4q+lz6ye)U|UCU5g5JF@AGzvX5lCpVnM;9 zBw+o*v_oqm%K9?G=GiKX?qXBrOZ~uO@sdFn1m-j2 z;ELy6a%L<+*gjQEZ4~&qDvHgj>NqM1DF_*@BtkkV(6;4*h%WZqGs5BZ50T~rMyhDn z_z|4#;wZ488(v}pR1djV!Zr}uR7Q6kkQhcuN?I1-RP-bYC;$aFF^wK03oS0e zR}zD~yYP>EV&X(KLm-B%tAHfY3WgDO1}>p6W|0gh@o2QX7e?SfnxO0@u4-DuW7yx_ zm@`HVLb#wVO{6}SDG{8+%QSr;uEvZ}D-y2qjZ2@3X&bG8RS{iw7$uhiO1*2&r24#s zdpCJYl{hjG8y|J!#Wy|KLn6Av(^EHEiC`S&dZKx}2yRw#luV2#{B&pRQY=MM2b{Mj zC~k2fLb~pZ`WRL+HHCxMER-m${In0uXXKE+E!K!Cf^%Bz3!4;_CiLwWtbds^ar@rb zG%`l&&DSi4haFz0fm0_nRKMHov*F7i_kDg?GQOb;(N|@agFXkaV82lA5fP#;J&khU zqHs>e)}ifUz9Kv90@)tv+uGjsFmL6w8L-z7(npEkFGsTsij}&8% zeB+zw1@BLcQ;oK_ucVl}De87BF@wKMSeZoRVD7U?2hS~7-ABi#{XsiQfY=v_B~$F| zRn~R4`|8k-M5hH+5c9{+k~p2uK3ctMsAk;WI}JR1r1zP85pXTLL*F&`;ssA!T6cP< zRYnIz#}1}D8G7|PiY&k6QnXI69#+xs1P+p22vDjNkN@4;ggB?qQi4Tk87xOx&G}n$ zf+r`JsFOXLmh|c2{buj0F1PV=qn@F07Ip}UBn3#+Qda8jSe63vMU{! z*c><`jnvBW(3_!zi#ptfGCt3f#@Y4tL}Q~lv-OC^>Tt_Kgp|bI0tWV{>M~+sp*a|k zdJJ$zuQVnPUYC(6n_=S|3Q-oJ?%$GUiJbkW-csX_OBEJ|CVFqn)pw9LOzUf_>rxbT zMIfFuhEEe9iFu%=LE_XmRTS5PXrUq?`LOP9yga8W=#R{i6P zeh&okV3Co!FP&XQ-YX)E*;aRz2Kd6&D0{Os-lfMTr=(R6DwSs@m25;yWrI8J;Rm7q zP**m(;EDYNV7GYx1|U#fn#bn-H6eR$&6%G~%a_XVm%5*{A&0?GQ5I>*dk!`#jy#N7 z#ph%+7y3i$iV9|e^d3U}=m^cG0!SpwZV3jhOagJCNmu>A{eyF7cf|o&w0ii zFBpaLaO#P(^Qszs^w^y30GE}kltq9K8FE<^jhEqddnHub2{Z;p73za;f7im^)gv-4 zpJ(Nij!DS+C=xb|=~C6rF8X%1 zPCdQuC|mvk=%{UaheHbtjNZ?6<|}gg$cdsV>MROY@bf1PEEn9Xn=H$Ct{$V`&1Yyc zmt>mNpevtDd@Q1A-d1ZUEw46`ciG&E&DQh0NUT08niSNa>y4@8GTubIOZK^0!VT}c zVL?JwPF716S?~$?57&l;Fg(+|W)*Kdop)~8kd9*5qPCz3pi|42bBRG_Sa> zRHrt2`$;_*P-sTfieqe4P$txE7+Dk3cD^_B77Pm)p`v z2^C0a@PSo?SLp*U5L13wnQXo0h7qR(C}a;diK&jLp|r;gsLLoxFy@|7_Khc#sK&)s zim6rXw&OHR)Xkl$>n%CA*HSQggp`$MX*Z9h0Yd7=trBbu$V9WQ3{b7766g914T-<~ z4l#^2lmiaG;5IQ4t^>+zEs|->UGmAPkr|6D$W~%zw${HS&#S6+2r}F=C`oYX;Ed54 zHWLk&^9>h`)xcyBl{1hN%)4l8pd=$QT20f3XFZhMysAT#MHvFzt9oX~F^tqZkKAg- znxgVVSM+_gfDIPT6_rs69{2-wL^RhOvhV{Yt;?G%RClS zK!Y2xMMB-n#5zw``94IsMh$lnXMPz%m)$3u8W#c<5tb zVAuS~S7#^_vi-F%=$5vLI zGi&017^0@n<#15`Ws1Hw3uTefRQpsUi0IaaI(oab)>IX9cWDuQ!x1#N?bFX7v*O7C zE9kV(8RiTF6#%<>kBLI33;oY zq;hD~rAaPE80lAx%W@_+0u4M~|5vPn3$#@=e5NnWs#R%BSg!sY@Wx`e40xl4>81Yr zxG&SPTa%XfZbzaUL`<6A-m_bn2S zm1iK+*D3^*F+w)q@MBEQe`(+=jDJ+4yJ`r#_lxTlljb?H+$z2X*OmQ0{baY{_#EUJ zOb37#xv_sv!e~uJp-Wy(N?O@-GPG({Aasz)EDbzJ;A%BSlb3K$;jsPV5D zAK7G7`he9_=kN9GD&X|MRLzCfk(A#`0)8|8Ls7Fq(kHsx0q@tGH2FjrSXNCg^vU1b_`e-?KkB=F)wErufZLQ0wrKOAo)ucGr!j_#7Ab9a8 z`TtTQWXhnkZg8{Sf7Qc?{N_c{&FxT6{=oiGBnAYfD$3i^>>p?f9>y-)+8XirnkO&gMNCF z{uidKC-1CAQroJdGNfpLs}Y4=q>`odZ`c-K#4O4Ty7U$W)DK9QYX1ck)wK!hbJYQhQvsCxUfmCtE>PyRqk_h zne5R6F*JzE)FbuN(G97LHGBVo3@QRn8lkjVJOH_-Kok8%MT6(4i8!q>6roAviHQ2?$k~tbXD|a4oBRQB-#`u&Bj( zUH2}*C99UR)9%s`%N%JZ!;gwOM94JIC+vTE;|R+BPuDwK#ZSc#{V9@ zVfhbmMe#pTpnQ8m|K&(34AsaS)9b1}jVp4-YTopY#{&I4|H3 zu}#)ObIOJc^Yr*ga=;H4B4w!jsx!&7VCN&bGY|uHxq)uNSHg?O_CIuow4U1M;8EQ|z+g8Q)i968@V`_F@%VJF z?EZS)Kh2gZe1CXy%&`tzaU6C%{WB3~DR|;TLUa4tBIU*7a>m3+o70c*JJX-mr%xGA zm&st0wPSP$u$3=;n$2!%MX|3|EDJPd{oyc#xiHm{+U^GCz-Lrk>*}2GV$6c47`%6R z(RcT`N3%yJSI23-+eD*BCOvt|S#wDghJfq`u3KK@f=vuUtQw5^mkTt%jrj2TB|}1x zVg4Sv-dl;iY!hT~ovhd$(YkPW#h_E=3$du;BAQ^!b1}GhXDL5ky4N^S@oPmBkeypN ziV*t)UI7EEy47A3%vR4hYgQ3Ma>rK2erw*BvgM5KuZC+p{>)}6PWOZ&4sT6Dg>q)t zUCsL_c*DeVQ-vkM((ZKYgC#>mW`0sHq=F^>U_?2dZ>D8w}l@$*j5h157_KyzZX zeVdujjl)o%35?gpT`EUWT7>QB!-a_@drsthqC ztcgq+{`?vZF%FW$zAK3kgVS8C%#Se+8*KrXSr&b@$!wG2?7PQ)U{?CVRLeL(PW~XF zs>Y<|v^Essi}>Zhx(NF<#=fk42v+&C9q{FKsfb;1)_TmPOUEQ@nzMNW(uQJ~_eEn(zTh-zo z1VSN!zP>J24IPe74NNb^(-TGCwB7`|3$Yb``OMrorb<%y=ApHwI?k}w# zgAB_;WD@(s#7+5tUlEreOA%Yf_>}0lL-?3Ya+uK;cZ=XRM7 z)~A>c{gn)^`i0eKe)uvsAy4>apCVsTgXcANK$I4TjpxqcN0XwJi%ekR$4}Yp-F%ol z_}@BS&)8fg&tx#XIi_~R-OIRHitlV6Z8SHt+aPf{P-6(NzK^2gEC#bJ_|<0LKGn(d zNu8Q;#G6UrHCAeUJ1(~2a5Q0G7^cs3pLVcJqF5!tU< zQk_KfSss37CyMny)|nH(xb?w-Vxl?^!?dq_gc#~|#0GU{OGB&*mwF2)J`pzls%{Sy z;||^YTzrdpAiC#8*Zb45)6L0AY$LOGc5v$3wE8!-hsB{%?)MCo8&3l;1*&WHGeSWl#^LC<8=`pAi`z1xwPqV*f zT2u22TM9WwT}$2ueaK-fZbAPH-zPxd<;-#7nqMH~XwCH0`f67!-0Q9lsC1{jOQ!ou zm)SDj^z-jOzCV$**YrL$*4=mpKy}`#ybH}Xxwn2FiYE~DYgMK)l;1tP?YGqZQX#2O zZb5Lqg+`lZ_tWKa3-K_5w0PZLq^loj@01c$dVp8u-?lNCy8_jOkk;UK!t#W>En3Ay z38rnB&2uuH04alA_Zz&9Ykq|M_t6$rOs_eL+?V_#Hv^2n{RDT zyi26^X?|mNc^PkHePX>bZ8z09x+-4WkXWtXilSYqevil)Hte;YAeJY0)%T^cESS=) zATjmC%X|BkCU820??WB`Ap;_?{ZX&iHq=`;+kfHN@=H#m#D>-H-x7YeK3(Fseej-P zN*yz2Kib1H&&%60;&ncxfK(~=3mT-V`*1H5V!M2Bey^>ztS0vU4)5?6tnc`1#pmWO zORhpyJI%t)%14dFbn@T`8rEMwzgIXoZ)J-YCC^O4@@ae_6MD5nPk#g^*z?-J1#q~X z?M$#J>1lYkb~m#T#l)v?PQtBj@9-qp%iY%6(5J?b2O!J_n0-Z$n8|cz+#*+&A1z*- ztsP$+_O@UTuC}jR6#tRxS2WN7;5S45JYMdsT)52_=JtIt3d!zOFj#xT^@-d=xO?J9 z#<0@dXdyEAvFS1gS79?v-yFzxWt9;^RLPq@`ia+b?qaC5#NAe7iRJB=m$~Fr(%#yDucQn{BwLz8aI@nYi~K5TdNL2c>oxJ$9*1(3knJ&y&!58WnGq8u{(6t3D1`5Eu2 zY!1FlaV@`ZGHqIH+D?Qss#Vw@;)*?9LIP!q-@3pU@(_QXuRHdpiF$$(zY9om{n9M! za!*%XwwG-2b3OdZAKB06DAf#k3K-epShPjPure9u>LFi@Ec~e0BlBU3P3pyZ<`_qU z@5*~uCrylvB-1tYbN}HZXT&X z$f(e6J|i2n5=u9$aWy;@(BEX?AJoOzetFD%S@V1JY$?pjvMSIC>czaAjdPIUC5VcS znagZ&RU2-D>}R|A!pz;+=KnlZi_bstKb+nFDux9%U4&mDgEimGIOgCDxc%i=Eqt<& z`F%W=V9TU=*E{ykvHF(h~d*zoKQf3psa5M`z>-av+=Z@5`3aEr)Rr>k zj2fW4zEh6iWF#0Wm+vwk`TAMr5>>C`+sC}G%X{n^d+cBGe_b@(XWlPY9(yNx#CqO> zn610Y*>b0?WpI^nsfITDt6M{;xIqa04~PC$3{?lFYf?Ov)(A}v#zahqFbrAT84PBgI+CPB z^cUR=NgC$~M}ql@yEwZ-TF-Mz$rf3}DL$~B;nx|u{)}`WyTaeLz%`V0a>!xbBeSh6 zZOyQMNrAJs;r1cyush=#Gl2`V^NOOH)Q8}wg?h$yG>Lw2x1!iVRI=CWVy?#(3c%;$ zW{c)!N}M3SZJHk)WZ1Hg*%n_hAOG$g22mXEwDSd6#-#?N9=e~dGvXEd*GiBE5J^N< zX^L&PdWPgSOniD?LEK$10RbNgmL}Fc4~~!Jvtpkua2e~4p*)L}RLA*TZSq6m^5b92 z=Q-aH?Wce)k?a)r4r_}LbuE()q`M=d=cjT!!mi<^R z>#vt}36D7_di9KaC=Z!MLq7eo*oY0N|Kl!M`Ba;seYk1lhW2EYX+pqb=@xUR`t!&} zqMgfl@F0{+chRgVqOU%n=FpDl>&9Hw3vujVtOCBW5(Ve=?)FGkZ@L{Hl&>2uFfGJ# z(2Iu4Bi@!K2|&66{{wSNZF>*;jC!ZDm+$w`vA+DVQ_rM>HKr%e&8cVnyc_`=ku_nHUyfoSSGq+V4SFjXqDE>l{kv=b<_h*NWS6OLV@VBFV zsq$CRc(>QU?^iGOHCFjKtEh%e+1M)rX^*SWu)H~s+0QzsHLs>Is4fXQmbhLd`12Xu&*27=6uHe z507+3Mh$shjUqNu@pn^~K}vxgrHuHc>$~-`$eDG3Fw)tAU$Nd^+Y-R@lU)q+QZ3iz z>kOIarzzc0=^B6GWaeQKb8+9%PdM8J-)9v&%@am-|=72uOR z)r-LIQ#D^ql5u%MFGjIFrIZuI1cXXW(9_l2dyO1i)H;(Z@NQfyV_EbGa^JZWGy z*qo^3`YG~woiHH4P3hpMje;77^%t0393i#qA=FE>$8-(fNG2daoNwS=#qC6+c!<~m zVTua!x4Q3C^7)9@k8`7HOvVwY12il@r8kd$6-F$NYj>B*O0m(6*SdwisaD=x=_ZYC zq{P{RJue@6wUZd&&)%_d(_xoeXr@4?j*ht&XnqYzT)a`3tq;gI6mo?+0wr8mgAze% zc)K(tzt-S$s)7}R$@qSrL9>LG`*B`9=wIJ!yzrG@$2@rF&GS;o%8C6A$C}BwY)DGT zr-rY(6_lVYn0(K)({!>uv^OUiK;CoWg6yL_$aQ*whz>jVK)(L{5$=PFLO!^i$MRKr zHZM=u6RK#iW4-#6Rj!G)r(|T;373K+o8(8Rqxtq=Z!bC25vv_N36q$a14YrM12gi9KRcYM$? zHy}=(FyNynD2^z=M32WL-f+0;s70AQao>q5hmJm4FIR2NmZdMZJMf!t+_-ua03_+g ze{*chJL;_EQ zQ?8wwQaV39dh|RtLEJ6<-1r2u=qP%X8+H=r_kVWPB`?fh2zp8dgcya^rkM9JoC@r! zy!qLsVPuy4_DjlL%Td(BSs+xHl1`@!ee8QO+i75(3G7le|J1}vZ; zJ=vwiVMXW<7XmDVi{hxxVq_v?O*p*sKBti)w1?;r*9nw{HJ$X9`4X*Hf5I1?I<`|jAs`eNew;v_20U63PA$bE)hcG=<_o%`1 z9wwh;84E%rtWyNWq8AzHd#~z_;(cLik~qlvl;oxPtOWBUfG&*)Dt84;?1qMVvsL*T~-9&o4)bh znl;_{`4(10A^=a1?m|hS+u$ZX3-3na>bbt8TSK4CbRCO2U_%( z=B#GPy!XssSsEAd8HKQQz7JW}bv`>Wa|G=kF^Za5Vs!?QTdK61b|Wa3DUWoinH1Li z^2CgmSB6LYE#dw@{4C9ce)lwqDGq^qJvhxCn|kPf+-mmz>~88YU-B#d9)fMicp5ZH zV`iyy1pWT!-67%O_=e3-BM;8qXOTn!_0Tn5-u1?2aoQd{k7dAmTy5`ncZ+Vo8HRLeCzK22Yf(-zqU7Xbz9Inb9Gx(oVmK)=asqJs^VL9Titf%>bB19echgI zR}tMddwaF%ITy>`S)gI+-zS^T(s~zq!sMrm93@nj%r`Ab5v{VR#&un@#?D5&QX1-Yxt^1so|@>veocak2|ktR8Kz7@KvX^#MMjn zu`AjdzUoIx3}5w=hUgQ-)tifkqk3!6@Kx`S)p(=3D^$J59qZMH+wBKdzgfB;Sbes* zS#xouI0;=`QJk?at~&26E{=H}^x{!!GcUfnw1-|izF4%2rxuHLacxP_E}o_LpQ}$_ zs6SeYuVymh$z1$^7HW+wG2Gxq;)@?Emg1S#QfzKhitT#C{aT8{MQ`Eaqow6J=~mUn zwpf~;ruf*N5xsxUiqiY{tS;H5>M83VFCdb*=g8utdyXqUy641pwz%hQtv61$x$#_Y zK=fQHW~Uf&M%=S?+vWrt4k@im!J}4Q8*C zGNI0q!s!-qjVZQPO~gIg$+CE=FDHKR(r>6%I2lBs4! zI}f3z!7Epd%=8EqC9YZItx0O`!^G;H64b10EycRlZH<~IJ;$`>IT#Hu)`+-ft5+p8 z``jw2IVg4tMz7|udL}i+$-d^4p|X+)F5;TAZ5pk2g*IC6zC}~jdoY}CHx!k)_wY6b zv-j9igW0=ySk${wf1Yk%bG>JKJI1~5^A-TTR~m{GL%fK4KVpbR5chsUZ}OBF1~+Px zxc9Tg8L9Vs@C_Yof zr>hJ(tzo#teO9Yg?6Xnds!yA_pwD*iG_KEn-7M&HsI=SkIqJ1?pEISsrjKpyHGMls zv1I)8t!h_>zA-IB-w|4dzHMg3zA`J$PR5D*PA#@i-{LB_?<|>)5Hw8Ox7kq04IL)# zdyiMJzRPh{><4k*72;32!NkOUAN88G?^EuEo*P(9+}8~l;l4YiEfHi)+;?|7Gt~Ex zbo(Z^lMl$Bk8EQ*`;S)J+5g7k zY}5ZXZ;TC2JLBCneJFK^d;{a3Wsd;j9*U;j;RCH8-=jYaOi zrPLz#-&1_EBU+vPkCs+v|Mv{h6~qIKyObCZFjQ6&T}(V6;`Pq~G1o2dg0qPS4D{xP zBzpCL(dYg2M$ge1(9r753`iM@YUnocfVpjK^?>Ex>^NXeu^tDkE7tyiC&X((U^nrA zO@;!=v>2@ct!L!}c9p)~fc?c19F-De(HRDu@NC_HvxdT62>Abxy?2j~s=EG$_nKQS z1PB2Fgb;=hLI@WD5fLdx>IIQfL_}1yOp*y1oXo_T2?V5wh)5ApC?Zm%lv+fjlp;k$ z8w<5E|mbObcGpArK=kyGD_D;a96t1uFRfP;rr4GKWtC>rC-={T&W!YlwzY@S67cI zuxmkTz3l2YJ+EsI)23bf(qv>07tmb?SSxqe5ou$i>nJOyO95WjipFi+)jk#JDl1{v z`Bvy2p{jRXY*(}EsAG9SW7mDO8I0D`TGW*$LWaMC?s~#Z z8C}ns?$Gsno;yNe&}D)pE6e8Hl?{#uU6x0W6O7ZMQdVZ~BFg&71%uM6S$3rY7y!C# zY~%2&>=x6?WvM<4I=4C4{m_*Aqn-Ht)Z3R9Hb`QGj0KF|6 zd=I+pSlU}EJ1;HPE!%#UZmsMC=Wd0jvfWCgvfav^mF?Eg+_l@4Y4KdQ;i-bh+drw6 zTv4~M0TpzcOe1tM=dZePF2I8IgYGuhzHBM)#H;9TE6hvlwnld6-PWh7wk54KreplS+wUn1% z@LI};$Xs83i`}l}RjFZL9=1n7d4n1Dg^Si{e)&oJa+x5?zfX@cySp6c72Wgg5UzWPUH|SqO#Qp}vs$eR zboYU#b-PozU?>fsyW2^!?zdPwy4omB#@#1KEKUQhdz~{^-F=qC;N2I=Gj?B|woC24 z+KCtFzJ^It1wMhvZiVphp2Hhh^Zq*~t*_=IW zoK=q=X+x??(w$$W_uh@EB936a<%CTJzFVg-iV$7x@Up? zI6Vg{$S_n5&^?FR(_+ujRyXZ=ixpWJ@&@Rh45PIu9H4t9>V@8N^%z%R^^+f=GGeZ(n$;4n)u!ce`tU;c{3s z8j~_CJOuMWthn&rdmkwQv7o{y*9^ZCL?|AsZ(Q&Wi1a#c7&H7Ni0LGr*f+rqwvRIw z-*nF+5Pw%((YwX-Aihla`DHIZ1ma?Z4?O7x% z<{)+%*n7ZZ!$6!`@!xyqJ_^RC8|Qpk|0IZCBo00|^F^1#wZsVKk4ABj7!Fx>_AH3SA1)7ld=-cz8D9M7DD^vI zR*CJdxpOaw=sPZS<$mRI_+1#Kf4uwAQF}mqM}M5~>NF5X8NBt4W5q6qKd5oY!@DN^ z$qizQh_S9Z2x0+_?K|v^0Gq($m#Shdh`}*7d;h@!Al9E)xH|YHh(JBIKlH*)Acna( zp=bW5AfC3^ZKHn0pr$h_-jm&4xF82sVZPexNK7{5pi{cgeoAVx^I{Zriu#3u!t z{C(DR5Tjwl+pb&);-ifR@0u|K#1|WH3N>BicA93|et*Wu2|Mmy@hFJZ4kllldKZZ8 z3;uEH?08~m z&3zzd37CB4XCpyO0`T2})2l)38}aDh=kx(K=*M}Zs&;@l7~&7FK34-`oP^^;KmUfp z=|KwDZqI20Vt0(Of1TYc{f6JJ_-=U+g4+wtOQgQ6fF;#mFRxT8hSRt7 zaJil4VYYFR@yx(i?wFbjV(x-dX6AhdPy{6kL+|#WH()Glaet@gi$UDKV`7lAi=IFU4hVfB2xw>Go_H@Zvk2uA-}q86z&P*hDR1TxYTOnyY&P z_@Pu`i*`3Y0peAI3p2;|00mSYKU%$906U0jn_eJl248+U`uglz30b(kR z?_KsVEq}%i8*l#gJEb5dDfr?r?+$=sC{y_S%{wSMFb=DDV!}MGg5{cp-~D#a-2lmr zkldp1ckRx112II#4af7T9gO8Mj_CgweKb~FcvAbzv>&7wUtrj1J# z_Wio$bs%0<`1Rf&X}L7MF8KQD59fl|XyZfkcDz6}^?`;Qg-2XoihPW#Ft*w}@N<{b zDAls-#8-!GrZC6Yfns@wuW1Q3e!K{s+D`)U*27VgqO(EFnX!4d-%JNcu7czmg~v)d zRDxI;;%|TWnA**Fa^ashgy|I+Yju3?tMfFS!xHAIVR++ppSm1oyT*wUXEogLuFGls zYT1wCeRF&9s%Ke2;ot9i_i}&=s8N`#=7`AH&f~C`@28l-n7rZ9+IntJ%Z3XJxBTuJ z5Q|Q{wCzLnAWnez`K&*`2$0+k$z2Lx>vtfVDqIAbrn|51DWnk%YeB;Xg;Op&IG8@} zVKyDUky#x8anix3mkfOpU@%;%@GrN2MC-FL_rpEyi?;%lLJx)8r#Al=h-(*K+iUkB zfN^lU!ppY}rOpgb(ESuXd*i1xqF^&<*rssg9am-fK)W(65}w2D-L_B{6JfE)H0Ex2 z$vb~5aybmpjn_8beeFN#ts8eaylUUNgCIs=cznhuw1ybRaC~&mt+iC28qg3?`1Fl; zaqPDUG%Qv4(R*F0Tn?LHp5L0ek`*dg(Nz2qcdyTt1p?M&pm%;gv(*aX{-`)`>mRSHseNz zhs##ejATsQ@ag{byjikb)o@+rhtvb1FKxCIK6ug3kARr4<7fS!cYzqU;ja0Ac+Lab zwGOmvqtm=wtZ9v@A{MPk{GHnIO3<$13ipkiM0-qQGm2+^_?{LK<9LXVo!I;VK=Lpo zk1Bj@b>&|HlE)$Wg~BmExNfJ~y#^ZgDt!5$J07Huo0#>B&(3{-N`>v9VVAJK6m;Pid2m^F*dw7z6!*)6+fE!HEmvu=^;LO+Y7Bh zyaI9l%zHkfqQamdrf~9|j|~J!egVld3O^dQd85l=x^G;)asBYS7lHV!Q_SGn588D|VfE30SAZDQ zWBlO1c@JP&jN%7{FHjFO27P$!xz}kE?23VQH7NY$SNHS0ZTVm$RyO?yh+#XH)ZWUw z^Fg3tsKRAq`<-(;9h+^pSYuR(AOEHq_jb!U8R6#6cK{?mgyb=Wm;Zei#XZK!4m-CU zNinUl(ZtQaoZ$gT?uX`_(NbZK@+X|n|DR`UOYZhqtT!pi5 zy@a=nmdh%B@zD~bK{%Zvm6VAmmOXGiH9G7A4F?r|G58w_UX9NnHo39lIe_F+NUl)$ z)Q!h^Jgl(Y2N53J!$G@8 zDg5PRe+VGC1(Mqp-a7wgj$9713IewKZ6DgxKm}_s;O>$cG!YpmSZweg;Hla&l*T!~ zcwxQEX}@e)E92tX$Emv;Ct=()Y4X1SCPAITt)b6oZ*NS3vGBVeX@nd1aeTYY#^*o` z!g1fD)mH+HgfR*WUv2pXJ$(siSGmFgbv{1wqp_B$aN7AdXgIqHLA#0-27ce~*C3Ab z=LxsX8CN1j=;=_sshvv{yXIvF= z#0`_br&}KZ?LMCJ^u;Z!`_edoW}u;!!k#^^e3Cw%01c-UF8K1j>j9Do8e9tha^qxL zzFn(n$yDh3>!(kFxZmP5-|uO)fGcU1Re0tff240crPOB?J~e6>P4ULm9-CjgpdW~J zJ1%^#=xY#1W~}<{V-)Tihg1Ah-woTT#g-;V(lfM4O?XRW${YLpGym8)>* zxDK>s^Na)Sxn1G*;Y(fwXaxldS6`Nq4`MWqUH{o{J%}GRK7Z4`O&}ip*#Fs!U#3#? z>HR2NzUijB=*^7+?H;Re@907QMIXbU-7$qzPTj>TiDmwc(M3mEQC-)7hV=@&SM@mo zkkp{TqwvldUp1wuX*y^RjVaF((4OTAlkPq*f|%yxE#)6m&oK`5c+)#bv$rt{$Mw}0 zQB-e?T=C)0fB6xB3o;d+?lFQVPs>6XJ$;aO&z7Ms-uC3wjv&t1_-;ei&p;f8@x-+E zsDV5Upgl7b=3HA_M@_H_v}=vR`|5`N3dBAd-+HWsHUY)}86W>()bByOe6f1Wbz`Ur zPJ)Kh3Ohu8M&sOl3uyNQg(ou$`M`V|XwOcCQ>P#7Paof=$yZ^Y?Vr#aGzRQg{Fa9g zV*M;pg5NCYcRzLF6`(mPbR=D`uWK>YB3 zy!Fsy0DUPq%H`n-9@t!zmsA_r0LudrMnI zyGud4d(c0Qf_9&9+UpyeY>XDSrhUHg=*PSM=DnX9q6cVqUxkAYJb5jZe+abeh{At; zb`4EqkgY&z|X@C^l>?8Sf%i?NrR}FTq8icMl0N09L%JTM?t$z zDE!5+r(UC?R)Y4dR`~lz_3QKzL3?Q%nAtOfippdO9DM%uvS#!#8#Lr99Qel=4NOR| zgbl7Zc?mE3Cn+XY_}I@T9|K74gXBSl;T`|}k$yTIG|W<%J0_c60vw`1PT>_9MHGm; z>p;6FEBs{9%QTC6HnQFaw*T>BTAV#wXi89c+2PU801SY^3XhLoMuDR@8?-l9VW%su zp)IYkB*w+R*~;@|8E8*0g|pVhD1J3=uz2j@w`lMgqi4MD$ML&ePK$iY$Q~EY`;f+o zYXVEw;P1}td6a58hLv3qe<(XfA77!sQ{liduhOOn=F?VLVZ*y`b0VZaX!jt6p@-V> zX~_)GuGtDbhcBl|*FAuCx(b*4;m8{LcoMYxv_j7%#T%%oA)w)Eg|9E#eg|Et%u0Gag0Hhz0sPk`h(NTR~6dGFCK*_{vCU8wN9 z@5g!&A7O0%P?IJA$;FUdrf|qL6*MEeX0r$nX60pZexeMtx0k|L*9jC2z+&oj3jZ*s zOC3OREhIN6+;i#9V?J87L4#JHT@|2RHPrV@K)cK7AB#Y{m(q~T0qx17e^h|>)F|w7 z<7S%pjRiE`y8iC-05PBiht8$Iq#t=@b6#f0t((C!+A1v`A5>DnH&tyTESRnPJ1>ldJ1XB0Mkf5n~j ze#3LPIzrP}z#);bl z+MTU%&+Vr!c~{tZ#}9AO z#}7fnF@+xu9nNRN=VM3?Kmi(A?CjTtJl@y=1-#dZ> z)!RWsmBNjGI;tqNS_0a=oTl$;(4Mfud#~6#0K`KdN42?mC;e=>C1R#MVbGqK!kj-o z#Z{^S?TRQ|-~MzLdb&K&?zRe(xvz4@Y7J=bdWB>5G|*1dJqWaWs6y`-_wJxZ+)PoS z!kgc_bT&PE18Cn2h0}(fZ9^ZAf%ctLIB)tuhPW&O?Om$yncT~1bvFL{*rofIeEe9( zidNX^iov@9lFK2vO5xcRV=4ai^at%3q_E?`Vj6cDrJyrZjW4$=PHy#cg)v%;2-jDM5f?^4j-6$;Pp`kb06BSI^K!n|*%_jN(? zC?whT{%RKI!uwkMoGJ^Yv`A7dW`p+3Q+WQ74V?QKN8McEqSKc!Z0L5H?i9}d?qlwh zt3i9$DZFRr3%LNvIgp&Mu*@#EHBe#8`=8)U))0y-6kam>S?b-M8B}Y9 zCt94NJ%?vCXwN!@ulHHWhZ5sKdn*(!?LDyrJ|C5S>l5uT}VmYd)e_##IK|)k|TY(cg6iNX~=g!wO%R^gQjdJp3jV-mtfX zQz1J*dtOoKIsFxFi@ft`)G3TTUq!v#yPMVNuvgDlZl<=NH9S*c_0POMy3HKWuK5Z_ ze7kM}HF;Z>Sj5ilKJfxHgH{T!onF&~ZnA*}zQQkE-R`AgKBSSZ@bnL5qv*N?pxuiV zhEHtUOCNi)HY2{&yf*@)^9{$TOL(Yn`V>yT zVpky*5utl3yy(_4YGC(y&~B}8!`_7x>3#*EeSU=pW^eM)x7DECVTH%8InFtP@vJ|J zkGkiarlOXCcCA$S*5+lrU)~1VwNv5bhy4s9IYrB`!WLBl+L(KLgZB1U*sph&8T8{k z(5|)$pVr=A1LQ!S!oK&v^aen36C}4PJn{aqHUP;TkbFhqgzuU^NY{*}-L}G4kI!vR zAIE}rjaT^Q=My6IaSf|?;0gHj=tj<1ATUYcD}-97UbdRSnt|R(REyqgPRjz6}}< z&_uZICTgU9puGbX?#w;KC`k|KjBJJ7_Wh08EF%Uwqe0;{tG6&}Wil)N;nIzLy#T{u zl)?qS^HC4>max(!KJw=0e)<>&?TslscW@QOM!sR7eIpfaAF_#}ii`qUsTICacz0W> zQ7h0;ps;44x|FWnMWasP*PW+*4^RT-3dcV2_$@SOeYEeQ&NZI)T?*HApH3l-ZwIX? z3PlfvJh?z99Tc@mPR6;>RZO`$vt1r5U#-o5)820MNa z+N~5uR{foVYj*>yo8shaSO1wRwwYBcF}itGJ9>(dpfkoOT(f!De)_l+bjAvW=ez!+ z6d<`1lCLQo_EH{I!4(GWiYff)=aDx_vcZG{@{xdy)MqAJsMGD^y^zTD;DgjLo zy|8WFivY<5kX)?r-zSIACf2i;r9$zd!RKj)^!h=2OBFu8^wS+4xEe+%7z4M^GFDEr z2(?rROP^x$%KPYjx?`-}itB2RKLZekn8J0BccM@NPJ@PX3WK9&(FEnL0qu?`yy56I z^geyXtfh%3ORl0Q$u|hJZ>Yjw-@myWAh{Zn>(q~5tmg1?E@;;Rg)ePeO6!|z0qr~# z?tf$6TB`6V*7C#!JNsM1-8l;HZ9lSGtll<3f~;Khz~a+wEipnneTZ%1iS^bZ-TocSIf9i0R1LxsPco-v7v znobX)a6|U~i>auqLBj}zE$Vt)4Uk*}$)yUrocx-hTxUVU_XF*j4n~58F$(|w$cEW&+Wmv}ET-8SX<4VBW&r4n!3y_%aTV>+e6vCO z<|)j$f8GZGZ7I$il5;nIrMi$^ldR{Z<)deRz1oAUk>PuJcTDeJM~YhV*$0BLjCSHwA5$x0-e!M;l3kx zQh)XJ0`2Rk@ROT%bB=c}XxOiC^V1(cN>5(^+T~aH;<|+g=-UR+Fhk+%u|YI~ydKcr zY=!IEKEyDBBP=P4Z?5=UQ`E2+w43n_RiHhS6n^~GAx`(^gZ30Ee79lMOLU99pk4bF z{%g`D^wC>H6TiZY8{T1X+bY`nD12`5Xxg#+)`RwKQuw#4=QRbG0m<14kH5F;aez@U zR^j$BqdQSIEuf%V;lWo9QJmzP4%#ojQB zIfWCqwtAfknGf2%Na01l?DsXT6N^B5_>gWbMP3T0Wdg&8#?UZUcA@v4Mk23pj|U)Lf!(}y`2i*4chZI_1)W9VHn>m9q=8%SQxKx<+vb~ z;Ti_oHBw>!DX;y7K8^?Ns!-VakNaq|=9&-MwMgNi-miT}Z)g^1&s>GKF8W{_-9Jo= zw!*(WzU>G=8T3*(|IrV}(&W0C;w7reepc1S_c#3N8!EsL+Ec0!rZwTT$tcj?u?mkK z_>L3p$0`0-cw1qUaRA8^kUT{rnjx}4dy!0q`zf>qySnzIyseEnS&^y4|8VZOp1doQOc**A^#!STTY zcRtnV3~1MRg;xz9Pb-6WI%w}Kg>yS};iX+CP+;bs2HHJS;p%6fVUYC!)*r`~Jp;6(a1R6R9!X>5U7m_L8O!%z z=p!d>I2hy26>onTVay10PEYeeEia{<*4ATw&RBg0$}>8f!+nOI^4!H}Nrne7Z-Y;U ze!TV2W4xVZmIUY07+=YU|BPwnxROEid;-XEF+=@%$HKG&MlSF=#DoOvCo~5$z?BbI z8En9$AU>UEFfX51bIij)OvX)c6wOo|?lzqAVbT%PJs7!SmAANQr$6&D$n-A86!CtV z&tmwvl+P*nY>vZzrfYHZ#hEQW|7FOD)fk<<=^vs7`9FJSq{$Wg)ItFm^ zEWn!$>2afGvqjn5x0z==1`SS`+4)l2W-rMVZH<>q5n0oe7w&QH&fjh+{3UbK3L?;C+{s8CBoq}Bb6AI$H;w-Mj0>4roNC~_HXr759LT4!oWN!#51*a!y2_Id z&st0?WK3UAmGb@1+D@+_@OgKaN7#YHFbl#KkI?ZRH ze164HP)3Y1agA|XJbyDNl=(%RgW}njvx$r)WgZEG+Bx{;vkXRPaqz(#Ek;Xma-JEN zoVsGbCFfi?jl>6w4EJR>nRuGK0^Z7(L2B8|K__tj?Gyrgt&!gGqUeqGHY^pI|ZMs;kGUkKP6b!FrL^hvsF%5!`w-{W)G#U;(8MMg= z0;?egEWGF`K2hQn2&csObb}B1IE%jxn~( zWMm*Qvwt}vX5uC1%J?vm(XV_G#~cLC3vdkNXj}?G`ajuxpN;tv7 z^fNxy;IjH%Bx-aE@j)V%3p&Wz(J3-X6;adh^ZLQNN0tKwaM^WM*CR6dvH zOdjWI85_b75QZjmnu~c4Of6*KFjJQq=+7YpqZ~PsVe%*=&6p0vm=vZU^7e``RE%Zk zBnETw7$v~zTRtM@11e_6GC++FYZ!yWM_in@;gfMb!{g|N(GHBkX8;AmK^aZJCnXFd zV3HAUCiz63S-@74(EgZnh8&ABSAlbb4AEjtF%vedc7Fcm9X*-($s{V~5OKbmb3c4Q z&#}2iIB% zVe1UGW>5(8CwU9X*nF$Qo%zQfpJL<_6U7+`%qKv+{bAgY)rZcm7~7G7+KjnoHaF)= zIO)WgW`?UXu7$UaEGNK8Y-X1+t(zJC)!bDo8sPgHQ)j=-$WxubPe_|Ez()sK-iwAJlmYHGt?{)Pm#8Mn$AF+P#A`fls-%a<@4!s@J_U-t4t zd=SlGMh4a}DuqGioTcM@A|nf!V!<#5=1TD)AS0g`1IlnaCh0i3_lmmA4ZO2uauE|r z`DBrCeGErtG%fFT`BaN{Hk`#`Fdf55`B;V%*-Rv0J~3mLnGMJcWj-6`BP>2f=Yu&; zV=&>A;bP1?X81cFy>dc{cUYV?VUh=f9{E6$)hHNz$;Zr&@iz8_i+l6No)fE#k739> zpS3U-mZ@C4U$#bL=ywwy;A|+9x|r<9hsuoWWnM1hym{}=xfZ4fHlAAcjhqzcT{jb- zc)Q80FlHVx6_xR)%-XW9INxexPfma`ACR%Wj6mQsP3EX`!kSU#jML>^HPcx6^w0WY z{&yc|G6sSXiJTVWtva8ZG8KgZ$(&DN^c1r@7%0TR5XWUT3)Q7RPIz@1b8|Qs%dj*S z0ATJrbBFoRg5^J$yT*HT#$hqdk_j42DrVF>6LuLL%$zu8{&HS|QP~WCcf9w(=Vrdh z+*-$oTr;HAlbi@<5EV1hn3-s`>#v$VpToQ(hJG>sgULCZ!(ezjr#u_?_#2L1<7XiQ z1|KuBgQ0xP7~zv&M#(tl@>Dpw{ZrkEb27}#;-h{R)nKkG6GJ!y$AD>O@-lyy84%2O zQyrXtveLsFQ8#H=}n@p0~e z5005^!aM;!i)M1J6@>m})?>3cO~_bo7D-|FI3ELXI+1}VOnGOl3iAv(Z^zUl#(pvU zmg#v`?N>kh%wtTqW>yd54>`}zXYEYIV{i{2sai9}R@YyfGP;6UeO4*)kA~gJ8C*`$ z@F^LejWNfGnF0*JU?L`GYgxaAMHUz;&lv@4=6m#?*Uw@=G0WC4m4b7PoPXu)5$i=T zXo|sG3|?iV5F;Qtv0=S3{q8rt|4&B6&A)!9lm&4ZL&ZdM&cHbK{d-=1b_;7#aPE|) zGgz#HneL2jV%icX#yQ8w$LTEK!Qge~nKR~>5ko9Nz$e~}n_+e{XRNJ8yk_;5Ud-2F zY%!;}m|(^72%NHGULLblSuBVpf|$V1G9HX& z%#0@ssjc1bx_pYs3Va5~)L;fRbFz}b2MiBk20kk^Fnxo8jGSs>+6RLNSj~c^Bv`+I z6XHzJ=F@%V6ElIvQa0%g{S|}$8S%kFA`CZY3NqtknfA@8SJs(ekern|9tI;7C-qsG zgULOds$tCzE3rHl#w+H4TlmBAP@z~ihXJh2cV|c^rv;g-%+w-IG_zU_pR6(}nss?t z5rxTJ7L?Hfuv)nyBZ6eOcLsT4a7zYUXmC9S&1O>NB67r8OM?kF*hxd!68#qj%o+Bx zIdeAR0R}E+a@$5R$-u-7CEA?a8~UWtj4)sllX);iE`#nisb53jH%JqM6fm$xlUFum zWJ6#xdPYVH$w0adIl)lqOmf2jK}_<+Fmnwd&(O_nP?^!bvG}?sv17myhB0ZFc1D84 z(DMw|&7ks)q>L?fWfQ1u9+2o2Nv=px@S+MK1P!5P2x3FnT7qpAl%aq|gz+n2KuMws zrCvl5BquGN$%6?0TJXM-h8GPiVg3oPQqYG&kq{t?(8?qS zFBC`N^osO{q_l;2E)YoJo{Q9yzyk$YE#N8v$BK%JkPJnd$sv+tbQMHROZc{uQ5EK@ z;GTtOBM4(jPzzL47&am|Bp^RY0!o%dQZ1r~CX`Cyun0*(FqxtPA^Z zqTwY3KT%*4+N=oe33OG2@I*UC^5y~?62Oq)ZbipR5Q8G`<`l6K;HE_gk`%lsKRC3| z3>-lSgF?p>jRukS5WuA9>Ii01Sc{_3B*+F)1`w*6gL!2D{sLnZ?7i?Y1YRddQ=zj6 z?_EHzj(ophGYq}T!2vfkH&K^zir84JGJ*aI;!XJ90?`okn~*DoCna(XQ4t^#MRDw_!UXQT1gfS}QP!TQ>B8;dbi3p*{jELB& z$OAdS2bM;H0}Ej!D21~w;$p&b6=6dGZV5wM)N+I>A*z4ENfg4E0Mi8;E83hwkr&9Q zuz>||D@+o>>kGPAuouF|6r~`cdWv|3@E--=DHKSNNE0}m@CroF)WKgB#Gf#s1Z5*i zOu~K=BD$!jh&+`DWEw#|(YzE1IfsgxVeJVE#z9vQw35KE1Ttr|Aq8I~pcmoq38+cr zqJ-D%P#Y7~Bcrn;il!neBx=Bdk`qBz;n4|nMr3V87R(_#A!K=xFg1doLNXM(v_p%` z5Ew+o*8xg2^gLw7eKBM6$Jz*LTN%y z6rBhWHxl(YhkmW$6N?0fXu&!4IxL!&s5A)7R5)j%1T9o*k%1MFRR_j81*sHht|+Qm z+8`oiEz;P+OB6jsk$MmT8Ic4RE|~+hY0$qS+9qOQLdO!F2H}qhJySFrL@q_NJVf0^ zxMPA}7okWIQxzRC2lUs7eG8pKWI=?cEuJeTBb1UMR-e4+oG&1;*f&E6`@N}>K4AGNJomSs!&)&Nl4Uy zMPW*i!@~9wp;93q3ij9up|-RH1rIE;i~@)i?MjD6q7lv&#T?O^bYik?b!0=~6jf7E zEfPMpurx$@Td25B%`8iaNU*~q*ehHI(bW=ut+2nHdMrW=v;fP(AQ53ck!TW@ndnN0 z_MJ#62z_3ZUW9Te>^Tvb7g5#a&?0pqYPO<~5;Zc1^0$c4iTJ-r9}ANwh0eDam%`f>-3t*C6;WBy;S@D) zk&hG=50Uc|y+xs>iE^1RT1C}X)HTH>K?rrCT`!tbLh2VK7ZD8<0+0wMh`gC-F^PnP zlbK*i@e6HF1U!WmCLRnTk1TqELf#a;EfIqj8E6sH6Zu@>+6bRmgzAJzClpuV!U!r~ zRK7)aM%etKl`LEj;gSh+L=?M3v`w@s1hFr|3xd)Y@{z;B!H6G=!n&}+L{Q90{IEpX zMK4AKy+x^5WZ*?AQxsf88AU{8Mb%!E7esGUL}Ep`MksT_nG&rAs>oKHiVNMExMW`zxLn?xPqLm=J$wIIe+ZFLg z5T!dI1&RQNXr+oim^jghLZZ-!g#QWnIej) z!n+q5v9R{UD@3&NMA}}Y#zd?{91w(bC-NO4v@QgC(R&gOy-10Q^qH`5MF&=#9E4#k zQotew`ZACcf zr*A`%L@<)@N2~pbno!(d6|JnVr9Xt?{zTLtPXrRy?<%8_st`Ns`q^~XKcyyAS>q4* zjZdDxHZav+LBFdD1*`n=`pO!Aq9%|q7aMayzhx`vuMLDEi9je4_s65PL4Q10TM-UK zDuYxHsc&U8l88ma{^;ajjEkuXP7a2nb+uCApxk{*O(-1nPYg{CMJDnW_3>aV-uPGa zZ>t`ODE-18h*bGgrv7Qa4MqF`e{D1t^haai3O{vrirL&sc<;R&c#%BcCL0* zu*zRC^`{$@A0!wHO%5bNlY{2L1Bst3uQC=4P+5tZShRj(jXx1=NYn?yX?6A6p2L3Q zHteV;zuVM{4^{g^34cu>?vF$h{)%8QVoC~DQPa^E>3^h_t*ng3s`!mh2_i07d+twe+b3>#>kxh841){MWP1+cEaZHN^2Y7oR|kVu6$1&bBb8nmi1;Vf#}lFIss5-{)rvqo#1CB? ztO^DEmC<;@UsYchw%zxa5RN~0T035CPv zHTYw}JL^L+de2e0X(*BEXpNe)qCVk|M#585Kd7ynfgdc^RcXS`(eZ4K}4FXpL9aU^4Em}l|k!y(%*!c6EA3gM_W+$P1FQyFC4U1qsM98sf)(>A>x5hRij&2t7#<2 z^KBsFPq~frv{qkBf2RuC12z5fC@-~vNpx|nvWCV`Jd{Y?ygE_mudnx;@++oN54Rrs zC%aD~>bJ~t|GVb+=~0(j8Y(zGNYyo1C13F@NrN&JvHMS~E*i7uYiiio#6To8&CK8a zSdi8x?o1Kq!WwJ{R@Nujah_&yx)uG9VlbYaHuk2t&l%?nH7Wa+{;s5MKINO z939CY@W+F7ff!8=wSfq&^>y?hH0g3@{SWmrAw)PBh=r$0Xc*^i97_1BLe(4+$UwfJ zO{=V~Wz7#x8*4wi5KjzR^iBe!vs7K=`d1#06zHEg35lY*7hepMX1Ii827 z-Tzyo%W?a_f9yNXS3}Jo4pqcBT;#AT5uF(1hTz8yvFUeZTAy^j8>)C}6h$L6XHakw z^-l~Ye!8_QIIOCzqn^g$va`XZjcWsyHK7QHELGMJx0a;F0X6;pCx%a@<0k)Ym$q*h z;_(p+#_MP)rTL?Ay`5J%BKZGUa@rTxkfGLY+)wRttZo{NRC46tY~*;OR{n3^(Ep_u z@IT$%FOlwU#YO(We~K)td7_<~ewW5$p>bi6seG-q@I<3=tC)Cw9WCqs@oJun?CRL} zP9JktyjByPB9ZO|L$=Y%I&oqwIFaTfZVao%>Z0*@s3JVopK{tyjq?CqZ`Cqj1))EE zf$b2V-ZDQq=IOx#Rg;6X-)vMxj&F@WJ>8NALUc;R-`{%km9bD=;->~VHD;I=ivV@D zx_XYcY4PK-tiMO=6ZAzYGWt(*nO(Z7 zYFryi&_kv75OacX;p!6i2Wu;WRq{jL;RPeKqlr%DkT8)5*4FVCwSlUjoC%~qDj&PV zssl79O%72f4%+*!V5EuI7y@`p}1uJPe@z=#d(U^ZqjG8}Uy@)u^qJ4T_ z(B`S2CeAM}QYGclq!w(biv{C6n}=$x4mX8o+(d}ye2N8W;`hiXF!tLN}u@WN|& zKOUp#JP|V69lB|CC=sC>@$AbTGgMh04#fO*^|89Bi5G*Bs{gl-{p7z3&VOqH@tXg| z_xNwmfBW?AeM#zX_W5uBKE40<`S1S||BX)G;|9pOAxQhG8-fXDU3$PbCVEFO5@*__ zZ&)B+6O1$4GV2#L!OA=0^|f(kT{g2nj;7!x4#{~+sn*H+T#!~ma<)?b78kPd?}Z$h`Ho`cNcsabK>YPbod~=Hx5}mb()*K?Rxi0WxGN$S{ZAP@o{w zKKO=m#lM9?3VhZ9I9#c~6|IKR3OqFXz&Hh&b|eWE3S6N$Bow$3QJAK{%Ln7kWaWbB ziLz5?MNCii@*>UTQZH}Rm^}&sriNyi*^QZ@?%Kdqrh2+6f=ui5S*PgC=Je1+&ooUB ztvJla;0tQKcrX^X zSYlkBOW9w$e9rP*&E)Sz3cRK*T&32#Vmg((J|48#Q|>^d%Hl|6r=nAf5M|1^J+16& zdhrp9Vgvz;PvohM#)1}+$Q6xQBq2{Y7>`?|pe#ufSiGMMlPs{fI-a_4ecYnkm>a(4 zsPx-*r4Q3>0(S&0+Dmq76JSwWnx^f^EG|kDM{>d9hoqjv-Cu#vI>5JR7%weuArFszn|!MXgEilNeFDP}mhpSg>GsO(1TefZdU3!ovD` zDuTg?1?kPS&zUUHE_W@jvM=-cd9MO5r)exKqE+c5 za^vHT%C;N9=i*LaCvhxzpSzmBDzdNkl{#O5ua^S%)M&khi{rcdu9iQIFmJ~}cUc{vSaVahVh7Tm&$S+!fDAjg>)vOr$eBErxsN40h|8q2=7)5<(f z&lL<>h$O4lT0y3rLb9MnR*f_A6}T${5eo^FVe&f`=Ep@{H>1DZG#P^wcsb2vVR86{ zXN*$d<&=|!r}0!q;|U8S)5MW}vXC$tCJAL>RXm9hts)tV?V?sF@L6do3p|o(=Vw4? zY_tb_#x4b}cocNT9=nG7#xDb;R1D!e9`fR1f zKxfiiNRw9P3I$$1p#Yuv1pP4%I&*_HQDkmb;Ik5Epfk71Oqcl@&G_~74l?)gP!CYu z4$Ix8Z)BcQ;Iq!@L1&&*kR_+}ptC&uS_8BMWVMyjvq}`WB2my;z3gJL`k5}4HAr4? z73i#?_SniAZQncV76qPAB3KJLtAZjXKJEdX6;|N0l60W6C@SJq9q6pNCZ^F@i_J^S zqA^h&q&}3jDy@rTtus?W)^-J%b~+Dq)-JjKfzOdTrgl;fGt%QI(t zc(B=tKhW9DOoz=bu}jV_my)yl+I7wzq#(zc76hGrrMYeP?Go4N>?$dyjuwOfjf(7s z^gvK&&*B;dso#yAJExb?E#U!T`Kd6L;~4+?Xi?i<7HxKGX3!g zwZjzt=}85dcG?ki_E|fQjDv3CGtb_nm0js31qyuD*)QlO#q@fjv0BhgdfPu8C_f#N zwnjD?=3FD2++zQJl1zI|>YUSF6Ph{A-Wzn2c?z01QkkHeEaTYR+Tw$5vQge)lU;U; zG})^lOH!Pmn;f9Vpc$jdN&Eh1q`v1BWZJ1u&^ao7_~+y)$S`S8&^e`cv*z@t1yoX^ zpmPRW0gs&+1)VcS0rUl(6SgasQ)j=b914{ynH4KI%m&?;a<)`1sUe71$53?dUvsCE$F5my9t`oUcgHEfo__wz-J}? zKsPNhD_7ILsp|&FbwktJs_AgYRN3@)YZ7Z3cD~-Gb!qWP)0t*GH(exQQq!g8LJAYP zvo>8#o2VLogH6}-hKlF^rd!Q~(exE-AemD@&`tM99gn7U$EGKp-LWYufQvyl%dy{3 zvt0WPHEXLNOVXa8oB3%n=0q^)X8ja+rbg>ypqmZl1+k9)I>Pj>W@DvyHyiJK{ATtZ zq1j}9e{|7w`!So%PV3su=1bRZwnFN+hIgUyV2u83)`@1@6=X?r8FaIq3Vc?d1>Nj` zeM{OO1_RW@%}!d=c!a0%X51%ukJa3xAj2e}K{uz}QcZLU=;lRscWYis%Wn$(^ zbByHLV8N>Q<@ae*Ltd2v zr~sXpu+Jd!8swKV(`L=QxoHjx+9=l7NtFK%TfhR9JyD}EeDu5zU3(UQCf~Q z8$0^?b~DIZPO<~}mb5qW~C z(`KNS$Ly)8kH5=wbg%H(I(mkMSyOV$69;@Z?47MnDFFJbmMN>XD-~C?&Pm(1(iN@SD#&qWszJBzD=(|{ z2>Z6J$EFXx*0(!`Uh9|w=m)y>OncsMJ=YN|8pDI6=zHa4DvwC0tf<_IIzsP*CChd*puXDc9 z{O$G)_R0v3qYGSa*qvZ zEzxF+vnAT>wkFm#hiI*}@hG6%$PsdzGZ%F4Hs8x*wv}B_+ad)S1`-3hZ7J{2EMN@i zw*4Jjq_)FM>$jEJzHQoU-?l1MenRHywlgkVXxh%TX5+R`q*uS~8hb)+yUm_r+U`ng zhqikfzwovnrfz%A+JlE;pxe;`mj=86-7Y7stafG2Hf=Ysamnq5J5J}@RoGA6uFkGo zyN2}gW?oRjBKhgs^q+1_Eg9|PP`KUO&N;H(LE3$#z&xPaov`arpm=8{z#h;AnWmc- z_#K_Ipp*i{0H0kI^fSG)U`V6hS#WjQcqkYzEm{z{U_2Bwn66x~DAknN#%!B%Ip7woa7>4Maef5DN|G+l7=f__|Z?t*E$eXi8Fy`9_A?fa!q)9nX2 zrs?*h6u>2*+uv?abL}haX}bL+1wQLU2z2|HH8ZuJ+34)D{cPviW&36J!BYFR_LA9t zqurg`Z@u6t+V3>Osr_M@;oBc|j;HotNY&7;TA{~LwZa^`{K5kJ?uEtnMTO=3(`c09 zu0mPI3y08l$bvk9F0==A;W(*9VOk2Puto}OkXp>NW1GUc&iXuTHC5pXsm&_6?b=lN z8`H|)<~UI++@m1Fz^FhM9+F$1lv|%^v~m=FF9mnVbr#&Azzm2E zSEhPe2a08EL>1@`R!Ydmo`CKUPFtZn*n61{i+TR1*6gs%v}T7Vtd{6N!K($<0^MPK z`VF=v&X{&WnuB_HY^4g1fnAN(dpZ$`G22umqKrhfmL#4@z z#yXpzHFT3j@qR zcg!HryYlz2kbarTBu`%vxPc_P3v}?Zntj7Sqhq_ zLDWEZqz0)}0g1Qt;4zz!<3^adg`1{jRh5qdd;8M{3y;exz-)CN`{{RiEU@Q#Ue^@~i2doaX z|9DzI@oTeN?v!f}gHHK&O*<9Q7-Y~L=uXrOp$Iitr+!kUPJ^9Q>NG5MG}mdI{hB*f znaAuDZgeo$DJBy@r@7`ec3PAwd}&(Ys}#Th(496aXySnIf$p@~zIJcw+Wl$QrXrnA zr>p_m>3pL}q!V5+YZT|1Oh<7K1wIS-2fDbgf-Hgmfi51BHn$Xyk-4QX{ga(?09X8)$^w(YrvQt1r(4G5IWMaV)L3bWz zmpRUEfX);x1gTebu2$fwkJC`=95HRtSq?Wk&uH{oI?uV_wRB!CheMq=+wIzUN2>32 ze$DQCo$XD2=fga07>Fb2&h`Y^`JB}sI$!wuOO&ZzNrALSiQm~CCFKg>GSDTG@F*Fq zz+)Yfl-P$nC34axJRtS&3< zAFQ?~sV?i%YPZP_?7Hlt#YOSQK>W3Ofoc zU2k>M(#_PW0v`ok$`C0FL<+j}ZK==^d%%^l#_iW^S_vX+&o*r!dadFJY|C#NAu`M; zz{^0C)7aMpM9GSsK|pkt#NyVHWM|t*hdc9{!t?ae=yb8chQlFh`dq8ZI@bs+M!Z0rT1mdF-bAew+| zNCG0mBo^Gpkvh1ECPnewUUd4B;|>e(i#9&}IEd;iTgQRuv9h-oh}fU)*=*bJ4u5Fj zVGtoXd?){re}E{mv7HKt$P}C50$Y8sF)4^`ftd0ee{I{N+)}}1_f#;J4eToDuuflJ zyytrmQA@UW0?~bA<53W+0P$J3G-uiF#9?oI{*euA0B(5?uy-YhvN?NKf~c9Z(HMy0 zAzMCy$iuNGki)EbeE+j$zz!qqcMHa=gYDB?4m$@UBFT=eAYKmahzjCQ!1kXYBK7RR z38LH120jLXsH)=pUyr^VQl|OGR$lsVhc$WgYZtKhwCI8^+$yz|J?~H*QG<+_2Fx7#kKg z83yBS!Unn^mKJO`>+t@5L|$HWj8l4~wpeIN<546TMLbZ10d0*wqc6uM zsUXtyY@6wFXkr`vTQ)3&lpXp)SMFCXhmxTYyA$_rOIw@06WtDB?UabFC3VX_jUZyb zY$FGvQOV|DAexG7M-9eMhYe9d6yDMO%b)cEk(FelB@nqc_H+di$7fSchXeTom%9B9 zU-Qp9kDLg`K!?40K^!F5X%s|OoJ}4!5JdNs4e%TW`{kiCJwcS`*wh=?v54)Z z!M3ttb65~neD<>h5k_VcMG$3IHa-V7b`tk^%c+C?hC$pg*zC{c(1bQ}r0lc@BE!l4 z;9xwD*jO0EuHye-@6W@es?N24d_P4`%=2I~Z5l-5*kFq|M9~;UqaK5Z8jYd4dpBKB zUDc_oW>DjZhTu$$QxuKIAPzBzI7AI04$&y0ap0&S#(}6oV~lfhmb-q}y4SPs+Ev|n za^ClM&ig&rb@(HuyQ}uF_S$QBp8I}2Knx3^6~M*D5c3Dujm64nm{`CNc?0^C=KSpch72tWXzG!f$uXa#QIU4=E;LEsdYItB_(SZfpX zFJWCnuw{is2_k(;?#ic3G0)BpeC#-2{M|n z{v*iw0x}r5_X4OFDD(ov7P!?ydJ0g_LVyQ&xxzyBps)$+P6FgF0F{AeFFFi;RRe}bikp7$PN%hh2@ID92Ayo1t?Hhnbq7LR)0r;N&`w?zzG9E zTFC4GyjVy&04rEn*dJ_PVex#R_=JU3&47NXCIRaVRD4l(7YOn~E)`hvLOKsL?9uyerLJ|=u=|YYWnCC*m4=Bk(^Z^in!s4vvl82`p zAwal+P@9NXb$9i?L8TMcxHNy>^M;M0)HDF;g~%y@(uH^*fUAYH4ZxU%6a^rJg>~)$ z^%Yiy2O(RC{s7-rSlAmNK4FPa@actwD3H;GTpeJy6LFf21B{sSC-=2)h}L}v0$&KH z0?k~=r2)QN2%G_!TSycE7F)>H08v+18XPP~VWnPh8- zCFKDd71nA76H{2(*jzWa!9EgYx&g5+}rS_TAYJPl;5W!}W<0l*}#BTwrFT^UD{5PK%ECdh$Ln$n%3;1>-2GdcT1hDTy zPL0{|@AukHsq_JGe}%=#&3C?k?=GU8L9?dM3wsH%aNyeufj}T?3-Jt~XA5x)=G<>w z@b5wp0#KsDqR$|P3Tro;AJ**DAfQE>Uo4yZkSMSaSbZUj2#j_iaK*ek?&HNmMgq{) z!g~F{VG0ZEnw{Ug{UiZ9(zLyL{WpamGt+s`({>tA)D(!&!ZPvzc?!!81KKGpBWz|I z-fSm!B{Z2^kALg0LMjtz@Ipij@ZdsT18DF<6bqp5LZS+&=R))b5X-{4?%*8@i=Ttr zDy+6`uINAEr{d_9=Bf)$?hq36%pu=cYqt_gzcN)#_angIz)9xrm*=k#a)QjXAKklL zNDBh~T38R?Tt2Gv*P@t9bLUg%*l#Lj5d3{1bO*eAA=m~md?Aen^m`#@2HbEV@BsK& zVZm;IWQAqB&2v|^9V&o>0<Ur3z;Zd+LYAEa$zseb@rg=MJC_o@e+C;+FLWn;hcu8>t|#ymds zJt6AN%zR|XB|>T$82UnZ3*g+ss{iKos?JA65vQi}7t6m81$vr0>Rz?ejuI%%J?GwZ zoDgDXHl8)rzVZrPGr_Uv+KFA+MWDG0krIF`3&8+D1Oy(o;y%T zJ2OxA>aaUfg2WW1W6th)(~g_U{sE0&h(!VSUI_mH3|>f*Fk5F;A1a9&&-6Q@!;Vpk zJp+e75og#qx`}yy!q@HGtGpSIl7+SGK?xStItTbySdQE*>(}s!fOKnS+%urRDAv^M zy7ls_g~(4cZ~9(W3L%l^zGWBMSyPFO=H!b`LT7fYM6>k%oiGMDUYhAV{?pHdXe;yj z%zM`+Vo)U|ses-t#6_6B{&AQc1Ql6o2L8GJ4k7Z>Y~1JJtu-+kBFT9rvRA}>Dsg=r z@x|X|ux z{=fDT;<3z@Pk*;gh-xyYT%5HF8KvQv-Df;~kdWqJc6w~z9|%au=GY-S9xRI9HM9Rc zVOLSgvNQ*7zQyhgl>}$zK7H{dA->I2zjvPPr%G2b7mhsscmamk+|qEK9mtgwYOXor zGrLn$#GE)yf+&1HbaUGenWVX4+9FY!Q-GP1S*4 z{76WDGao*4|9OcRGfP1^=EeFCRtbSw=FAyqj}oG7%#AZXvNxr?74ydG!?4hIa1$V@ zg~jmA$#;>4p zkpBWSypYFXsz)xkJ>j6zAVHg5Tc^Au$`>}L-&Os6A)?OQdE{Ak#8w2A`NmFn+vOdY z$7a=u2imDk381EG%l=ppI82@SyzQVFLeiBv;*55?<5YN$`Sr}|4FbxxnLTAeqmbNc z=6*KtVj=j_+}jY;3HgKOv-)d~7Se&upH6tvc7Elqnj>z!-7ayJf@i)q^VTPX;32bR z)Q9-6!~U8(pZ=sxNHR5Vo%tu*hRPf??HeA&62&ps%=jms{U8x%dpYWpd8P3QI|3_3JcKV|bLS)|D z@bGg&;*VMEZ$`|}VR1~=-VOGlD*@T;@PjF~J1KYA-1?W%$S66YtJ!<$eRjpFtSIxR z6_43PqXL!8^P8Ujxe(4`e!lGZv7$_Cb5-5$wL-|W+4_s$+Kqu?LCv7IC#~)kLxGYx zr~D+-_SNbdA?(xKvaFwdos@cLemVcaw}o&ubJI!FzAXelo3WX%ek#Nbns?eK|4{&R zH*aiw<$57!)=XRUgxxAD4%H-YZMFM&hw>u9I zqQ6a3^RN)UZEk=5 zYe)h+ZlSs0YrniR5eGUsSd+PL)1{9KVb|t}rXS4`qL0mQSFB$kq|}-_M}M%RkWgwa zyy+@?X-fMsqs|{_cT|d(G4CDtk?qthaRlH<+v&YFjW$W(JkpP!8v!u!lS zr@d?!;YySSDD~u+{%L8KPoQS-1DO~OA|2&o#KJao<}$REfGi2 zIZm9}RyPLmFGqPZhs-(9?&g(bWcI)3QF|8(oiV?AY;Obv9n9MNK5_eLLdLOKl)T@r zpp~*}K6z}+QHdDMQxSgV{J9N?);Tn!Id}1KFDBwRNCyfuKfWsbtq`wfuAS3l$1nvU znK7$^zXU%#J4|~j5oc06Ot3kj;pcWur9eirfAgz$3{qyI`LOpM^+JZ7srulr zmkNnyrg8OQyNmcT*)uIW*6*E&GXNc+&#dTu`9dLC&g|Xx+HOL^n+ae1$WHQ#gfi!^ z9d0*BifuQItLFC;vKdYC$4v|?h{in2Fv=imEKh&nf4S?k+TU5V3X`B%=iOD6?gn%ezW;!uuTXx{kBo^~^! z04{Ux9l1S)_#<;*SHlZJAg8%#-k543E6n`GYraiLus553TaO!bBzbeo+}0lpvD>Ek zjVpIa#7NZ2Vl}B3ceATw1vs0_|J_4&#DPQ2yI1XQC;6MDc$;UgOTCHS=E#_){iyH# zix8k@_WwnHyADwfS~&Y4kui27)x347Z5=s4LOH}U>9yZ@I}wA`DqYd6_-U&C~DB%?KIX=8EQZcK%hevpIRiAMD7cL@jgIVaxlAoGHbn zp4sKn$G3KKLC3c$96Xd zKJlSlz$@6@Jb2Ou`vNHB*R-Cw>yHvKdarT}P4N4+PeclKy1VfDKg<>>o-M@-JahCy zKP6JUUyAEJbHXuSJyOWaH*d^5>UkmH*6exm&h|}Kx~&;rx7se)6kTRM>bl(SEfxK4 zUYqb!JE$qH+tlswQv~%Kch+3Jc51cAk#?6nxO2C|M2ahCv$88eI2cCI(-^(y*J7m2{XZ!CWB!`>EZ)evF+2AJJFd*ls6+GC%a2|!a;&|Uf1SF<9$v1MP;T_h zK`ZCoEhLDWRX0z^b9cl?^OwX|&J)4{&8C-bv1=Gb^qF<@7yeOXzI1x#-E)8XYmwr5 zDL(3%zn(J&`kb5X;O3czXR46Wu9q<3d*;(`jVKDK*kX|2+HutgL8)f&Dp4smwxhDH##xAcsvvS+s5G*;!s5vEd&_^QEYYGUgnv9`^(XNZ( zr8GMn{@qPN2A>(Y|Lct+#doCmzGsfOY6X^-?e-~pX8PrOB6feOeYZWcdDT~uF>=^) zbKRBx5+Uf?TzZ{%mXN(_Zru4DJC7+3(%dm?)C(dbWQ=ECe539Kk>X2IeAP35-suMH zFzY20-!mt)%t17uQ9_yKnddHj2!Xp+31z-#whldZuDzbQDCuUFobl2$Av)JQwQxD2 zln!%i)*pH0uSJGQy=Sg@=iWbv6t_t64bN=*^ZRz=Aj9lgJu`aKQCP;EX#2lsj#=>a z-qm(5FJZ!N(GzFbX14$5X1nEylgpKrYc79%7l;!a7T5gj`iEW<*;)4R%-+x6HO?OB zAqf+}pgZEfxo+JFrwjS`=FHNTQvJ_nRU0wbUDy7 zkNw+*i)}01iGqKo*BUv({aQyw@|2obwS9Cl1bUW)6DNZW03h&|&I5+*iz=DhQMgxH=Vc$-;GtL$o1>BMH@()Aw+A-d+;`Yxm^9Np9m zytHp$k>V9nyw)@O&3?SUJAT~Euu{|VtJ<#$xtnI_>REzs*cndRAA6!Ln?=*29!ro1VAx_@H7^O z2th%#w4)YLV(U>0)p+}Sw;$U~-cKweT66+-dZ?H~<78MC8NVX9vK?EEk z4=55bVMCdVXeVN7h($rJ1d$S!J_zq3@P{oprYFcMu)4zhh+Gqb_s~(pI{;ld00fo3r_C9)D5){78TIzLf(y4A|hW96d@i5#WMC+kYFPhjdU$M3~(+W zNrZtFOD3oZk)(zs2VyPkvyllzSO{Awtkw}RLzV@SZb-SX#>TE5dwIkXkVL>#h!r0; z%+Q;_zksA7lo1erK`@GtEYe_5AR}anbOOXSa8Y3V!e$$3NNgK1tU||yohqhV2!fz^ zM_?BMRLoZp(P9ULL^vD-5Dp^A2uU9T^AJ5FDutv35-W%`U`>NeG(z0ar9zF0z%Js4 z&_H4f2w4~6caYm-?*W+;^5s|wKnM@h00MhR>%%mF%q8Nqm?)sl!5jf`7(z-Iz7cdq ztj_h98&3@8Kx2aKE>w`%=^~2*i5SH8$ev^GhuA!X8c>Y6hOmoOtPo)@!JZUJeP|jH zo1 zghdP%${YxSkS2#c0U0+$Bit$Mk_akX#MBXULBbwFB_w*V)`F%PS!)<^u&l%;1mcuf z<6bm&*VD16fxa7REXY}rQiVDVIa=uIpc{np3E43OcCg#R3KM&3=(mu@#g-n66YQ>G zO@V?Li#BAvkZ(qg6`^A6i=mc*Mg}n#WHzzIL9PI?Y1acD81nMRNc2Kzf>cKAF6&3k zyg%02Pe}D&;ikdU@2dVjWF)W1|4`jSxeviT zq?}lxLreu<2Ncop>L7gtjU~KA*o#BN3IPvt^-y&n-3|dSBu_qH_(0-> zGy@j0(AFc(0Lc$lju6E{`vg}HTua!BxhAnoM5qT5V@9+CT0UeaAZ9?O+qLt9CvDgr z32tO25nO{31TsU!k6=BEy&kJK-}NHw8`v>JHh^^=WdG0s{pY&!qZ^+22*OBYJCG`g zJ=NvMwr+rR1_F4f?h&WKz8{ee1g0SULQD#qP>6L9O2@_>2|$F>p-4g26}ns3V3WQ$ z<|*WrAKp8MLzE9Z=d*$ZkoIV-Z--E;r($2^PLo|d`wp*oq<${}DhT;$s z3Ivsqka3-B&!e0EhAe>lFkga6iE7i9IL;s7RZ_ z#pd3M)DP!g2qheHB+$|#0s}c5%!)|uLlp_>5>y41ulRGv%+EvHhByNDk?`FhB!O5a zq7m4#x^H|wZ0+vI>mbwsUmLuTun@rx17{+Vmyq;9h>6WJV&d)(7k&0-9rot16Cz!X zT`R(_5IG}f4jmGls*t@v?1Cr*RLQU-z~uy21{5mrB*0&VXdFZcaIC+Yp`2}mK+HS1aRTEff)g@G-#lqFUJ-anGEPl-BI$d&-^aLyl~4w z?S@PYOaYK@Aq1;Kct=!1VH`;8+RmkAt!c?c=)RO_dre>A_3SD zAP`^GB8~`j!d(RS4>DT_<0AVB#S%1oa7Q9m2(vJv z>dqG0DAPQ6suA)+*c_2MScsslMX&^>63AO&m~>WvnR1MW1Pi<}kp3cd0d2Q43e1sH zJg6+3gzFS(_TbuuAP=fr7|3AdhRY6F3YeB)CV<%p$~NScpk9IB$Em@bvf3rlG~1-v zE)~tHM@}6s8W~slh6<*coJD#_Lu)FdP#Yu7vyd>W*I31gDgxAGr0VrG)2jwCbr@(O zLIa8#iBoTp%Iq~srnW{kq-wTCJ?@%t*L1q3^E8>P`FFLVsW3$|04k2wG=<8DH9D>u zIgMPadQde~k$G8-!!AduaeU3|sLVujYU-R-0j-+MRo$Vwy~vyrnJJ@m1FglBY*8FU zVGaFPbY3y1L+u)sP1G4M4o?pW(@9JUP-IKxV$4Xc1r%tor`2efLb^QJO^87ll)_mop(w-#kqO)Y`KM1Y{2x%5>^MKvu|!4%8H6LZc|LA@r8*0fktu}E$Q_K`CYrIe#LWd1a#?;JF08GPr ztZ)Oxz$#E;-47ODpvO3#v~{jCN`;v+rvr^n8QS8RYGoN2mOh|LiwaTNEvUGp7mqeT z8e~|UgqnM5l4AK~CP?Yar_z>cEZTW!WuzROSy`=sK%WcC_rwy?lrX5hjkaA1{%8(n znv_)|=<}pmo~8|YVCe>-iu;659Yb4?xCBM#u5sRm^P*MhsASPXMyQu zYV_%lp)-UcSE_pG_oLH~>M{B>DB7R`nyTA)4HxHKrk#bBItsU`5}>YxZuz)-h{k=& zt0U*Hnl5OpV__vK&I4vM?Fz>CmI0_8}Jgp~i}4Gb#jFG=@&nn7W4Y5Uol=p8>U~G&9o3O*0HN zDJ(ET8$R{*6zH@11w9I^m%su}EX5M50i()6s?lN^Je5t-oJ{2;`v(3Byw z&J%6xM;?pC%_rl=&E83 zK+3yV{Dby9>iwzZq)k3n>P!6|v>i}iPirA4FMow`fJr%5vMK2e9q|}4a*F~Qy>)^4_8{OO#OtPQ@CA)M9QtL>u9(|!K zkVpX}-5acfK;sI1x-2k9>nv+MvAQLdcPza@(-`acu@Dza)UhrAT_db8LTxIG1+bhW z4UnuKL8CW)nAFp=9A~^{oU6IU3ISAPQkBO_W)yl-B~Gm;MS<}mbk6=y7Y)UotR~1R zlq}%Jf@G`@8Ml$S0t+mSK}jRs3bdM0LreuF3qR7_NSz_Y@+?n7$0MEOtT##nGp)>JUu%siMf~=gxG9xT7LQes0Nwl`oQA!;+-E>sWvR*B#F;T$Jf^V#D!BUAd zfl*LOI~>b6Q4mk#AL};KAtB7#$C+56Xg@bkx(E%YwA5&qix1rM9dK$4V(I zVaNJ;G&<5;!@7PfW5ik+G;UHxOBYj20;^U@7U*H+5Eg==73XMdY;2s?)P(;wHa1rLcgmE;X;Y)$x!+Hj+Bmhz^BeywE~fHdSH6(U zd5!H9{~`Yh?SY@vKH#TQEkPz9_?@{R+0mR1eEZi_rVwNbezwgow5RfZ#9Q)H zdB2eL^Mz!=eXhhb_CfRTf#vg)zSe#BJCaNMX8XC$RM6_@yIR`)LVL2HU)EyzzAKaO zcO+ApLNb-f`}u4~;OB#m=5#XC64>kD^|oX)g3C{FMFw?m7xt`w2gpY4uAqowCoSGJewU$mRk+oAc8_KJT}5TWxM9J`-Na}p(~j#yH-Eq zRryM)id`q3ZRx?M+Wb_(Z%^j^Ot#=R2SLV>tX1XOLXvl9K9u(@s(KbXWgX&%x`ZRFn+%!n=kk= z0Kc#3eZP?Ez}w+K`+mev$Aiyj?a}NB7Nyc@eGGnt%(ss$I%%p<>Sz_s+1yp|vzhdg z(gzfE?-O$GCksAkPJS|PUk+gGT|evS%G;ABUHjU5cRXC5BwQbecK7K_HfkOP&-2No z3PFeOa9F-W%lm-JN5e~75adfYpUU_VFh2fmKbg*Fu7@Zc02g1$~P?< zi#fJAa)lp*ruz|A!`+{~4#Ls>WI7!kJA$Cc9=C$eak(YfxgW!v`%`&iz?u8aUGscE z%KaF;Tz_K^1AEszb%*TaPJ&I%f3S_+qa2-={}g1CY9Kjp~H@CTo7V>Aj939 z6VZrzZqU7WKSVjZBQLRka3e&wR=1;6W(iK$y|Epx+g@md=+<_%Ukj2g?Y^S3G35G+ z-}c+Hi_l<;+5`8#MxVlg@lpHd9v*HM?_QA1+m|$m9xu_N_Da&pmcZRl`IFEwv0MAc z+QN3pbLe^L0^OrP7&u?96YG}s#pmeQ4EB}O1 z(jl3n{zD8i8a9Sr_FH_1TSjNf#3$AmDbtSN_EaEZ+J|k2@CcdgX2>4D=GI^sdokoS z2c^Cf>qvgmk5I?HBV+oV_D1ZWi_W^8>(V8J19Ea{m`68G6?}ygmtN#>66>-JMl+jn_n4! zy6lPoqwaR&P|>JQ#-EM7@X}k9&G;aV+P*z6DD>Rg%|2POZn+$9aJI3+Zv6;z8%;@- z3u^oM9$uf8*h#*$OGhV6;r%FKXuDr`{9CaV{C1CA_6PUM*w(J>r_pg@m{{A;u}02U zwDOlcp?~TD{9kwXz1iK}y2wv%Z)Mqrfp$sxSt{R!%7bN=;Ja?($!7EJFbYH6?zb@( zMVAwuyZoJV>oos#fm*;C?0$S%tG)7+DHP|qG-@(9Xkj{sn&?J#!oQ#_~ry+d-wRz}+! z%c5(H!!`XjyYwloQe#`4if`Kmy~AYMYlzU8KB1Uy#<#biYwLVA-Rj4Im+?QAz?M<} zjh$*AzXdlL z5e&dKNHKf*KsvI8AbQB%7Obg!sVhYb@Ccaa1C{41B+oD5@%()&dU`%KALs3lSpDhY z?E9!}J9|aSo%2h?IiCzWey7B(bK98i-@#V0j7*1&wpj{-}Yp_{lDNGUmpM6r)kQxeJbO>Q=0bq-}vu;$G=&{b9#yNJ}$8Ps^fwJ z+%;8FGdsH=$mC(KshOF~w+DH+YI=XIJ!n~w@9N0IT{9s1ah6@Y7YyRb^6%AO3Ts=?9v7k`kD1bjWq zJa1g_`}QEq>_MuJDa~1)CImJV&os-NsSAD%b^bKIV*yip6E$kw33u&2P`c(Wb0|3 zR3k7B#}W=?@2F2HRs1*c~Xsy8eBq>gawGW)Z%ZH7hBxyAhE?)J*ja~bhvgL zBe(crgbgm52o5SWF2VgP?X&Xa0wi^OEb>n5f~Y~jT82+x|BQ326U%;Yi3;Qxs>W#}gs zI&f>^3{R>$vbn$s#sQ;EEcc`;4bp4kGW$fLG=bB1*J`rBX|k(2(_MKdtJV`f5xeMT zql?}`igV%(Px_QL0Zzi)zidzDG{=2o$pxnaF5O43X`a-$0KSvk;w-&pM;Fn{!TzIE zffL1|)q5>2(dm1gY0pwhMmQZRz~j9Lz3;WUyi@jC6YG?{9*rJBujg>ZOjtOrr8_0N zI)sm9*Hx9!4^@pSU00R8S50a-En%&uHk=}`Ruddf*VixJc21&K7wzPnc&=9Wb58lj zcB_hr%~dy(TwQgW>lamJQ1>c;z1y51ERiZW`B$&@WZtR092L6CKIG;g$T;a%T@+_> z;w*HnYA^a)bqzmUJ)rEv)uXhJR`23TpHgDQNtcwDUVT{fe5#N2q{gLMoKOhOSxv@y zb-MidRTpBTqEyXe<=;EqmM01OQrFlKFiHS9 z0YWv>Ku$YQt6OjhYc7bc?#eRaIR17W$=EgbmR;6{ig&r@*|KB4W$!DnzyCD4>d=$A zD1{_pZB=x|wIe*~)soCeSZjN~=658lwd0=cy0z1yrl~!^lWL@yB&^kGF04J)lWL@# zB&?m|NmWZWUy!i2*}j&g^pk|OX?uw-2_<3eQhRGDJB!p_5glr!Cp9iDC1LH&p43J8 z83}9GMlXErqn;%4SqW=5MpyV8Z{{U*)O1?H+Sj7jqjsA@(`!HUq%KNkNm%>2zJ_&u z;x}G5NME%&Kl=ORs0HeFEALu$O|foUXUG0#`-JOkJ7hCyd$d!dgS7J?85~5DViMLZ zbU(Y)n1pq9EVP4G-AYfY5uuQ>;(X1iiSxJSa?X-{fgl1{?jc2$H_orJxY zMGXAjSLj3RZQqHu!1kfuHivG~o3m7A5{9>@z?+Y@bQdk^AhzBlnpaU2~rUJn0ut3rg7MP(5v*Q@O4S z`?T^fopwS<+P9+5;_`)H*ymhaQD6^zH9E3OAWGQhrs$ySJdr69_IV(BA^NQ6#r%dV zfj*m}cd3tkU*@G2+F!nA+hGwty3LciDD5a=pO2$;WM0C)HG1!Thea3Nx89Q)7yFg4 zZ-ae2*<6Q&eH){X?#GYLD4Qeu&Wz8IeIqD--}xN&`gX>Ly}qZ#@%w$3d(ts_ZPEEZH^%;YKQH7_PpT0Um9XE&?#sP?FL_d{@hS=XeQcjy zF54kt|Ej17`rEyLOZiFIe}pGBF7YQ}|4BM?^`Bb$-gJKNkn*2o#QLY> zkGFqk*?Oe^+4?^BU%_Qk|Eu+jcA1FI+JBYZM784y_P-MwDvbaAAJ74#|8Lxjq)|W# z`)}kmzERd4`)`YP$Nt6>*-OF!{h}u{U{Lgg28{BgH`AUH4)E<@j6|@61DZUkT9WO` zNjTsTOo*NKw?}F38Zd{wd%!92+aD0^5e6*8^Rr(%Bf8B2OUt_UfQ#6*2dw1vt;Q}i zALQ(>b)Fco-jm);E=xGzVNYsYpOtXH=IE4me;6cf6A#$thVcxB@d4-)*kcW>@}yQ1 z&=L-`yQTK*A_)hMi@Mvu$#(i}w`^=vkOCLXz zN3{F;)~tjBFVmTF;FWf!43c)%8+e^3y|Nj5)YaPL1J^`PeBiyF^kzCv!h!2uD-C?k zlWHWsB^~DE^KNioYX+qIYD_)bbuPXgY`EK}WfPy+gu5 zb7E8SptSeJ0n)tE<8tr8Ae-Tf8@t<^=zpiQ3C zxP-fegIx$w1%J!}H8^cF=(l4H=mT>q~KCIzKMW-D;r~K6$ zeroL18=mt-nj{>4b~Nq}UmDH7!!N3cyA8jB$GpuwJ|sdV9KKdxt>KSy+8zF6{1q7< zMG%H>^`yp0J|rBz4KufW(!)R3*I`7W;;D?N<6?KjxUya|VrM?D5&K2gKjKjB(j$)2 zd27V1(!4d|#Ax0c(W=)xA|2h;h+O%+HR3ea`$k++(GVk6=pR;>J<$<&#GmMh_0b77 z@dPhazA_`WL>+&`#|VETJ1yY|Qwo@k?B_{cBzBN+Y-?e8TDvcJB->` z`M^iLS~~5gZV#TyNw|ZZ;L3zI67JBi>{vVO9BPABZ=5{YE!T*Ox;6^{H2Nm`f>fI-G=GWgLuWGquf?Fx~Y7a9(_P; zm>zwsC$hJMqfd>7xzWwhFg<#{Cp9i2B;n|s8<|F*T@kw+y)+)X9DQvRSQ>p>G-Zxn z8+GT=4|KbW(GTnEG1><@zP9mX=AElmo}zfbo?>((b>l|L|+=S3qG37 z+I80$&f{Zd*zJ&$JV`hvda1{p$g3DrmI4~n&I2yyRh%8IZN@B%U(aQ(sm842)!f9> z-c~yP+Op$66pN^h+2~2F%BUn9^9oPBji>&wV&)k04<2~zp!k8u*6Rx~b{9L!MDiU8 z$ClCpV-GF$w6S(A6Dg`B9P3g-kr+tAvFWlIdTg|p8G8lBf7_a4uhrHZd$Vhav3Bw5 z#99)Ly|erT4^^IElYLE8b0gu{t=;;}*iXx5>v4lPTaV*oAGe#%*5jI@hcs?K+W?W! zOTux7u*t^Fi8tA}xq46II^Fe;TjZJqypOxgPXCKC5{|pp6PYIA zIHtnJt+HLsNyQ`_cV{%rjawg|n8*F5G%=6cY@0wuQxcAQBf6fCJ*kGAOu}*h(4l60 zjgDvI`?+R;z)ZsNL-bqYC)t$|#AXtX--Qdv@r?G2KcwuLjz3awaQrE3q4CY}78;+{ z)*XLF)Vkx(^`w89L`}l+wt?CZ+!?>B6fqso4gL6s%EzYhk49&HsdVO7%g+2x6s8~l zxhFMF*e0RxX>0fckSTE@Hwpb=p7f^FO+tS%ckBLs(Sz_0jb1DNDBVW*vymoG+rNIx zlYVhIoP>TWzj`LWdO`G6yQnM4ek=t?Jyv+p28cM?vp&5+922Aj~tOPX+C z{E{ZjEQRJKoESak39WjY6Verdxd}NA027w!W1O&}bnvUo4t|p-GF`$6Yoo3(;a*Rw z+=g^Qw7{M41pm6JyrWFm9Gk=@a9Eu1d32o(o?d5zUFCP>?G-i*&`#1Y%9C0Z0ZP~~ zPTRU+5BHjPWbOFa(8SltzPAmB#)GX5bNCuHwDBGrI?INxhO&Tn!$tOwDjk%t;X1n{ z$)#}OJM9OZj8MXc2R!Lh5)(?;V3)c3O4#sP=}cSWt=aItCUF|VvXOCOl@0+DN9YG8 zj*0ri#D@6AP244#LnrQM+s4TeC7gJOw#~#@p46($P{N5PdeWyPHk5E;yC=OVQ`;Ypv8WKqJ292_U!Uv}ILmB)QnyTZh`JQ-LaXq0f`d(rJo>gP$ViW?=IG%T8M zCrxrg_@v!zqf+iD;iN{}f==`(;Up%;C)s6vfJ352;jbdnRD?mTVBA%(7W@6=R{LQs zTL|8UuyS4{;=BUGrL=vm2>}9=po}umN`m^xm1wnncA9&eu&Njeao5F4VzT}r3vHDO z=0w$ON<~OmGsIQ50B}NB)~u43cjr@%?d8hb0o3)Z;!s5 zz4xXaVmMKA<5ej^IdTw`ghhEP*_-pH@3XK3+XXj3SScrt)_d-%wnGIK!+}Z?R>}qb zM_2(BNE%_0NpMw!6#{`u5f<@7G4>cp`8j~f!r155oqkdxR?Jk(`GLSEsH6`Bn`oJH zP}773#{v8j)`s3L8L@&vxapDOo)ngwpedqUfpm(L*0V@;M#RgtRSg;Rp z5@G4FO6+I_s{PhuXPzpo3kIN|01QwG9zFS@lYlyPb(=w{5*EU(goN*Q%fNBMx|fxN zR?%V4zVU=X?9fXyPBF?;)=>ZX^$`J>^6eyJ-)iQw~ zY6%PU0+l5!rn_B?Xa$wC0<2vD*^BKskQ=v-)d~7M6OiCyjVwEwq z99|{G_PJx`=Y{3Hfc+CKqz^=%u$nuFZ^Gi-mDtwV8y61}7F7jxLs$l{l4E@Gh{F=X zI=hun;B(%8#a@$EYRC z()OWh>36`4YGSpL~(wC#(Vv;E%Ah9b1V#DuSb|HF-D|Zk0p|D0hXmP?i%pg!D z;^jF!(*^-gKO>?mMVdiCB$WEo=#*n6S1ha7e;}f1rtqvOOx{wp9my z0oJ~&0u2nAumCJrU&899m5g15>PC$1?`^nyi?ALVaHFDV4?sGFEDF&367g#9t^haq zTZvdzUoGWX2`;Yy^AEdszjqVy;;^o4D3GuMt_ujDLYM_0Xu?vlfN2TKV1lS6tbqwI ztT?z!B}co0k)D3%A^Y^T^fZ`r!fL~nq~-5SKKI!e7XQlG+olSOvi_eSxhufLcOH3` zeb8F!7I;h{WdPJfVV!e;r-Y>o;~4St2OqR85o3>N#Zxdrg?I-5FoiY#|6}fT1<(JG zK0g~TEPo3)S+7|Adlp*OqM<<435)esLb!MQ`@Q(9tC$PQumBYVHn0Hj1D>uZ)dmb$ zA+`hrOkn|iU@V1&?7@pm#Hvwi30iQxgcTvdpA=Sy2k}l=s~5ng7%G0mQ8&FW04f3N zDx`h@S}No_fP^Tln+>3wu)HeRVZzFjU_r%r-ADf6t!e?;2moVI=n%k#iCCq5tu+m} zn6T_5$j73*qDqGSu0uZDC@f`ONvK}je7D^xX|Y0JfrT_3FinMs1wc%N>;&LBg~jnJ zLE=wNI&+DzXeQ8S!XlX9UJLkDU?_{Si~v;?@*aRV6+#g}Gfc#axohEOAlih5K0&7y zf6+$~eW);FA0HG9?AO{ywSmhb4KVkJ=@WX`FFoB{LMM;4REJVrxq9-gd3nZSf z_$wHt!m9sZ942Db)3szWIGe)i?4WuJ*j#{Ei=w6~8RfUF?}spvBZmO^D#Shj^eHSM z53;AQHaysq!phv{-__(DJ*IZ zMx(HDGeFv+Fe~7bg;X6NkcD&`@co3wjWt!+=BzOAiC%ETqK% zlPWCc4&0%z95C?PLP8R-#6qwKIHAId-oOqD3*&+=D6DS_zN@f`J+R+GViN%1LT(e# z-9lOtAk;$U4H(cu`U_ahLNW?q$wI^k7_!33_#mGq;&swp5pzJ^h1@dWsD+>!U}c3x z@WIR%W!8a@F2rO3h%V$`fz~aA4gu;ZEF%qeqOc0D+3B%;e;|M(njhBe)F1#E0%k5` zNC8bQq&tC1E(AG&`YnVL0j4d)>zGrfeY~@PRS3?!5OD_ja3Y4yQE&t>_(H-P(Bwk$ z68NUV3f$l#3uzeUXV*XUngGyf2J}lc35bg3-QMA50W{GJ?%eG#QAQ!S^+Nm^sPRG& z7?9>d`Vt7oLaqcrx5D!EfWZpOq?`SB+O|@FeKfh9!ayQYA3*;?jvWa5LXaDH`9j1R zaNI(e4H(rzG731W!V2By!t4JqTNK`C2JZiQqbOYvKx!e%1UzIR2?C^6VSR7#K!ugA z&AwY^-7O$lnyb$4@I|SM=EXPaUJ%6|0>>^SJOPg_1g-#bEJQ2-xhpJQ4{5KsWBv9PW`z{0{(_W<__%eR|5I^Mclz#ugj?;v&%Qh*|m$cY%5 zMX3PbgbOJwKuss&Gz&+V03=yh`X5MPVbOW>#yuBI5fDyIa{PpYg^WLd>4kVEfaHY; zBrxQKv?H^uU&AK?HY-T?LIxDr%R*uRu**U?fO+KV@BT~(x->T&{<>WXD0dDpV__M4 zv*wt;Un5}78uRT#F&#Lfq?)`%(uGBm=<}5plDf<&7c_AOj+;)5FApyQ_P_sCn$)HrNS9 zA&zFzzAxD=k8&2xA2Nq+5+cn2e-|Qfzz`Nzs1Ii!B!K&xzQ;HJLKI7D&R;(L+oI4{ zvt`tWi1;}ksCoRYWdntvJ#*mZTWl9sN*b`^LfD7-><1gq5+%}_OP6o>r4YqxE;{=h zyaEpAX;$y?JVqzSpfabueDF6U;I0HL|HYf3>#Su>r-e`)0{CwH*V?|-Q=GTXRbdC_rX(n#?_h*EpOmjrjk7fxelBRL`H3&mE z;-fkFsjUr(7=ce2RA5~TX$_!C3z-CfY9}IyZ$y?bYx=yfmk=jwUO4H86NPX{6RaC& zcb$qQ1Yuvu+W~7|NZkQRUdUH5C*D5eY$0*foU`yC`=c@;!G%x60AGqa0RdmgoB`rr z2>&s|Z+P(gqLg9t+Jv9l9hJiP%y+++>C-Dle=Cum9FNKDz4SgiS}IWp*m)r&#a#C8 z(XR`@+h*hFh6eh7xg+vN7_^-!J6%sGZjC%(37sVZ$OaAimH$`d3=FgXH z#oFAFi_LyN$=RD!=&jlO>C_8^NLF(~!_V!+uBa(s`Gt_0FHU~t8Ud%<4E)OX&Jd+e zn}40UX1$O=Ytq^Ny9gPE=7B%WIz$M>Gv?oS-YR6E0az}?VwiW@C;w4^7B{?r^i}kd89jDWMu6`A;=NVKF*p#WnK|f5JNhY6 z32=8I&cyV+=UhAQE5_BFvg$PC!5#L`Bx+7SM*uB1f17dX3Q>}_xqsDE`!NMVn{%3f zce9X3Xm(rtFSZ30iD#Z4@uS}f88+tSt2Sfv;n=ih)2svR(5=uubMaG0*r7n_WoDOe zABo(nLm8QYm-g)|N(whs-~6+kE)>3MF8E8=EkYoq`Qe8f?b=SkgC_WW+b05)xoJMM zcV8h*)|}n(rhNq!hG))t@L6nB9U05KFl^+%3h7E_vA-GVLI)Ev_a1#_l_=TU?Akiz z9Z}G>`PuOc@g_SAs5!WHoZW>hh|o;j;ZM~9uDsd#&D&2BfZNRpss1YxapI$c)tLpi z~)V*4%hvfMMPNWX-2S|m!naf|_Wj`SS%DlL^w_UI*ZO5Fy?i<)xIL3`R z>#Aezf>kl8=A!S+Lcq^)*33SmF6|ORm&~+7CfiP~s3Y_1J1*ZWq*0q6J^ij-gDa}Z z?2`I$cOiYoZ29ze>x9@r^UL`UzAc37nc;P-Z6{T{j+uGxGj@HX+)}gW+S~1(R6&qt zWoI3hSq^$-YL9y!OEkwzGB59Y8Ft7Hm1{Ph`T>*}j;Cfm|I1}|JW+s-XIit|mfziaS8v%vno4Yb$EPn5$%!g1K~W*colD%{=7p0c1(i0C%2C93T+P{OF$zxh@B_AA5B zJo0%T`<^OT&a9ii@Q)(%rPDJH40+j}L^-YI=)Zp7c5FqhnKw5){G1SrW{zIyr$mbP zOL4ttp5AW-me7tWY92h{IotUZ5obOhwiaE}(Y(xrGwxj~BukmkYra6IcRVNa_tkgX z`AH#6=HVOP|5Qk(H@|E=)pkwA$(!KVbI%ikPR*0OI=+^O5rUPGX-+xqvqeINqFFcO z-Q$G}Li6W6Z`deOTqea!JoDx^Plp)IG26}gYlkDV=P2l=;h8Gr)*Kw$Jo3=hPYdD6 z=8gA1K$z8`kIkbSp0OQFA)Mxg2?MVXVgOCAqrZxo&QZ(E#dnXiOE=|`nfg~Qunnr9 zCv*Fihuk7^pxvkMa_Qq+M2c@o@jcHRe3`)%)ki`ZRrxaMIY$KLGy z{9WS`F#xi%cFloZ4R-gS$V!vEwRM=p0FS-u5XZ%;1Zzm7Q=q>?#0w%O4BNx}>iGx@Z5)PPG@hTyPX4e?d=s~Z92nu#j2nwNDAl|@8 zj|3l-IoNMvYlX-alzj-;V@h!!d3y5n&k!R=m;_-h#Mh98N6r``R-_7%F~ggJeK-~r z7%-snzyyk&GPJJlcvtkF@KX#Vh$SI+gHQoBXXtVmoRCaI77p7Fgw!yVL)?R%m1~R% z2Osz(h6Jcdu;7B^2NFB?UAuoohzAQ2gqX02LgF6-8D3hXz>xMt_53BQVz%$F|{MFkH{5tec1g%!-l;DR`~A1ZGS}w4PhZHo1vhEo(StyNMf)^ z#X12&J}mdJk;B#xf;i+0v3|uO3!NWA3GB9^0f8JB*?EkZka!_Q2^AouuE=B~uz)ED zqFQW{F{nbJ0QnT6i-@0M3_^q%vSEk_u!%zm4citZII!DA3K_!`G*SrGAY+D@1eCDI z7eQMD9UP`UNI@YgajkaM1t)hv9fYkeblXtoA;^hEJKk=r39y5N2o^y+tc?+lMG6BW z8q$SOIU_8J=@qI%telatLcSRqBjmv#FGAo1>v@F9v50~;7MUo>$Dvt5)Er|arXmCx zAc{Z^4Ur=RgdvlGWB{8^?8~s_Kt2Ik1VkjD6oRM>N*d%bpc;l!5J5l)Sg=>csu|L9 z2xzf^M$!$DcjO`<%SNIW={JM~u)K$A3TjTMkRd@wN)9>+#OjgIg$@HM90csKPDVx^ zaYpQsp&vwO39(aXs*oGSq=8%}bi`PCB9eu@G$NDG=VH+gX)aV0h}B~g3`GaDGSEgN zn}@^%RHm-K*aa-2npnGHhl`jy5(-FzA^3ufH6h<$E5uW<0w4nq zp)N8w5VIry1c9b&t3hv1S`EoK;?@v7LSgF~Y{ucuw?af=_g&EVBFBz-6Cy$E>YAhRfrT={R@f6Dbp_oT)M^mqL#F`2B{sg$7bACw z7y+bc&|70q24NVaZAj5WMGq+!a@xq(A#VXGI5J~c#6lE_P!P1*t_Rq~8-lBdIz!|Z zyUY3!Gw+XecDtQ&)8OfMRev9wRH&AaJjXH@!6Sr5vG|6D6YC4e-62iHju;^*M1P@| zKcIX4(h{f( zU?V}E1o1Bf1EErf*Z@0jWNjdCfV>al8Kg8^lRUlh-4J;SNO~ZTfCLRW3W)6>nRV^_ z;7J>HholSXGAPxtPlhxVu_OfkVz0;Q&3C;B^*;1#*qK49jVuZRApg0pY}b{@KSKox z(O>MTE*6 zMTQN^3JCRFgV_xOln>i)lK)g6vYTXVVi8h7Y6c-e#26t_ajTTCTyWFN2yG&wg&hRc z)2?&vd34j?kPvqVu=_d~ArRJrQXg6lWLF_ALT;_{yS4kTyb{Vs7`z~Q#D*I30PN(D zi-4FPQUwUMUB4fG%5_&FHQ_q?yZb(NDJ1J~TOpp0AS!l}P!+>9hCCS5DhMG#hJk1n z>;%YZ!o!0dXYAEj*;%&+=@^7upf!Q+0=5GvppZF6@D~{p#4BNxfujovH)zD&Tao(V z+zVj`f^-|2DM;X45tTDT}Z3JL4qta_T-R! zK*tDIAQBrcBOLasOVoIX+9SsTxgq>%P=vtbgRmylorq~eS&Q5gTro&pBBO>BHo{~G zW5J|=BmmSHkmMoq?1D{=GR;F69yT^udLS}Df)Tn2L=NC@Krj>;)TFv5GBrS>!910ps&bV{J4jJ92(HE$%}X*Q2Mwc!)zp)ri3>H) zs9jAnfSQk0(S!#1Rk)$aNtM)VkWfQnDlt`Iu__GJW2Mp~^&x38S<}dxIaLRWdf3!J zp|(K{s;j16btCGl)BLmg$TY(jW!f|x>0(DJXi=GlCbHDksxBYRAE{YI6G$4xk3=|8 zMkY$PGDl1I8U?;gIx#!K$Q7LqOqtWnL2oVP_6&p4yG8vmZGQB##w6>iB4Es!PFiNi z8BwK0fgUQ#j3}mJP?EAU`ibc*qQ&1>I zp#zPe)Co}xN244?@5~5LtWL8ybBvTNQ9MC88jab^*wUL#D-A`PR7B9?6E{%NH1DL| zRIbvB9Lqszwv#eACOj#PpkR}VRBEZI;-P(lIY6pa=;fdyE@n$qM+9AZtUE!=2i@V6 z4zrR11<-T}$I3;}E~GUKD5j(vm-g?tp~_V_pxcKk0lL!Z3}MEH7FX(0=XV9+){J`X=ZyVxF9W>6rJ6zAEZ|=`g2>hsiM3Y@k?&?qjWFKt(=_e#Ejxv|Xqc zif%ntXrPUZ{$<*D>GGtujD{$tohbxkrk$d2xo*8y&SV|?SBc*kfDLi^% zsK<=kep#D<;)%$suAW!QQ|MWweTKSicbqgFKLu%ci} z)j<@HGMP=)3O#15v_W47oh>X{L4zU722jICp(nG*6opfH!^AdKDy%C*l>|+0^g6My z4b|IJ=25E1IvTV@Q5!^!ISpAfCDZmtL3YgC&8iSoT+^UP!xxRURA19d%%T7kCa719 z238sbV)Z#xAw*+-Oif4mBdhq(#i?>w)-<8Bm%0@ev!K?TQn8rxm9A2CJ+Qt8EsT@` zuwo6RimbiBOg&va^q138%>o}(AkkSyw=``HF-^9XN1;$DR>DL@=M-ww&rBUZt8cI_ z3B6KO^u%kFu(}Za<#a^T`0q*>u;dUu(De1wlt;lCJ;W@wL5VyaxYP=>4hD+=(Ii3T z6b%Pz?jzFqSK7dL)HmkNi>!UpcIYfI%=V)=x13R8vj@V zh2@Uu=ce$V&Ufm7SsaG7&8VQKbeS41*7RWAHo8&h8>OI=#z<;RS?Gt3BUag>E{t^u z=<%m=kR}R>CFvBUbCSh_=w+r4jB;-J9;ifS87$UpqpgP3U}%h?UY|w4ShR(XX%@j@ z^$fb3sBooch8j#NH>n|~=s#Ym&ehXljUt*~Sgwmoa8`4pBZg)@THoWP@0`Gn9wPd3 zsUfA*n8Gs}npqepUhKsA94SkqT9Wcl7DS<8m9<~!S!Hn;mJDHW1iI*1bAm++XvU+R zjy^9|cwnJ1I{)chqWP9}3s?w<{#aI0Vx<83ttjASK|%VA=ufAim!4sY8fj{voRg&y zsV`?KI~JLub%e!K=u@GIkWyk+v}1t)>UrrOrvZcVO4k2kO-Pmqq; zptG+iY=5fX=(D7<-{MbgY;3guGtIXj?+kKjzb)I9X+_OmKUf&#mSi)5pUV3vPFyGi zt$rbkL-@%|t6wTd???DS-+>4HY@1(bPvv>)2=z*lFezbYjLTCAlH%iQC!>yCCE?u^HK|gjNg_E0zWFq>}$PbzazQCZw}nG zwfYf!DR%v>xSwp!kK-R(aW01mx7S%{PZsd2QX9I0)IM?Bem>g~Yj|a7``Lv-4&RIs zvt1J<3%p36v3=n1`~^YKX&VU7MgLB;n4ir1+0G#2J3qsuJS6HG*_>0=J>GTpm+f~S?T7&M7E|m*f3h5>OqEw;1=ljpL@c~3vn`(7RIG!dqu5TDjn0(JEb%*&xxy0HBJIh1=|h3NXZxgbJ{S&uN3sy)QpvP!tau}( zGJc5*{Uy&YZ(F-1Xzj`cdDnR|t@giV7Zc?2*^KQh{vtGcI<+9+a1a{`>@5JTSavCV z71@p@ytjR}03npUkI2TUr7zG0=+14940ZoyW(b{*%lH z?(I@mP`3_O$htm|kH5cobldv|uR5Fa+kznWi0w~&{~X)|9X3w6b}w@5hD!IWa24(2 zu}|UlTu*gfI#uvnvw<7=?dfALWLpsAySKkXv3Kj+KaXy zTXKPW2kd}R*$}p!FoP_?Gf}#!&JCqkpyHk0i){=$^~bAZC^>DCo- ze4XEsY>lr%@qY9;R@q;c-XpGW?ZVbAZ?P7SKr-9!)5*MRh(-FHe3yHJbsFe4Bv$sh z?k_<_+t?GhZq?LNPjoAw%D!Iu(m&aG?5$u3&gR$$xAV}tzgXQmo?`M!JF52VB21`` z7+QKP-I|oN7Hq<77XqQh4K6X>9=b@z_QL20Z9%etwP6hMr|Z0~wDy=D$Btyd2QJgM zYa6{sVlnNK%C78tyzx>$)2H3jrlZH(blbunJ4inJrJJ}Ve5YbhKI;8CsdYPDvma#g zcAmqLQ<;1rnJ&!TLbuyBM7y``jNo7X z*mYs+9%!oPM{3u~DL=`d-If~DrdtQ-A8ULMGdlcJsTA3PZMIfD?RUFUxgZu;_@`RPj?Qg(M;B#t3-Z1qs@$_o6|g|FBemXa zsYTI)lLa;vmfBclQ@wJE>zw#PNZ~>aiF}7?N%Wr>X?*10!IT*%neea&e z;eYZRJsht4pcK8~_L%9Q5cqM(r=QJWI4xxTc|qBMws%geL)`TujlCQW9+nq8?+}B)OC|VJ=By+hX7#_L`?(AiUYYtkn9rj@E^k__bLD~?0 zCRo(%j$4Bc-7vQVcE8!ymBYMNd7T=S!ExN-%a0U&zwCRCU~ZRXcH`G-H(trMLXgAJ z^lFm`TJbF?Z;4##ZVK%|CcY|d%jVjGlxwP}aWSOlISIsqPSbkoT0QLQQ<;jn`Jd`n z=ua9W%WD{-$^DoM@}03|OL>p#{(dV+P^W-&Dzl(661Y!kNf%KE%l`f!L^8Ua(k)JO ze~v@sE91N6OFCZTm+cuefRHTT+;`i*^*Gv;_C5AGDxw2zjC$nXYO;^5Kd|F=u7a{4$2Wh)lYDq?W_*iJHd3&P}S9fj`gsa}py>{E# zIIytWMd?>6c27OO3yz$cb`QyQgDH&|U`tU24PE@7y(OuG}l%N2SKyJS>dKVkJdOF&aDf*!vMo>om$`)fi zJXWmCnwKC?ggudt)JZq7MMFd;SQywzI348k(K-Zjj#$)3sw zK4ofUo9UZ4FPF@?@f^p%PDz7MSeoRLcHNL$;OB$f!c>c0bmkVgU%Smj&QE9OWusov zZM!vPuksV?z~UC|cJ~=9Zb^6LQwsyz_0z43Qo5$as?*IaoykHeol{B`wI>(q8rZ#< zZ2?BVe2`AN|4DTqP|ZNcqLlqM;(I~)>{K~{yR;0>iug@*{a9dcFLqlRrGl0qpHJpe z2t&8!Qiu>YFX^c#+Eq}hV7n>rvXGh=1z4l8E9tl7viZEb7%aiEU3SQHKPTrU?O|fc zq+~iB%yaLLuC`(+C_Q058hmj$CZ>4f(Uo`T&NZ{0x44kiEL}34#&DC$*p-TIS6khx zC8?{}_}%3Z>TUI>G{xd%_OUA{wxYfAr77jhaz?RRag%kPkr_l}GIm%81s9u2N2v8n zY+AQA?f&a*n;&6nqez-!X*C{Hg3vPfn$4k$m$jtrAiUkyXqd)KQ;4k@nwPj2Kb>u{ z+u-CPzbjKdOf)aSuQS=6vIu3V#wf6sPPvGlhKM=$^8d7?T7yg>nbrg3gYR|)nL;X= z_FI!3ZoQaGVZ_#u>!KtQ%F)~3!vq;D46v(6wk}NNgI4|-oOsua**4wb;2i(2B^Cdp z{*~!}+LQVA|AKRTss3lm-h1u6*VIb=&y=RA|EvG`@Ax;Xcup^o-p2*WTub|LK>_6g zs-$Lic0rKIqh3JG%w)bj$fHa^@2|B7EerBp9eI=s7!dt93mE|^F84fdc=7y%lpUeC z)bsq}dA%f2T>Ifnq(y*Bne59td%5LD_KcaR+1!ZWo2b$B`YgQl4K<-%~~rfU&-op?)P;*_j#VV zSAW!N9`|`ZpL5RV{QP{*`5fz(v11<^GZg{z*{=gYKqoU(S!T4l(WiaznTrbM6J+Fj+~;O!vE8Wqs}i%5<=6rBSJ-fJ3p6KvN4 z!6t+pD}+n}N>D&RF^Up;%9ohV`+W$3lH%fG^o#vbhH9rX_;9xo|5!q#_IMMM3AURY zW$56$?;&%ALfF&N=JEvJ)Jf+0gMdzH?mRG^%cFY;kyi}}VzuG1j_TM-2%7jJR-qj!8Wkid9o%WUM|q zX!0^*mC*rp@nYr9agFtHJ-++BFUf8H@5^$U{`*?GWE&kT6SiH)I9B#sC+<8vR<|4L zBJUgJw7d6>C)7>I{)*Mi#xU|JMmJ;hitO^h~y z8&*0HztVw2?i43uj2<{s%jkicc&Y|e%zW!8}p_z7253}9Xdb=ny?xYI;@)5$xeiXPC9H9y6C}uB>_}+ zC}AfNBDJ}T5IfbAKs(?4*e*$=l3fwEX!d{@rR`yaNNs*2#2(@Dr9I8%Dti_opm7z% zo>xm8d$CI#du2?Z9@0g$mE#{7Hwj{IQGFU?Y+IG!g!@!B?0pWR_F>oli+wE7?u&ie zp_qM@V8wQ*AU3<$g(LB#38yAJ<<rhBrh8i65i!bdCX&>@W~tDs__P3hX|2?^IasO zx^JA(JCT%Hup?P9X^-UBD$+<{JW(R$u3r%88`Cil-S^N`NMxWp^avG_`(nHq8SCO2 znW_fK*|vm4RP{OAX^_Yw7r<4{{A&}ui^%$fw|CsI$dLoJ3l%x7hRn^EghVdHMyx{~ zlhkpkP$$`iu1;D!j&-seaOxB))0k{hNSzYLzpGOflN%?q6jG;;!-;ielI z2!SyZl*Dx=y93T}WUtO_Z|Zpp*hur*A{Vgb4$!L%iA|0vq|O?5lr29}d3CnCARHlB zu|s`Go#XKqxlWBc{8dj7>u~%GNtQE1Qu>c(NXm-mX!BB;sz~)Vk_OZ+K+<4WoRda* zzt>v7B+vRK%~W^NY%)TU=DO-OX$c`Iej6H+w8AsWNn7I=DruJs@1ew1PdZ8nIk~cs zq;uZj*SwMHQv67DBM$xQW~!$?(b27Sb86Lf-C}3`>sAs1qbI4n)a~Uu19khW(H=8$ zBBXA0qG_zFLC)AQ=KU!NQcyQR3hHXe70bef)LrEQw9)01x?2(;-r;yObq~039d?aK z-D4hDrwD;jV^ogo)+ESO-AfMbl6~%H$+q`$a!SI>$(ibVPE# zk2%yyu5qZ7e8EHWaxI!B0*GZ1L+W`BP`xyDfF9N$s$Q1&%0rNPd5ZKHbpYy>xkRp4 zbp!hK`X-`ZZ@7bgy(uoZ^`w;`5AtN~Q~4O3MU3O(~40e@b^pkE9In-mi`)e#$V1MN%dZ{P$^~ zkfO1Vlk*HonUOHw0*4b*Rya_mtacVHWu0qnQZ_nKXv)?E)=1gmkT&I@2h0(7Q$bQr zByKB6%2^k*>+b9IiD1R@s3G<3xX7x0x(e>3DGxyEx2z?4{UYzviukvCd45{`frPpV z8P|~dL*w7|oTB=333U^4up#vq)>`EH>s{Sdf2%j`&Uk9q-%GIFYzj#IqqRWS!~uQD z`cnM(LsAboe3*L1#Vz$*9FbEm z)q)}FpLz+@kOsC()&|*bjMgB}U6ckzgrxX`SV)61LdZGCfHWAGV5H0oBM9gKX)wbz za1Ca;p>~6LiP3d~MUHZBu*RF*3waxCPLRC@+v8=WffwvIIHBg#WTZnHobmqok~iYD z1VKro*cRABu~;lE)g?e$OLq;^jHeenK7pi_#$%k;n@}e%#Q>64ZFaF@r$Ugl37(Bl zo8^)qZN3vVr!97sW!kbEytlMfF6L>8qMf!g9`m$AggS9K@Q}1q9tu|p$%*;!kTh}F zqoKE((=a!Iv<(a5Cumq{b}^-4e;4|O16}RYaL5fnHyjbap(qWf)$S-t!};#S%e;wK z#_?stwH{wK+~U5v(|dJKB8&$TU_9x*f5B7y4KK%QgNAN&m!9MLVvifTSOD5Kcek5-9y_f^kg0kZ4HLg%ENo0w9f2UBPISt6sZT z9RO)m5Cht+5r8yOwWg3Y8sa&~jfTfo-l-aZG#cl*290JVFhQgFaZJ!?h08mQ);n}- zwAuToZLTyl+C>OC$MTRy`(pHLbSyzdH#*@lOrr}9JsMqg>CuSe*FPgbKu1VMnoHb_ z40qZL^=3^b79=CzWsi(fv!Q)2B%{*Uo#_gHhRSl61jGq!lvAY-44PsZWI1plB-FP+1 zLXGokw_qC=J6=TNN)s)+2|kdyEK8}E;I+!~+wk)dgP-eG~vK!Uo=j3g-d%q;gc&t1tZ zjVDoNrGr3b|2XB8If#H#NT#=elR4VupUeq2Xu-^>o}6SZNCakSJk>JYJ?zYF@l)-L zQ#F|f;#5uMaYtk_Yg|>6>BRAwS8h}_nb%xZ(7m zRf4EB>FtSHlfe!Fn~ZP?*ko)1l1(PYBiUqjf}(4(*i&>(*0{86vQcFyR$T&VvNhpk zsmWfAP$oSBX>!c*f18{lgq*4qNRzXkxHP%q!6XUnnq|eql$AjUdzmbdtbF(ID$Co( z&gyL#&gA7mvids=nl(%bU0bmN$r|OVjI0^vW1V15;_S)=ZY2vOYbBvhTs;dUYm>7Y zS^EefCj}6abGj|uS**rqXsT+bcTj+hfG6B_QYZJiO=8&e@J`L2Jyi-Us zFK%siDiMpCgwL-jgFdVbo)g=q894=#lNw{+oGe0M;>ZWaKyq?C>g5zThp0KFiHA2i z6`mr=@lekhQai>7hsHVMyRBi{CGj_dN}oc1P9+`7uS7(aP)as;n=iYLR()7?*-=fqi@=J}p>Z(ioi z(!9!hwNJt<%?BjRG9ppjn@@IUog25|+I&H+kTzdVz)YIT{;@hNNOSK{BR7d)yY*0z+%yN&+&qF6D_Mf%7J3xV zt%y4t$?ff`;@m-mr1;}{NbXQ?j;RU6%1zkG%w42pi0yvndf{E}26dDSgXL~X+#=20 zS$h_nd)PxT_hhZZnp@*yaoHR2nukIQ@;+)2aer@-mOzIVS+%S;8q%W3MYKhg`5{&* z25Hf^b}SFy7Ng_%sKo@2k6O%d_^8EP@6|+CsKt_a(zICX&AQe7a;IxoTkJ6>T~qE? zC^YbMQk@|!j(HN>!rjqqamjHpTU_-#>=qoi#4W8vo^6@tsGyel9)>N8;+d;uxwlo_ zvah>)(Q;7iaL=hbrJP_1$j)}vKkf}h-~Jl-vA4T<+$MH6~tTdyb=e(31bxCyk3r#bt}RkdDRN&L`dF5*D1}L>Ih)oOoIR3 zF*^Pn7ofZa@c=Dxu*_TS{k|>%%e+nTSmy2aBB;E>KL+K5XZiB3I2@lRgrF%{gR~Af zPGIX4ced6V{Y|}3HBIYm2bR|Pp1aVx=tm6Ly4*Q0Xg$cBjE+%hXg%adz_cFiPBzOG z+SYSxCtFyX`O|utOZ(PqTsdg1zK~m82x+}VJyl)V)_Xk)v_AM_;EoZjSREgv^(C{C zF7o2elAr96DL*}yS2E2U`{gSWOdRtFB)|Aa_~e&6>zqG`V8yBwA^Afb(a9f62%3sR zNd5#u$f;X|ZPPX77?NH*85TK?`I!{VlNs!frajy>o${r1Y?`& z?pn5462A^@Vn>u?6rv5@Y+Ic;rOi&)?`*S2i5RP?gtR%}GEtio4x6?)*G{H!R7=VRj|t=cfnx~RulE+YCofwe_ODwncHEv~@kdw!OXI`z4T|?VxxPv>i>T z6IU?`X*<1llg~##$F?A5S?m+0FzuC_L)1L1B$coWe_v)D&KGrKV8gQU%&sF}`h= zmgqya%W(IB+7-IHM(s*n*xFUble1m#8~v7c)t=wdZh|Z0?WQH3+q9eIQlZ_F8zx^- z`}rns_U*OwY`5ENIJ&j8kah-(BbW( za!*ARb&ppOMSW{s>Y^czq!f+z1{?1l9~F5!okcT#q=Jj)xbt`k$wg}tSg~k>!+b?M z6PT}PPlESVbkx}yEYc9qt?h*rU2v#VB<=+512y_+t2qTru~ZA6>h(_)_HHg*_}-T8&gLN zY47DKv`@%YXn)MXwS7&Dv)f-FSg|T&Nc&4V+oUN9uY00ZY8+@M*CJ;^A(W>RBRvp_mfyus;^eB%*ThwKC; z)1k~Ar9EMSbT~%{OddH!e{x`kmTs zNXaq}yOMS0$Z?7ZLrXRrqD~!SK59pli^ z=fd34b}~RZrVt{vmEe$$>FP(0G$VH`bO-EM9zS5mDhJ1o1D)8S2$ysz|^M~M}s8$HIkV(~PXgv0vCb%EWtli`4 zq)Nf7qla{I^I1Es`B5@sCoj>XQ-U7sbig%YozxGGRmVd*ovDTEQUV~?9Z4vQxC>pD z;?lV+BW_2p%sl}sE4o3!mU-?`S-*H#$_6=+T-hiWsIqbHbd$a5rp3WoHp_#vY+*c{ zWy_QwIzYtXJ8VgheY$$oMEbX$m0j|-#X3_=Y&%C1hU}ai zKV;_|Z>Owtv9pk!D^xsUcLhK?SJhhd&M^@fsUHq=3DJ41_t!*M);dokB*ot+0O>rd zc27~kSnfz&=M8Zu9i2CO8ng2*k0_n@Ct`3U0fV#d){RLs=zPWVManIAG0RiT#%=A5 z1d#F!N07=(ymcxsk6)+qUb;>tkn%ybZwyIzW0Yqx%cr?^0dspX!gE$J}L_ zdluSd79s52r~v6Qj}Z3mRe*F^;gx4~+2q2vJub58vWsBFE&zaZIhZj035RrD&U&UNQJjkQZYSVeN@c$n6hGVd}v;=!i8dkyX+O4<8ZDp)G&8GKq~eT zBDFU^Kq`(p5?gWJoA*-UqE=jsThy*eo^*B1bVapmOG2HvJ0c)m3yojo+!O)n+S{3? z>kxOEuEXQe>pIp^WL>AZ{N8n@8N<2U0n&AD99CVI8${f@7a(2NC3<3Acg9p#*Td?s zOqrzmqw7)qTS)=w>K3tdy+BBczkLGI^)eyk+(7}U^pd_RvuZQfD_g2Tb8e-8R2F-5 zsr0g5DicZ=E30EwNbYSFkjl|Nd)87`jb&l5s~vy3JBGQ3~m{z{v`W<&HtREp`OD+d6{n-k|~M z=4J19+Z9jXZu{Kfk0k^v-A+2p*UhWm>UJfO$GdSHk9YS{Ub<%|F2zHT?s-md-Mz}0 zt$QEu_5SfeRQGC^Z@P~rgq%A%Al)Z83*CLD#&WTXIUwEV5F)kLb3nR#TVLJRC%m}% z#us-xbn1SLQ16C2Js{mr5<T*liV|rp0>j(J-w~2p6RY_ z>**fr^vv^qOsG@qsrZjE1#CTgJNmlk00kp<7Yd|jwL64o%X^MDNsr#`D3G30+?Q&z z{CcW}i{S?8Y1#(9$#?CTUj>Mj1+j98e`~eQ1*Tv+@w;tzO@`NJm_Ity>3bg}vvPVZ-M#cz@Oo489rx8;0DCP9!&JZS6H-^1%(&BxYt`!l=_ ziG1?j@dY|=01}E2zp}S$KS1nMh@C;ay6XR0>SL)X3gvg6dTRh6k>QZYXySkVCDU1meV+K}#;vcwYc9#Z-2JDMI%!`>WB_s74S$-bL)1XR7l>~w3hdEeD*@?&TsK>%BPU;dtMaVUv;tyP5yTQ* zHsS|*O}j-uUjPX&CVmCOKGCVSLn6C~Z~JFjwlc$D2!;{ASN+qT@S2J9vkOi=3$Jl1 z|D=7F!SFf>@=wY>d>>x7Ywq8qr!tb$#h7oqEklR0$3g6g#3TEx{7S*tY^qs#cy7<@ z3PuGaQboLh@^oaU?>{%%-&I-Enh3F`65lbPn{u7gU6*H^ed}I$z1X-;uRrI*>zl?~ zKgsN<^InI9iMVf0P7@tD3xZtY5i|Dgg4bM_XP@}=2?b+>DOBYVC;prcukkvkKepm9 zye54def4w$c#YAy$%(h`fY(5ow^#pL(YLlktX;%6pLyjF&=&)UYX&y>7G9%xe(&NJ z&%kTL$U~<5daADWSxD$S@kh7+MwNhl*pxDJgBGV2>*FO;zszHYu6Y6&jq$|awN2}< zh)slGD)Gm^n4-hjxu$ZPKTX=W6ke}cUQqKwu1>NU652*TqzyfzPs>#a5nmhpA}JAu zsOS=pdu#G=ecS+vY$l#?rsD~CO)&YT>?v=+Yb(jS=63f1rKli&{^p-5Q#&2Jx$Z4F zuPfFC5MMFz)s)ST>SHOyS3!Jz_^#}5Twm+jrmt=@(%PPuUhhY*XQS8K&}{}7$};u( z{IhFEF2U=9$Qyq>@-OiE6Y|nYckAMZE<-}si2we@!e;;rNyP8;4eka8s|ZYg`cDtS zYb?tH$4{CIue&HeH-GgtAH=?*97d4vVN-(5cm7}VKPl1rKqCE#@BHGuG9&04-5Bh)8&p}T)*4eCYN4wO1IfzC=(LOA%2ecJ*10!1Y#d2UcPX;uBNZ0 zxrBw^IG&`s#%cP?7yfVJHXynU(VfKM!tT(yXG1WLc*h&NRJHhQh%cFVT)∾k9n& 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

1GW(Ccd$JJ<}YRLB)Jg*6LynS(YMZ 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�z4+2{23O@5lM{x8_hd6% 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%0AoxcB-mH8guG$b9? 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ⅈ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 z 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

^yUGhZ- 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 INLrRnhC2-ke&y_eYUydS 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

  • e>jiK&X5dWq04S}klI zioJna=QwJZO&$!bV#BmmK+6n6Hcc`gm{_j9u2lI|ixgzA5Z5!3Ie1?^I?6WObgFBW zf`gi#yWx*R0epAFin^yFD?Br?BV=pgYUV2`WL-nNcPwC%)t07={NT(5w_S1pB+$Ql*ucpi<^8l%mK{xwoy<@ZTQM z+M2MpzNy70NT6Y{vr8PLXCYfBwh;BS36C<0Ms>@%vn3D_f2o6GudXi=`}M$I?>#dX zYg5Ipp#7jR@b2SqKBO#{M(frW;Z`D05NBYKno9(v0YPyeRBpi}y=!B_v1U=<+0{!- zq!|uSCHN&FAm&)Vs59s6+p40@c=!3>I4Q!Fd|Em;y?4h!51w$WK1S8u!j8UjkWsob z2XhTh*4$e#5w_(d$8<1*?W~W;h?gV`hyv?X!+(C3IE&i??0gT~(plh5OkJiPqtOet zpllEQvD?7W1*FC$D)!%~Cw_p|#RLqF%iAh#h%d%0oK2okN10!w&RbO}J;A6YH~?8o zd0fA%l%ZS6b0K>@C2@s{n3{a|o&ZY~#6x7Zz*XB_N}y|Zo6s;n=WNs8p^~wyc6f#* z*rhGv&8HB5pN#yp#a%kYXwq@mG)09m3F53;z#p9W1b-<2G)vUwXQKG^ySSN zz-jQrncE<{=9+)nT4r|Lxm3aZv0Kw4JQVo3BliIsF8Kvl%*PVwXXX9Zit1;E1xQ!EwA5B-T9?x-e zL8TJq9o2!R!m)UFGeyV0S2QCTb|MjzkBuIB(e zr$|#|*P=kN0r;6t&aa+#ht}mtudu@Yp6_3EIx#(LU0BY7i~^VO0LRf*#nIrJ`4OrA z0w`X7GWzAd`nR7_s+|XK?P44uan*%iUSqbi4~xR8+$)yGrN$z$axdM?}k;09y z-2tkC$ij7&jh~;CL#^pNZ;coIuI2mH+Iy%vQHZH-h)J+9wpkFUKQJ-0y*wThQwd0G z9)}P}_iL8T@aU(@X5e&McvQi?z3ZsDJCt>Xt7O|-UZ`11%GQi9V=`7FyA-;xTfF45 zbrw~~-M;mTzq+pF7QR;>kaU!Zv_M|ge~ z^Pmp;hE@@I8sdkVbE5351Sk36R!P6~!FmTR2U__L`dzl*wcUPOw~!4695s;*vUvE! z{5wo)MFJOE7$9xE%#Fb{aIc?5Mc88hW(DWg^y`zn?@$VCn&uy7RM9&aq2GD`ZFkKO zV#XOHI0kf?`S^T`(3z(J&$^v#K3QX=qP^Mk=2Jl|KQwz4=is1)uh=~gX^2o4C#`b5 z*(~v-ZJ>$LJ7&#bLa$q5sgq0ks|e1J)HYB^VVFe^dVZ365LC2qfiEqg@z}W{K&76? z{XZXEcv3^46s*=B`z5LxwUGS#f@NZX8pW9|Axaj~xk_6Kf*SU1N?-HGkM(=w{Y#Q2sLx5mB^0yc!!m+h;{Va2DK z`=BMa-Pjbxb@lZrO*)7-)F5L0KpP9DdCv^Tv#u3-R*g<>pEECKZzWJQr-D0muCXHI zuU|1`6tO^WIAy50s9)og5)hv%|M1l#wSovJ0EJ`i+xRc7^v0S0l}|s$ zU2&(5ipC`9x^3{7;_Hzg%XOt5CjuU0lN4C`LIIdn)nWQ($zy9QHYN|JqbGxalKI)|pyeGpz>?Cm+h{cv&mIF71Nl*p zgy@rBMb~ZuxiZt9k5Z-STqMy(P8~#fe@|pK-&JZdSvY-CH67n<0mQiau!`(kVmhn| ztD}+RpT=H`LgrSxbAo?9ChJIX`3PAB;MboQwd!HNny9SuA1fAh=DSngGY$M=%8Dg% z0|t>Vf@pMG2UL?Ij~8B#`wW(*h65s<8qU|RE1pd;;b?CfqK;{6!Oxp88BC*&al>Yy z%@&`O$@6B+TYJjvoL%X(FE{c%V)khGF4^Pf^Tu%SMSg@#!|Y{@cQbS{xMIGV|eudEDh&5Wsf z1q7=H$UC?4YmbY8;_oVF-Iw{8T@4a~<|F(oMqk8gW2N$c!}icYahA>F4Quvw#=2vjik`oV#64e|3s(yf}J zTZ9v`0!x2;8e{RIcVIm6vpFbJSc&^~j-S!uEcp40ixKPGk;!&a0lZP639=a8;Ed>A zv>#$;hjn$KhlD4>8lCHykZaPom7R8;soQQuLPB>{s&CD20?k$>y4s5a>sW7bmYS8+ zUF9J^{C4aN3S~=~ayZHJ81ZF95ae$aoYh}SXmptO{GdX$#OTyQpMGqf!mM?SDIe+| zurjb1Oz5*(TYIhYv5IvkI%2oZ=7xq#Bda;`Ll43&j-RHIJ}vF+U&SoIK^LtScWo&q z2NrZ;<%D;)#2kG})4BH3qL#}Wok9A37o1w@^5c`Sim#=Y-e$Gp!3MlA0kJ6o5gywSp61^MpJ3!Z6-Km9Wl^^r>lZfOsNfW_}6o2*Yq z@S`dr1Hbb#K5{-D$EK75ylmEl1nY?=IIa~i?=>&zG0zk6CcOKW9TSN*9UPEH*@~S; z5y^Qm&7mNZQ?*+@D;JdTrYPzOG{Qdb{hgzdzSsyY9PaE>8Jg#Cq5pTQX>`>Hptak) z%wYG$dsxAYGG1Y)@54=}y^As5m5yqx)AeR*Z`EVMmMd=E@fVO%e#kL^Ul0RWwZdH- zQLV6kU(U71^Cf1y5*ip~QCzis!zBA9`cIiqC1bl7OZ$QL#>I-nix`ssv^5Rlu5C;s zoy*dh3`x?1SMEPwTdyCE9;6H<^+DJDRF9l zs8zVLo(5A&KhM$ToB4b)&osVP1~~c3st}iw%ILDFXC}Shds8-&QR+OnP#oz$S9P03 zY(-D@PsA~dzgffjJDOln{5XTwF*(7E(i5g`h3wbIzkdXr;C_Wle*At_@&Q=8{3VoS z+XGbr_-WZGa9z<411q9%3#NU)#D*Ktl*yL5c!#1UaY*6*wkcz~1-cOV_mC!e;1=rk z<(8e})yfDlo|xXtz#1bW(zDACRzHn);@kToOFWZ;NY%`dYhSiu#f>F(Y|mx#IAAWY zG4^-7Rg8Xb0{qTWZZhF+U`(D$inJ;po3vmqbpV1NG;I)0eCQDhTpG6@Q;bBK1~9(4 z?iEhe6-qN7iT>JzviSZ#%mjOr) zIv)9ec2&&I5vM}+Lv7xztHgdrkauHTwFxc*3+O{jUNUhC#FKU~HObAZFwFDN1HcCZ znu2KZcNHtO?pOvcc#$MAb`*%3IUeNC+|a>zmq8!03L?SNO%8|w?xXoNU$SE*8>X}Z zip_m(8~PwFI%ffxSKnk`T=)}&YBeH^#EesIF=nv^I54@hV@ud|T7pQ}CQ%$w5w{6L zA1!oQKQ9u0=z%&X>sk}89Rl#Gy2J(s7FDFVRrGT-kgV0Vz|Z3P-L@7A?kSM-AwV-OnwuDGRq%FJOpSj1-QjT`|`1kB;?sK_P zvRe@q`5$=_YdV%&KR?C2evB?`pxGvwMghqYi&Q_bJSIko(*r;EeWDQsM~LYemBQQSLYz6XvoBHmpUSmoz6`>ci-Qdj zca)}*GNa%B1`Gy`H15Z&v6Y4;?#z#i`6}C-DdpieWs6?!0xI3wd;*1Snd9=p+#F8% zSk;~Ks6wxsjopae55>ttH_L^?q;-Y2zwTch=a4!$Cm!p#aYWGhfd!i`&3z(wzMD-VNHVFtmY^)^Y82r~H{*GkH&ASzKse2J6o5Wa(>@ zYt?e2Qm+pleRgZiy8_)x>QMjKzw{&LEbg=c5@tD~lw#i+J13-HwWzZp3dV4=^hrNF zGI^UEDSxr$^KJ5sgba>!kKN|%fogO|6~Gc4;o(5CVxuCGIs!??UgTP3q*3zRUu={G ze7_3Q5f7!!j`Jezg@(B~Bi44F=KE;kb%s#jMr6ixzxD<_K1}Ki3o)bwk{)f2%5meR zjf+b@=4hd+vdkh-9S)RCwr$tRfdp;$gd6>ukW@_ zr7tIHH$alDQ-lU8AA@UmQbFe*bFa*t3{0sbrR9ffJ=dg%SiPQ;r=#c8H#p$MIiklV!#O1NKzoGz{mq=e40Gfv0oaM3?CA$~$MUgMU^ z^WbL|vlmmy#V1;J&mEBfow%}g%u12_LGH$D?}iMT5t~z7q`+@XA)j$1RZli#eVi#; z2$IH?t{So1lKwD?9Kgj=iSBYB|48Kk^XJy+&2_+U{wUMNIfZE;d&7Hle5$?TEaZ8S z>N_2oRw}{ZFruec7_n0GG*rx5-hi;JA(yXaW||v}I9k9DW?CE5nTy$m584Uy*0n^> zt;cv%juNxQ{8C42d&6v(bKH(A2-fri8#f*J; z#18v(qsrC=n#1Tsf$=iEjc!>`6$lCKWJF=2EiIw>c}5c z^;67){Z#3SBEk*Hlg5C7_fkXeC#21J5C?}Z}S?@?@WQsFjCcJ?nF|_dE zWdaOqzhJy`pq*SXd)Gfx^l;^-p9*{&GEE%YbZlm{bNt`dnCAsRXa2{& z(C_tHNMX^IKNY6Vt2!0L&wS<%5!lLC`bwx-3(5;10LTB*D!OVE*fyIT@4p?wOv&eH zUs&Yn7t*uDKW_l``CE~lUs_^og#fqiqdrSsJJ#;8q~o1ER0b>Z%>4w28=u#d+nkB7 zx(AT8YJKVxHySYnJalB?+tMHH5AV-EFMZ^)w}q$)#gyM5nC%y(D&9rv-Jn!`4kEYg zUD#p*q7;0o?70ulLj1B*b@PCO%&-zSh1X#(ExKb7cn;&%g-dc5%|EdWqRAGNnp&T% z>$Wt?B@z)}vZk>6KQTo_ipe>L;GQ9|v5^51RuWt-jT|L;dI)CEoSum1c-NN05$&iP z=wCddu;z`#Y0M^QU>oLYc5gkd~u zpizF{d5Z9lA#r`sNkU(a=9h7Y;ki%Sb1Q zmbEl~{mHTjp9f&tN$cs1xibr2V{&9_OH6f8_b z-?-pxQICp+0)I*lDlwnd#Z512E{y83nwg=!aUNum=x8!s)ncTYCHdaU-)Nh}awF|U zs8Oj}sLQYZFo`iGRoP*mbC^^Dqbe92!ruOhc9{3Pr*WlHEftZPC z-Z{3oVBQ-{_~*`zwe%@cJoyo-5fznQO3%p3Cz~U>+^X<=<7&2tQm6p!IM{gG3XRh4|jG2sv z0EQ?HuiiOY#>V8i+x2zr%cI*E|KjFxRt!lDEZhyYfB5p_tZH3J8#0B!lYftzv!$8i zgYkUlp6ev3Hr-Pidr?xIMg4>GRTmt@z{-@eH)LDhZEh_|&KB$JDuWGK*v50Xar|5= z(9oKD^g!oU>~Wj>-oztv+Z+m|d;QM*e9dfV#r*x@24l*b^dW1ezkc*bx#-ie%GlmM z;$0NVf`Yf%c->&K!;nFz=M>V^Wf#;>lbw_lX}1O4>AQbMDkZrivvc!)5wC4hZR$BTb-98PIC@ADyed{T8hmc$1Ni039s+>B)iHJTPh zT}M;m{}CVXAg5v7#$4S!v72WB!Jam+oGW~15Ukh!dN=usw3J+&lT3+wET1ig=Y9p^ z!>&S#va2?5T>n)n-=w|qo;MRd?&WPG)B}miA}(u8T{Y(2XhBZ2Ddy|(i}eSe^ohpT zvOQ#}E*F$%5jiyNHlQJ^lCl{>lyt*Re}TTnzYYrRtX91D+80)8mpr!_ylvQLCzw4g z(IOl_Wut0~KW+prccs5JNoR$*bfL+eTbWzdpS^3`3!rC56+5;J>vVGQdRdx>8!VNy zX3>))fRfOzS?xv@lM6rEFUQh(tOz=1jlGMt`{5+^UG5@^i>Do1zI7(iO$d5_F8^p& zq1{{BOM62VR6V&i_ymx_m;8QV#0si?Tz)o&Hx6{OcguO!X0I3}<^Rf9}>`N^P>+a#x zAL#j(ik)L%qsRO3G9&FK41|n-HKv8SrJ5F${Kv&@-Dvo;-qI=f4rD# ziYk2m(6D^Ke3JVhJl)N=+GA-;_kne~`;u1|;|3e%Ysaf~no=K;5ypN3*Q`}g=T@3= ztWY`>_S~Q!w;vpPtO4wP{i-mQ-W>xb0_>3jRdu!+jNSb^Z&M}cfX>Vh0qdJ*@S9Jd zMmIAXm#H+DO=TzOTTiTVh(QDdV7yNYRWpO393pICx|t5>~El)&LF6 zYqX4{wQ^C`IF6RJiu0AcU&U-T18c_zPqw}MG@ugqSxKcRPi;8<`4i~LRi$bIkjv8j z6)nP}*l^!0jW)vt?X~1{?HHTN=&-1F5*GS=D~Y5GnIw0N9=METZ_;57YoL|nPA&Hf zLaeSmiH=dr*U5o3Mx;Y#@Yh;J9#^~OFc%__z1gR9O-A04>xeQ-PPx*itG(e5dekPU z6ns91x@J%LM^?%)GquF`ZUd>pLzzg`KH_(K^GZL@iS0aC`z0K3Z&rMa1A&iu2z)u& zpKQJc2=(6~IE^-ER1#j}-7n)ttTqjI79ug2BIw{GL_vYA8wJhN{_?EGS-fQ{M2aQ2&f};) z#LzMwyUykcszo1H0To|20)-ad)6gaBos5gLO<$N2OxfWS-1{BBni^UfirOzXH1b5Oq+hI_0BmCt?}LTo`yc2zTS=ca!LahIC8>e|qPGz^t0taP(I-o@^$JMM zYS8G3n}X{%C1wg%pbWC}Sj76!PB}XtM{Z5oC~>y899g+OU~!FRs7HtWW}D``!$SIg zA1s!AdD6r7oxPv!d}Z_YNuHW2TTF8?5W%me1#k+V?KaYLiumy&KHBiq0>C{zkSKZs zXAMBh;vRR3JZiEDP0;*Eu^@BIpdhn^f`A_q^|<4}qA0xkyMP^QuCs%5cy2(lRnx~( zR+T$t@ci29T2TELXa4MotG4^bGk2V@a4FTRxb@WHyr%p$SOAlrn-c}!TzhyE<#Y*0 z*kH5hEI>hq3C9$}i6og0?v#ylvr$VoMuO3rHCLomUFXhO?B$eTT%STEW`2)+f2+{r z(vwP0WA7%C8u1+tfbeYlckQ1Wtki?#)<0Fhvbef>m+^;-pc34d`^F3`k7p z!^GT@9aZ+Y$p}CKbx)eQd)Onu6T+8L4#|n^2-RIakKf+`8hG9gxc{1t-Dd;HQLUwH z?x~Hy??5Z>hK)7);xN70Vl!oN%_V#-17Nk@`scEdizLcUYKj!F{7`1Nm6!6m)6k4| z#6nNDFl32aPP_f|hS5wB!9X7${bEM7O_kO1AeY5e4}@I?O!U)ZK`vnSWMgU9$Ham7 zAQ%h{K7{Ofc$vh+lU%Fzdae~o;u2aM_b&NTA6&ID^8cx=Gz|gG!!d^*!zQZ#)T<9jjN zPKx?BU!If&_&D->WC6Hly)lwG-!Jc)T+0Hu!P=gPk@BK~>| zMv~JD$PBg2pZV-|Zw=pj$Xvomp!c=?vyIRu-QVC*-?kMwkCsb73 zSK`HlInIoy^7@!h7Dm8q-Pdl8Q0AUCBNOP)ar((9Xai>?qqTT0H> zVsNoO-=mjpLE&68#e;l%xLDTdzPza5vFmbllp%yq^G^w{v=|0V(H%;Aal>GQpd9g8 z@n#TyAp$5Y935AYYosy0mozu`(xfszGiJFt=tCt27Z3FeB(r~f7Cfi)B9~@KEP0(j zvRB9={77f_g84dY?z^oI@+G1zp{C0jzV(KfvV1{*8KevkDim&9aQ>)fzW$xGmJi9E z31qQUUu&4H!c@gWOwviXY4=>6Aw>{9u5RyLT7A}f_|s2$x|Du|#=$7LQnsXmc#1E- zDQwBH`&!Mo<%J$^umA7A>A*8sL2jS@-}2Fi_=n@XEt>ksBUTKboAHFW&^ zu!1gjzXa8^Q2=jwmlPCs{A^&%f)Tfrw7z~^8nCDzfSS8S`O&U6Uw^ls#-biQowq+^ ztG13o$r;NB_VV~m2iF(9tG!YhfvqU9A{7}C9`82{E?TCNtOLLiL77IySAQ794ieYf zckB$D(-}t=r7A`-xOVs7M&vfHcz3w#>tw|_3+zmgRcwv~I5X~N*BazQ?~seu@{s@C zDKs2KPqBM<#og_A{BbSu2_Xmn5aU?_6&{yVR@M$RF3C)-Y}=)`4yfRD$mIr1s6U4Z zQ#e$cIbmh;=TkA`(rjf8LAxM)tyjo(WP#_R5}k;&2|+A>-T}$tDwg1QeaR}ItO`+7 z(Ew$7w_j@`(?crK)J?5p57_1aeMEqsQ@iX=2@iamR(G%@xeEhj4>+qJ8-WxGy-39A zzMYg0Jo!Q!5mn}PG;jm7u-Cx?UcO|j(m1)*{X9T>H+=Cw)#3JgSYj-{6G8U_?U9#WkH+?En;c`_ z>>Np4(ZX<+ki546K0v5g0qNytFPw2pTu|;xJQp`KD_*zek2sE*a#U;!8JA~ zo(DIz8s!u+_}P|DaZ#zr&!MB{?5Jrw>;(Vn@fz$ABbHaAnY>ouK_?a5Yor_!wf2nZ6AOBcApKsTc2(De@c zKZ>sV5$gYse~RR?PtHn(j^a)B9%YuxUU&A9y|>Kb%rh%z6J>A0*&K)Ly+ zFYkZg{d&J&kM(>!YciEue+#Ieh-&r)|E>Rcwv%OS=QX33Y0J1a{o;I_;3BBHp2TgN z#?Q^gttqova&434eD|_1=jqaRix>Nl?RVkZoyC@czeKZ0auw5^J|#Wisq8X<3nn67 zirMzj1(^v^SfqqiPoG7GKfG}olveuLTr=(lcS<{wIP>vdU>VVcnk;$^NiwFylo#oI ztd-9)FLh(I;MKolTD2zZ$j)Bh(shT&WzyTwm1kP%XJ7yQ69i^DFq}NAGXQ>#TC{qW z$U()mr^@#|`~f9e?!J*zg1V$o;3P2ZYgTN{w)=2UT=wuwWdMK7&P#$TSNX!^nx7JX z6oP7}USXv1IME#1jEj+O}+o4Gp!0 zB)xQ=(EAlW-@Fs2A~gDFkl*+)w<$xf*O&VXBx~g6xVm!Adr)y`LacY%v{&arh_ab{ za!{O3*77xzN4jp&e`aX83{jQU%c+MvlCnSXF9Qr6Y7PIZ0z_ zORh?%Y?@xIT#ue0_s?m)|13DoehtdBvewTN&UTmN$`t}e=WD{EqKkNTqsm>Gs zFwMzcakaI-(PZV@n(F;dk>;->3~n_vX?4TLztY1e6XNhIQB)zZbVy zm0Mkph6YTstOn-049yM9|DBbWzS|oOvA@OR*uT7Y>MCy2vV+p{Zr9`J8u+W3Pw6UI zM`p*O2~NZiZ@Jw%-%fs^6%U>&n44VCLEzWkS3LZ3hevx3vN0DtYf@|sjYa*nt~P9R ziL4Bh$7ON)CdEnoLqo>bgF_K#B8ei>Y! zS8hSp4)+shxTcY>YA)>aI@k{7^zXmiLh@?ytbmn!3wUOK5SWN@oUbH$kD#Y&O{xtJ zuS0Ik?L4^>i>V|p+xTxP=WZ+IZ{GSGtR)YW`1$UBS^x7)R8%xcQ(H@0mPVUjPC94G%O32gsJ#`NJtQON;0)#H1fq-fi$D;09Ilu7}gHw$fCF~Kjb z=RFee`(cBH>FQrk2h8@3WquEDAC=y%T))2LkDN$#5G07W3ELm|4b0cK6g?C^k)G`yrF;S_x1TYg_ z+<20vPC=AyJ255<>AqMpr4LyBXM+b9zfjd>TkRGN9Hwlko z^$ifzpy4mM4>RCHI&6ed*}SKy6j7m6F{C#;%~nP<6cbB3=y@9_AlYDAVvDVFrpfo@ zB`w0)a8=?N#YmOKR+L8T%jxiCf;n~^$-3(BXva_F{^qN-*$qMMrr5s#b0KI(te3Hr z!8H)N1t5R@oxwou&BABoPk(y7{uKQ@3C|n5_h+0Lu}Z#IUmrGD z5s*GzJ?V>?WJKxUj|V0`ahW-nvTU6@7ZIstl4I1^GpCo>8MHz%rXVgD_Uc!Xo1Z!n z>^Bh(Yp;CYrs*INpZ&VfmJQ8m7%Y+jJtab(Hu?M&AYE$*r{+lqw17!*$;*~nevKet zUI?Y%R9XN<`?rI+P(`#BjOzc~$Q}|4a-w#%dkkanBZ=w#NmvC>EC&p3FOyV~!T4L1 zH6FnAE)r-JG@KwBVnF^c7;V;rddlPy*nMXP&4#8}AVw;Ar?S1?b2l@96a}%QP0sbV zK%q#ya6FH-zxW*Qf+)~LLadJP6mW62EXT%Bc`x{Nu&nO`LzZU*zwU9YZBn1H5U( zl|p~C#&|Q-zQ0NQx1~*1bXib&YHhKe(7rU0@jJW}p>C|+vefjJ?!>5I-!dK<&ZW9G z$k{V}0x4_fVtqzdED$A{bRTSVW35zCAjb|-WXWv*6rHRr5?U^z2`XfGGD6AY1%mB8 zz+H$3jC5k}S71Y_x-C5}G%OLR&@zF{gFST;UxYz1+`wL&!Jz_MlwmFj+hdEmOAVKq zrWDJ$DndrEXW(6fc-&2gjct6BD^OJ$%azGcJ4xi98tNyTN{#AcN(|61ZeO=5ba*SnDS1U50u_UDu#)cqEW_7p6vYb@mPu>n1ZT ztjn-5Kw-5}Lbc3|$s<4+7WZREWj^A~2sN_d66PRiAY+kBnHQ|ws|O7G^8wSvi8B0k zMlxELU=q$Di#7H%hRsdN1H3(;`h=_e{jZJ>(T==0+I{<@#YJ^sk%2ih#uW5KG0b~U zkKA=pi#zqLh_*SpJ3U5G)>_8pA~eJ{wSTEOY!9z5ZBg)k+ze#*4zB5W{l*;}1D8Rm z9n7&^X-D!SW*I@9%avM-4UZv?51IYUtPS@kqkwXK;uMNmgQAypJxd2+POG`$<9qc% z_yCH`n+kdQx(~|myI|t-Y=-=2^^YqFr*KS1^30ZrYp5{I{6!O(lk>&LH?{nqDNw<5owDw_}5` z^- zNJ6)#6By2X^F9@y%F46VQMqsQI<(s)6vUl)IS36cp`; z_ze#OGYobGW$#loJCS=AtxAKt9cvg2n9;OwGQ6YG^UbJKT1e?#;*b5Pt~PeS*|DS> z;pD87@!!#mAmVZ(e?i4!pYaXc(Dl8UVM-ssZMzU|3VNXcpZqpl$#7u&9y#}3>D2Q z@up8*8=`8D`}dKZ;3$a~pu!&|nZZrj!3MA=`fp_kAmd*br*$}Q_R74nh!vBMv}FGj zcP`n#n@pBw`B-~bm)Xun+~NyS0SVl<=qpOQ+2f24yTfP!e_6_{8QP4Sg$^ zUU4$@r1prS1{uEoD$G#tOkVJ3G2(KDy2(aeg@9p~1+wcR-nSBPKL3i@$7*un5D(6{PUaYa21y1rO}x=kRTq_H3Doig}WU74nC zF9ldU0oAbejn&F=TZS7b@7b3HR5!7N;!h+N=X!@;w*sH);ddz#rz^05M{O*S374y~ zWPFv8Ym_AL`o23|IMCX7NC20Kq!|7MHZ22fBW4RM=7RvE`Uk4WaE78M9%XXd%)P4q z?u|C?oEV;{QQUYX%AREuUZ%Q@{PPOag+!4o8;$gGa>PSzBo@E7Xl^9P*jAv%S>^ib zZ135|BXEV(Qe^9<5F`)m4>4|&#=MY2(H%2chdQMi3q0C7HMj=WdC^eNQNj zu#8rApzrY1W#X8C>&)0%0#E2y<0c-FF$lGP z5?|-zSu|can8#(ox;|sy9OOoIo(jkF8JRvP?Nm}G35AEq(iN!k(2nh3wc5C%=9mN?`L<7 z`Fun}c=G;PrX`XbVE4q3Ep={WPcMloPRUq*)Trcd^ek<)ENv+P{q=xKp&p3^=--Bq zCR~PynuErReEpwg8Oq_Q@188C+?}lqRW&5p`yMp&#w*`^X1(sV0;&fj9v$$_p@%mV zU;eSo@nE3FE&B^1l>ih|VtoGY#OXfl^f9h%omT7h_meWYC(jKcCttWm*YDH#mh1awJK`oN!MMfT4c zG;L~@5iA_w?t4zN6(xG2C|soP4Rlt7ET7iVkqbbrpuICnt`+ccvd#B}Z>Ia12QF z*}Fv*mZ3(UJySUp6i9*r^Aa-44qV#FxYA100$b6g?Xnq}#V|Z;)taL`9O}*3dN%fP zSu8xq#tAgX?lED&W0GOPirOp>H6DWo6!~ZTwx-Gn|DH>YW{)d#*7(dO*o0eJg%L-= z)Rp%*TbFc0cwQWBg^I=H0N2IDo=p7`QLF`(B7zCd)#|^PPbEK0iS2V%5b8jS7(6+3 zJ^uq*$#Jzd##ZB-^S_qn6|Hzo7TK6uTp$BBxU;!kiCkmh3a@HPZYk--drvTB6#$Y( ztB9=vrL1YY)U)JH6_Wn8>LLLKL`wuNv=`S17Ugv=*}M~R;^bG$uDC_F1Xl<50Avrb zXS3?4wZeIYTpe-c#Qm98buri$3oePivv~|r55JT^jfp=#K0-QYmtjZk?(HH$I)$t0 zT}$Z-P7@11A~ySw`|0eL8%F#28_x2WyaE(^V}Hy@sW~ExgVI0XXJ1%*Xt@&y(z1}g zAcuJlA@iBo@pvnb*}3a6CN>B){8C`Vq?@{RDV%jnjQ5wpJRf9$L*~FA>W*xTLNxS& zGPZo}Xk@aeS=)^s2y{&A0N3`QllsFq>b!6~IQDIr{Y7C;EOQdT%m~Wx7u3ndNk-s{ zMHd^KEwxbvW$mNSNR@1+)M@|lqNS-mW86}+t;bj)rB(`GabT%Lr<2y^Ejv-&HZ5HK z4nb7(rWCB#3>Q_t+x$&jI}WcN?_@J^p|CF*ig&!TxUnM6k)56oq$k}n1i>*P-Wz3{E%HRGX-#La62NsTSx+i&`m-g`=oE@$z{=2~Ofq2i7XSkE^{z*u2UvQU;U2^lUI*oA}b^tm7KvZiM*OL@H^6s&m05pY=SBurRy{NtcTCq zxO@+Wb|mbrZe<9hlQegM(_vWnewp6ymO9{56!G^DiF)NN`@knnkQtMdwp(C|AR)^; zhPkGg68rlQcPxR*uJ1P%ZU!6bjT`5wh^-dz=2KLHRjDZhBOa_D$-~1v@J2KhaCVRx zE9Rfxn5YJv(}-LB+g1r(7TvgicIQ9=Db6sw=N)uuNbsjn#9hG<_p0$fucT*hAAyV}=2apuvPCqnOw?*F)qTEW&4BW5q3o)K>Tm($#8a zj?%^UOq7+0|0bu;=ZmMbfCobc>xN&RgI5J0mJEyMbZwvAEhh;B*aPfF{LBhg?&r*uS8q~3}ll(5&(BXLG{$en%ZQ&W}pTY z_(3N>NBQeo;PpKe8_j*2c_xo9(5)uDO99BPgv>Zh8_nD4Dlc=IIB>WxB`Gk#tYvo) z8qXbt+)sPsRrpkU^&uhzS|PFcXGri|qNyKOBj{1szmx}~o1Z_qe;;j0i&k7V9Pon- znBn47VB3_JPhszbm~$pRa-Em(0#ekVzPr^o1r`<@=!JK<1J#b3Y-8c_Q+w8j4{zJ4 zv&EC}lCfpXmq#ED$Ov4*8l~gPH|j6vfDE5~+a=D|f~sSKy_3Y{elIPQIdwDLfBOu` zt28$JsQNiX3}q-PE0{oB`wQ%n!Sl4>gxc^28bBr|eCRCmVh8uzUnAN|upam0s^M6YsH-gLffgB&!Gh}g#L_muY z&j(upBh5 zt9C`1a1)Y<6AO#YYAD@xmdgTFTZhi~T>8Y)`!F z6CYTaVairGBst+YKH}`ZIXl_kdC}iRkvGLQj`s5UP)`#*^YWBlh%jOuU5PGmAzVzB<)&zs4LU}5V>Gd0=!l-rr z4%mF#?KO9PNcmVwAJImWkpxJsqt4tz;C=@mAgeUE_;Xr;GBodD$Z&djy03dTaZ?~XVM z@A)v(It5g~maYw?zmsXjVdKv8A^<9eE{ZFwBn1#mKx2?<#rDUmH*`9>Oo&&kn2$Lq zQId2&Sz3*>q%UP>VMo3n^vk_wz{e^MndI|BsraGfSf(C^j59%#eOno;1OLW5=blE8 zTnl7eJ)DJtlB4EQhhKJr2cpZt?6$yFb(l^FQDww=8vh(hi|l_NKK};JyBJ6{A7vSy zri86GZ*aeDZ`^NulQ&uZPMS&89pY#xbCz&@{$^Dq5o$_^{C(z^!E;f8R%`5dOz`Sa z?~-)j@2$GMdjXIWHpq!k)HOvVR?ukAPIEW&i9^B3pyXi%YICnGv>cTS<~wdj2SEiw zp`)?-8gPjq(9H5(qoGd|=HU)v)Ka8jkHqz$R&qNb-UCeUx502!kt`aEsfU zw>=o+#lNp;w|mz9#6Gb^Xfz`@2-HK77C98W*|IbfKUn3PGG6!i8n|vnK`;J#R^7+L z4YCCuOtvkFy`lT8V;Yh?-3B1piKVM8<3qZVfVg#3h+uS*c$kTEJJsbsMnu#uA7)J( zb@jLIDswr%2RstyWVT&dCoDw<_f4h@RQbwTqB-BTB=zRa`jECOX3rW}v_=suN9)7v z#9-|#GFS7eQMu44sLSI;HHBf%^FIUtZzyQs$tL3uQ(X+c^e(cy?3jjWqYiQD#(w33 zxO_m257GS;1pP(`dqr;wvbw#(P=jLEMIne!L5RDGl+#a=7o??7^oFdTmzO?fPX%SR z@Jwsa)7@f!S7lEqZU4>0A0pVG;Sm=v(7NGY=lMaIRwxwjny%)r^kI{(Tj%?myd&?I zn~>RdaD7yZy6y3XjJtXG=Ib|70_?I0S5HYSsXd^7FoVA!Wv6q`MyDBEWCUFv!`8sc z%?_1v5>F$~hSl7BF9n6zo*i=;YLHc=KqX{oyl;JmTlO2Eqs{oFJQ->#nQ56IPqa0Y z4lUu&%C)_o-oaA25^Mgv~0Qg2GYTE#R5>y`1JAu`-q0Yv9>wuzf&h0=_m7 zpZ_+~l?3gY>))j=oKOB+05&R*Q_Daya41$;gxjc!xa`at-OqJd^jgBv7$m-iU)A1x zSivb8JV#Y`GD=QlWWOb+})^>k$yIEFYP8BN zdgsL2JtC#s}~%ZdO<-f$C z#{M4Mu8ViM4HT@tl#lI&-P#-)AmIQ;O`QqhG`u6#V&!eh8f7X0Yth18@5M1hh)q}y6D+Ntg60opyT~B9-r?rei^Mw16?8^$&3YdGlIU#F>H3;e(#F735I<9Or#Mb9B zbObcKRgBLE-Oa>h2n%$RO7?=i1NZQzmbynveF&TuKaLNM6QYdvo6-srneW6QPz2J& zW4z+$l_DXOO2tbq4@VY(CSLOtbb@w5;W7YH1!UPxG4J2&kGDu zgWQeirM0+BrO=w+|Gt`gz@B+CRLAH(cq@QCmr0lhF%)!4nS|fV)Qs;TW7^_-IQ{z{ z&>9b_kDz~$VMdBJV?in{R2#2N>;uvHgfUp{*1M9xCv}v|(RtrQDJT3cKk=OENuQQ- zzX@pS)g-@HW=9wE2Y`73#Lnkff zSs$TWIn#&Mz`Q`XVU$hE__g$gJ+gQw`LD_TleQ&k^^vcez;z8MoR)}@uuBT_4RHTY9FcWQ zCDri6W;Fc^Vs->nINnP(K~~!j_DLCZRV+bI8OwG{$28=I58vUIcxNRnKDrPSyjkvK zW|)-%++mPbw5X6{SUS0j$umD^PJkACEEB#Q>!}mmMBEjo@}|8R{1(;#@baRF>YHxT zYiVkrZpk0)uoUP3YsigP+6gaaZRC8B2zF?Q3md0;(FByN0Ni6-6Yh`+dzvItjEVMm zY!1L&OkC*k(lDzs+%mlQ%^GiI@XCP%Dp--KL;6pWP21zL(@#8kzJ^grX3KC>7$w_W zQVtb*mm9J`fVxkUL!Z_rK^93&`=x?Ek;Kty(Gd6PZ!oxJDIgthtQ>J5hSt3Q5E}zk z)18U`wKQz*>}l#;SoVRU(o67uI71g$Fp_e}CVcwjV;^uwoVa*ZK>T5sX)1BDX5C6T zR$$h-*u*(slVYbAJZmT|VG4qzRlkIV!4`%?@Ws)>9N|zU+2K5{y1yv@?;e{1kGLVB zp?enl;REYAUrj)O?Bvf66%01IEa*a78<6M%qv(X1FMrGX8^V!q|x1frv~a zSrplC>qoHO_qbBs9tsyH9v^or8hBtKrA`Lo3*G=5Nuo<-6mVcvc6}xfQRU85{^dCK zEW|c)MfJ@SX|TCPKhh^MWLO1&SA z`@;md=|AEs3;)bomesPo4>F;y7h_3c8e?vhOXoY<89U*o`wv-v^iSsdFpUI3*^>6w zkGy(*eupnbQ0@3nv~u&Ol*zGEirJqGJsPv5L94PMW!}pwe}Rss9&<(l_)JXyHyA2yuf)2P{o%`Q=m1QxYW;o zAER=>jlQ6|-T1t-Vz~sDfl)R|jcU679Mr-L?xQ|eUC_Piv&RhHN4XnA@8aH9pr8 zCNOU~lwAn8BFSmf+250a42x5$QiFA=A1HChsH45Y@rr*{iZhy}Gu?kJh;L}TvveL{ zPCds4DwyurK|IM9aY5F}hl&&y;g4T?T~4W*C6NfbEJAQox=sB5&^kbLHLBeHP`2%vf=O zV46W<_NY(~q?u0Vnkg;#p*Tz=p9o%gtQYrmN(g!pT+K)PM(O z^OY2=g-qr;F5bC!PBc8Wq3-9o-A+3$92Hp%`mr&ehL4wfUU|N_T1QHNFlck5Cody> z!nOr#%%L~#LPkvy(->`f>gS4wLVfQb|1<<`x&gun(;ez_&$@51R|bbpi)Qm_?qU19bYX3fJt$YJ++#_MXQE9Bf+w4B_ha0ceO30<*#)mqDJgkPAzU}q+<3gR9&P}UJv0HSunEitAjmL2sbXR>;S@^Q zr&0)5wLG0AZY0k4p_r6X-0Lj^O=z)g?g&AJnDM8gGfOnPZ2gFT!82od~F=%j+wx~rzrT{6flM@ zY$f^AFW?gaXyOE`B`#?Wm-q`hV-;Ia1JSjCdE?al$J-N*e&G>IO4LeHm18YbO68{* zs6_49GVH1@5HB3W^U@_FddiU=y}*GhZy2l>iwpw#?QoyWXt>vpZ@;DwuTCqJebXKA z)|f;zN;`vI@Ac_B%$^%=??AAyP+-Dr-}>F@^2z1TNd+PUEpZ?PZ69{KtY=xPEA+~Fkd4?=VVYsW#WIgr!y{0uOk ziNZTizebQ$%MNh@(Z0B=b4LaObyrBnIErr~h_tm_0$s_sctJ&FLy}4XPks8Q#*r7{ z*&q%x0VDN0;+`rubC(nTr5ZPv>K^mrjfNJXB9?eCWm>p$>hBY_7Q-m0L9o!}$LYs` zK7OGt9L#tGixRb-@l?K%kOK%d`#`3+pyzwyjMIHwV>i@-pSENLcSLx@_z%6p{|k^% zJ4hl2qDxRGs`9(Ksv7CgpM054cF7F?2Jc2$f;dC{Gg)KanuRARdRcVRZdm#n0!@!` zBWcPP8(oZfHtSsp`3h9iA(A7AgdJ}52^G?4&a#l7R%HdZj+#l*9Q$#vWSlat%+U*0 z|Mb?>Z+I;-huv54^E1Z`ZlDh6#p~9!T;VMJ)O^+AWOx&VXb7CPkM8sEhgQ z>0wX!d7e4&sgU?&+BjTOUo9SQm{R7c!{}Ek{`xx`Zs(`3;B)mfcorCxz3b~hOj0AL z$n5LB(kQGQ`5BItW?pTa8lOnT<2s4irL5JB#()TXDP8OCHiYI8u(Jc~Jd5Ar1m=Bl z2kuH4W7>H=OZIZl{hb_j(_jRcFgDZ+a|edJOAw~35|z>RMdOgB7%m_*@01&}Q4qj} zzEQyK{0%z3C?ot)NeB!RPbEP02wEm{fbwGE1y#q4QkJ8SP-0XltkP`;CtQwlWPKE{ z*VOG`Kn|PzAX7}Mr0;6V39;12ttyfTJbzHq1vZFxLfUXvkuaYiWFAvLItb7!;2cSZ zR`V}A#CM$tE3B>3pDxz74!W4Wy zxwOlD*q_HTmsRX>$t_0jLA~0|8VOJ=Rc7=LNp((s8ERwPx$4g4kLss!_#yP*MO8YH zSJkag{gipjMNs?mM=Ics8SzEHg~764uI7T`p4Nhu+PyhM))(USZH9rdV(WhJM6#Oq zro8*JPv-EuJ=8AEQ$=wUH5QAwd*) z2PNcE>{ov`L)obib85lDgcwVG#p%gYFBZnC(NAZUB0(U2c5k=n{z{-9h7$4#P|qhf z?guyNxtiXGq1cW=U($#6cTK|QWr^WwBf9J!Op`Z%qK3 zwZQ$_Xx7WtmV`8SpmL)Z#ocphZOO99Bgx5VrhE~f&u zuH=;l7zra=HywAzi_s>a7|l-JK&UV2;>s_!;@jFwn+L?@WYRk pXX>9Kw0i(p+ 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%0AF8qu?^e zk28ndPF4p&qP~3sCm~5q!JwlzDxTA zbuTRXeXP@e@`F8}2HTtS`};l5RqXx5(Hbd-gGcCSCdscY>-H925RW(wPTRnYLlv zzGSNu*=s6V>}%+dN~R>!AUl;kTef5ml_aT=Jx-+v*_XqyB_VsVFNdP+j$}EO(@ctzMlJyLG(v&X8i2_-fHZCXQ{{GGc{qtxA*Ojf{`Qw=eatMicQV zPfIpK8#QtE@03i3&F;_GFKm8HEpfcdQJ)ztSLwYIz+J2ryu8OuqFAaiX>ZHgsa(q* ze_KhrG3ED{MQ*VV@#(pH7-WQ&#Muw?`I0`Vl$joR!cMVVjvTA~+D=?&ofuh6$rp5y zvWQo5s<;(DXwSvLm+RDcgD^dr6Fg1U*U)TQqj$aAAmtQ%!@1J0WKxjQ8fD*B@byBa zR1vp#Yj){~AEmpD5>t4PIrhoG>dRK&PS;Xb0dtwW;NSh)&fEn#fx2H6Pd;1lFxnXt zlG*CL6LC~Uve^CK;h&!RsR}n=t}Z3F4E#rQ`PK2JK0BgQ(`ulWz#MK?bMKd_*^%Bf z)-OlXmnlhF-+K0xY1@V#Df@e#T=gTGH%H8IEGol?5ULp4lQK1UtwS%y7FT62Cd-kR z)IJ~3KmK|onRT?ft+!g;B_k`|^;_?^+aqJaw07BLsl^RtoZq*Z{-Mdr(tU$JAFM{2 z{fU;E8_3Iex1MyZp?c)zA8Frd>keWa!yebRk3PXa^R(UiX`0&=4+Izep5WF0yfGe8 zH>PFyhun(tvQJX7d*wQn{H-gs_}3zSFfUpqnB`8l>u z$6GlF9ao-pKdB`CNn!Dw>rUl{SM9AIS8DxN^fD$D4iz@L&y#x_y;1WhG>^tcZA z_E;~R_{#9|!^uyZW|HoMEFvEce~q^MJj^bT}f3dqlrQlNKA`<&^WZ_na@K>s02S7Dnacg;Ps8 z9Q!zQO79;mCZ^n=s@HCf;ZC#qQq1-7PV{ffgNBw^?98l{?RA!SBz%i zJ$AAF5!3k?$4{`lYL4{ZwxS$aWNWNU(FG1@>YmQ$=x=MC|Er> zc&+w9+3<){b;!!i{B?=e<-#8O*0919iYqeSrat@}AH7*iP?d+!fo;mrnv}-rT zY#{jl#V>6Wb~6Xcwq2id@*Q(kdof!~yuBPCVRj{<;Um$2TTrnsT9B~ueRgNK+D5j{ zKb^#%Usc?7Eyq&vxfs#wT4_axnWQV{22-tla-O{WA|8*~ zZ!WC$^pIE6$US-bO5ThyLbO8lAE^Y*Vs4!Iu3}~3C<39-Q^jE;{%P;wn}2E;YfAhW zW7YZt{)>t4uXQax-d8-awzVEsn-VwTwkXBb_|2?6-#Fb&sN;>h=xc{9N<4!6@q zkoLcxZI_&EHxPgQgU_hhXN%zuha-~-igUy{*#&B)Yui_)3a8b$jK!jn(;R1fr%o4+ zopZCR%z50`;Us#=*-(DTu9{$LzaCq(u{ko)DO&O=*{&_rc`LF{i%hm0y&*$sys+bw z;#uGwws=~`x7N99CVx#OmKfaVxRUl;RqkA`;r1Z%XBKfr*~PaH4)+JJ_|o3)sEA*@%}Vt$ z$nnqjUG4bo?L6mgsXsc{XKTgxVHW)(#k#9y6F0(5@@C$qFM1m<3@rxwjxNYFDu2CU zsg$?1AfsKD;W{Zp*`AY`cf58wYDZ}>UBFxK#gDSm$}J|!YFj>s+vZh*aw$`wIfYPt zMo7Elz)`P&!J_H~>7;-Kb)F_JUD1#(MTHEb94m(W%IS2O4=SIOr(JcMg1T*2a@|&< z#;kAp{puTbYs~R?Kd*X<%tBeUaaJGXj&D{P>8Nia6xeC`F4mY&4y}i;R+$}))Gjf1 ziFV#>*sd)kJ1(14=+vHGyrSS}iavzLhKXg7uSi+$x!aBQ9v{-bkv#8g=}1bpd)B(% zC6W9mX2VI_#-l2Y@(C`E4Xf)q-@ab+a4qk=P~aN*cGF3uYv)Iea+RBxT*l?1AAGHr z#W)My^uk23D6?YA-OidGtfl2nRRR|lMu&q)-Znz!J|oMwCM!({Gx>VvW*x7$zEi6o z5^FknOr1n30<4@Yeh>D@NXfS`Fp{PuL|zid$P}%y*=9fcIlbx+6{Bb0=n|Ui=WessAdZ*Y5g+4f$EIAoa~C{kOv*zq#ypE2>v2y*By% zw37TSO0z(i$G7)QZJ?{rHlLx>$%#Hk$EP=jRZ4G&_Pe(H3BOEoO35UbRWA~|_i0al z^5v!dNsw{j=-X>OGCv$l$lK)95FowD;SwA&e4krVnXsAqt@SS@(jx(3a@&1sY0Xn*X$9@xuoAm~N4>kD@dRWFPGC5>BsHkS0JQTd48eTQqj2B#~O+pFHF z2dC`#5^M(!Gm>_%9Zz=uw9uPNwl3q}#8)gO=!BFKW{$W9m5-jR_4%D|tzh#=`(5A^ z*E*Tvy>uhtVVUvwnw`O?_$1TMskN``Wb{I2%QU>ZT*(2oBN{D3;Vw5@o^%6hpQ5&)qj!$v;#$rIyst$Nk=;uozkY#8ew_QHOwR$$g+T%-P*_T_9W_;o9MD(9HSJJLs7Ts9o%D~H^KaloU zVq(Wae`&8=hDymbx7NfD8nyZE(^cPA_VTUWz+DngQm9KxdgxLxpy%Y}H_sEi=2u4KPf>x)8@3zWu~%<9SimW`>OrQC78FI;VsEZaQI z^g?d@7{0A6KRx+$bBjOPb)vp{ z?fZ}A4}~A)^GWF2$ck^orz5H-MFaaXswKZR3U`WMD{DWZqJrBx!cik%n)Hq^cq(l{ z+Wgo%uJEDD`Q|F~1EZ5oAJQ`Fr(9>8cpt^**!eZAx?S6}ta6!7%baoM=T>if$mw#c zxAD76{>S@ke4RhzpVkDw+X!%}Z%GZJ&hb}JYBt9M3ExZeJIajy>?Gg5lXpcmcd420 zZ>Muqa`g4yqzsc{7CRNcA(}*D(a4>1wSIoC=20~S3&Ca2p|g3;%C+^G8CQ$H@1qPp zdu+UD)!=63`*Y3j-LBBBj#IND#u}=E?v~%l310fd<~LccpNEdL_iFZMv}!vfTlIWa z?mO(3Y~U>AooVH@{fq4M#J`@@Rp4yw+xljY7?D+C+veU(d#-A|(pFu!yW!5Sg$S4G zUhf9CZquok_K{kvH1cEKPHTrHA!SCB?VB=|syPofx{z#&Ay{&iYYYp7) z)eJ0FrylMvSp7XU8@iKje#Ls4#Z5C%YqgrZ(OAi2W?dPzGql8~@@M6+dO2UyXyI4I zO1H*;LPD40Ypu#Ggojy5{0sS7$ zLcVcz4CPNcHB<6Hz3Z^&_lvzbgrIQ#94?%FWWjxz)=#BRkH_MPJj;~SSz`VHkJMGB z0nV2~eSMZcN1ZLyM#~II5tbCoja2-~(^yfw?XA0%N@dsdS>gVYhYEm=|vvZSqWt&TWoi%ezHe%dM97 zre6u>s_XyR^~pGIbKA>jQ37I@qIU(1uMRYziLzZoDjgYhjCDF^|F( z)NZI)QEQpv*Z)Mh z7I@tAubr5u*1NXVx`xxE@1wr%sIKz*PGp9hZb}`ahNfm17xbwtb5S)2 zFg^0VgQW2*>qZ%?)1IYX{b8%1rf&6MyFJ|GtaB`cBIy+z&m-sjEsAB?Ij85@$_`GE z$$gtn>OH-k%d0V>c=FR==h}Wri_s6PZq%P*LAR>o_zpQs|4sQG9uPw~aJjqP?BHwX zpytt_k@@i%Jt@0^WLwGEdM>Nm%d?AB_sg`Om}}rYbGK%TTbnBNpN?*w6zV1n+-RlU ziJ(jv@a>)$^>b`j7gzPsWo={2j*^>5=f#%>dF#@O1XSjJd*ojh*^p3Ke_$+H$Zn-V zZGMkGUgNs*vHh;YpO_EY!^E}A+OB?deLC^Jih7;d$shA1AunR~k zZJa;Umu}~OgfjOtyhd4JQ7~;MVo&dCQ~jkm?vnMBzHJ<07BuT+uLaHi{jTSRIHLpd z90yI60tQ%FlN&nP)fXwa5%q|eVV4R!GI=gJS4HH-#k$&M-NW;q-+5flei<)mH=Ve$ z9C0+mpoDzPcV+C;V=gl@$HkD~6c+E7%KduyplLnj(=@#GO1dx2rEJ*Ob#b`e?K^k* z`-a_KJmo2AHNP#VMg!hOUwk*B8FJid4!4X6K7g>`3Tzpl6EHlLN!Ffm)Qqyy)< z*hr4owIQV|pRgG}mU50TeapA0ZElnJfG~1NA!LwR79C%y(H=v(seGmNr$R-6c4>Kk z8FkLf*DYar%Z#B~o2fhr$49bWJo-cgw?0O$3D~Ar{xD%LIkIEboo{O9RqZvo;=?cL zUF$?RS+K`RVKeb$k-w^U&y-lTL%MvQgI#Kw@;^WO=KJ5`wY?TLE(qh=+;7O6MTPsg z60JN46++qkuL<}9r$_2je2c6ZDtCk|Ge7IqWZ7I#GAD0aTX_<_68&F9+xJ!eshsXi zz}bEKS7mF_+`1(34&le+AH7*PUzhHXD6X=13kooFQNB&sq#?QrE`ZraPM~ zZ=BVQy5Osqkw0gFGg%hfSrFkcAjtkVD?^-lqUr8s$(rwCQz+6m=l1U<=SG#LFlKwQ z-(pRFZdKidESC><`gg0oQa?JA5@;PFe7>_6s&Q2N{_P(#h3vG;F4uz2>Ph4Vwv-M9 z;LS<2F~!O{@gsV_Z?K;sKPi33>%#qM2v0I!7VMP8#R%PA)B`6J4M@rXJ<1rr0}BN+|T0B z15u|ubwx?1YDZJnSC%ss2~`JuJ?#t_ITDNaX$d*|IExC5^_W?^)HEr2@|8{b?@zax z6X}f6x1b0KB@D%~y7Q>NuAECXrv*j%6+Km_R4di0)oYuRE7}8_-M)QEjUj5dzuY`i zo#5a3*lRJDa)rFrU6GnAH2Cjn(vXp{hy$ZFUx|&^aq&ZA%dJ0B-L`1$+Y`jCpS zYt@DWW47NiPRa$9T%3^n-A~fk?%^R(d36+vR}SjN5$=4&>2sNDnFh_wFwgz=?bgZP zY#B%Yf=e%PpVW^pleBF8KZ)z{=DE&aR-HFvgOwMmrlz)<+<18kUMTm|a$+1zGD}X5 zc?`smnvLK35mwgpACs#)ch-a`(=){(F75C6?EhK(CvBsb=9?td@{Kz3jk(GCCf}9f z_ldZUv(8d+gGVm$r4>#QX5RYYgp$YB)njt_#mN4}!<41TspRuR7ISVM3FF(!1>31& z4TJe&t2!KgIcGCEN!Ba*qfAyFH|M5YJ~k3{b5koWW$Du@Hl=j)y>#7)(*`N3g;(nq ztEPpzO!32y%g2XJvg$S79gN^#ZxF-XMCX(C;RnJljNnfT8*NGBcrs5 zn9AU(sKGf29;3Dub6lh5>EEkg+Ic$C$h6JQMipX7iGIq?4$m)=*vA^*63r&tX!|zW z&gL$^`J_yoG09HQe-fu1W$5j`cFHkySdI2rq*taSt1ezQN4GIZQ0f(zR^t5y^|72$ ziJGBi;y2R%2lkmMR^_Aq{Tp7!Y2(ziEe&&$SFwAuCbQ0+vgUn4Jh_yJkR_|A3EG^` zgt>%p@qp#l58ZPk+bUyy{+$mZeQJd?+V=Wq`#xDnoBLYdfFJR!RQKu1O_5Y+xtyO@ zviOe2oUKM(#!|k~vh0RKi0^V4fBo#Gi#d@v884q&i=7PT+x3<{eSK5X$^Cz> zWZ$xV`jl^}I@PW}n0MN(&3opmGslHb>ln6L6J*HS)R-6G;?Hs53^og8jE zUq$Zr)=yo_KP|4f@pk-*+E!Jc+b8rpsT<)t8QKL&T9j{J3Y&(%KQTYO@T$74^!oAN z0iz*-{l93#{bCzssti)a1^&CD{TCm0xeeDm$cy)KjV0yzOzHKP*OpbT?&mM{+mTHf zC^l3zkT7wK(Eeq{l&F2xG>0e!_U-{k>;;W)xxpiyiX<(^W#~lXuAh-VF_&=`k0^KGRV$`(_vGRbl z+4b_~Hp#1PZ-oLhUfXPJX=fPt2LEb(PJVGT;&OJ{wN5c_$~kg(heJu(5A z$3)!%*Ecu#v+3qj+=c76``7N}iOiDz4(y+yIz~jFlw=xej2OvRR#}`*6DabD z9IkxDbDeh1Jf~f0huVD4x-wM)@0a<2+@mf@USChCHQSwS=ThFQnOciap{_3Gdna5L z_KL8(TFDq*y;ywShOpQvwB{M5Ur`*@E=`PbKUN~wRNE{}iWXB$BiD%jnGJGliK^WQ zpZPCatmk+wvG#dn0IB?n*czc`b2fYPn^n7gQAJ9j zUSFc+rfJyXgW5;tX=Q&3#1cyegGQ&9wA@B& zYD4P2O_EkG-i>t7TwnlhkY9xe4-VBOp2<6hR#FONIUP?J%cF|g>$6H}o zM&MTD7jjjp&{M)oyIAI-w#<`0HDpJM-|Gn-G9fo_r%n8r9^R37iQ~g?ajj$f)8>4$ zm#sUi_oXG3OYc|DE;{!7`lUJWK1$tgGX8U5!fLX8WmQp8xL*Q^UtcQ4usSUFCuQbm znyc*eSbXP%=;k&lp&}qyfhN&g+egi4)Km;FeDJLDf-_G=e{NpYiVh_v0Po}?piK_r zpak`b3{5Uoknv5y{@JD{=1Px7y9dtU+vI1IEj{l}=k09yIg|(b+gbN}Z$()raege3 za@#n{izmDk6FQMk`D0D%!cUqVLw}5xXz#^ea!0d#E83{nZhM)adAdW~z0LdPEo0x| zX#PAO!jurTwUoWapD<**u@s||=9reX6L>siMpggiLNK-3_@Rj-VKBY@%cOMRS~|ZZ zZ<{?AiMQLmyh+%|ZQf^YtioW0R*|vAN`%_(}Y2iA~ zy{|uip<;*NG4sAtGOSzV?mWkK&SGx{-a>6;Bq&4QD}6*G$1){TL$#^Oj@(k?TUzR9 zz`LtFSbwK8-R;x;Nr_6+h2$$E%O~I6opZW#>incG5EqTsOSc*ZTX_}Ch?>;NZH3Dh9gmkxW)#18HtV_c-9caQ`vq#C zxsZ@w*^MK`O-ViLDU=IoHfGF;aTP|4{v9s`L{&X-(0mDbJ1dCVrTekqcY@+_{{3!<5m@EWB%2&&&SD3Sa27 zM!;&lN;d17h`YPMW?lAIqG8cJh`t~xQ5!y=p85yAEs%k6FDeAd#uOb?smdz%|i zw2L@j;Gv`w^48y}UMKzR%Za&73a*(Dz#9xGwoq{-(vuo3Yi}ewH;ULi3vpJxNF2 z0P<{r%&KIgB2!9&r>*k39J~^SnTqEO z%75S}ZK@F@_cUgqI&X<5YCn$ZwtMi4mF~=tlaJ%cMeFB^tLFy`91@ath}Hd4ZhaXk z5vCWOReI4hmPx82zQHq%t1|(*t4FD7#1($=H}Y}MwB-%Au6KsGTg*$Vmi7nd%{W&S zx-A`w7D~F)Qi^;1tF}F5F-(j+)+n-VSVVmMGF&#Z&@QoZW4WpNd&Wz@@zi)R%LSh* zyN)12rJi;EdfMFY9@n6@h+A3fy|0O?dc+Q|(azx2tgZe4%|*u+QOf+*mZXO>PgUOY ztsnobTCIMou4|8)D*xUWJaw(I)Zf)_Rh$sm_^oopIo16`xk0V3wosHlEyFiB)$TyA z`U!jV2bqHUw>~N)8{+kQaQ^N8k!FKDgab$%w_Ayv)g9F#zM1YL?h|uD<+}M>s}sH2 zeXKIHpY4nHH{)|ePc`GbKlR$_=9bAd`%jp+ON0#M4_WxMx`t>HN57_sbhF}D5?0A# z>K)ZRFX}Ti2^6s>ER(IOKYvVRSIpaeR^|9|{t=6+LROMS=XiP7Zaq)O`x&Z=7m9_G zU-@;sXy#M0>w0pEF4xmBG~CNEvQKq6-}G-~vvf=Dh&MqUe^p2gcAQu3J$9+JarTk4 zs`uG`VYP{JVd5)uZcC4w9g=FwFI;aKdN&8gh;!1N8vtV_XN`%IA zLcQrUzWBx8EA`B(D@`9?KDal*z}aG&cv8C2PL=c1vzLS79s&=4UA}Su^?~R0vQOHQ z$c^{2GJG3)-wVanE4{DOFfOfm?Y3|B!c{*9ZTbZh=ul6+5Yo1Ne6d-G6YZ-vWeL7$v|2;vFv>TR=P z-kXOLeJU>7g%eBP4?jCcQtNSTvwME%{qQ}aUWMd|6U)+X+<0d-+Fz3o_qaCOr8g!W z)aei5xv-9hHM9by(Xm$CJV3?ZuqYJM(4!n_jU;NOKVYcm1a+q4cBl}k!2j8f^ zFP=7c(vcF(h)*>x9?0gLZQH!|^qu^B1s7lYV?&{8-8Jo#Mgk?jjlJo7`WDtY?aq!Q zKQXoY$KeN|=(X9kr!B8KzPQJ-8?yM=2j?v1RqR8n_`R8-Y6i9Yi74&~DfQ32;xnlg z=dJ&7SR^#6$7!WDj7{eB{rAB+l1&MJv%T0Ps@+JSN8k!4y-z&vIjz*0u@kl^rIGjW znbg%W7OVcxe@X>{PU3$ip5n3r|tuee;r zLJos-kq+W@WZh}4S0GfoZnE;8-;xB!ga21@1KU0;r939N-y%DP%51)`kE#~hYa>#jIfGY5XQr@d~R-TcKO^#>hH}N)ugKgzFEue z{XK1NO8n7S;9oy)9d9`ij=Bgrj8gxk!#HA$kZFQ4ZHWq46+B!D^$1jv09cb5t|kj= z=rf|K!D`TwWUg7_D=fAXu82=N7kdBWg)$9v7u!2ofux6{m{1f_yBk6z;A)bLZe4wi zwZRdco7L_-9MyZ9+i`1^#4_wG2Bm*B{4qdJr;j&J=x`)UBN-xz7?B|g5D#C;_AYZl3T88 zQID^~<32n-U#~05%UO#vyJpIp><-x_ zRd;rdR54U~UjT)z8=TrF`AG9Y83dIua){7)ni}_9f8D)vT2Q4OPODp z5ox-&1S{gr$T6<}<2;B&Ok_l2lA|`(>=h7S^Fy_2s4>lml0KQD8d;|0yS6{V5f2>E zfnS;q(KikP%@0wsY|v31bY#H-)b0Y=K!V44y8>BHLX0j};X)V!Z!3YP{~j~?oR()d z^W5_P>2J2&w%bVISvE}Eb~O*1m=}6TWP&JLD3}3`?zdUMeDa19dvua1%L{d(%j0b< z&=NPq^Pox+#m#*#W+zZ{63e@K{xzL5C#ONv_NB%Ga3^2v^dgL6-Fp5k7=cmT@){gJ z_ytiPu<4FMR1OSo!iB0zZs%r%7-CQ<2Em2hzyTzLpx7h{=e*YBYJF^0Ww?~z$3Zj) z(aG}`w+Hs_2I%?P*oeH6iMy*$+{&F|%RT;WL5F&LvhK~gfn->NtZ`?n$qH;Z;qe3> zm(e5kR~Pz3G~jO@E;vTpCls!|lsUj8`B<|dZO`7ygS>wI|1uZI2cVHA)QALcGy3eHlk@1r zw&Iymrggh@Iv5^z!%#1w?{Q8Lt!%M?mso%>1c)&~acO{;5gc2Qg|%aEeHCu3K>`8- z_rm2RR$`Z@!3qs=zk&Q)TC+(S=#8VC1oTQIx^e$+QK z0UkJzgg_c$gAQ3ia)b9}?}WXtB*pyrxDQZB{>H6px=-WWkn0O0M=hYwYow-j*wMnw zw_>RBuPn}Z$txwC@ogM);YXJ;_l3a&Rxe=HLx<>h53gUO9#>+}9oNs2Guaz*N$=x- zXyW(IG^x0{|JDuEFo+MJQYHlP8CxpMWOK%U#(#T_9WtXs`{@svmyV0jqf^NO6T5;V zv=?fjPyzaRCk>nra5JYsB7)@o7Zf8wiCOUdtB)kxLtjg8uP5Wpu!zljN0%g8ee$%Q zEUVRkMkh$vb1^k{r_XU&>fB)9Dji$jUMho6_$KpRw%_TRPs}~ zJpp@#rq|v>SI2YVwKyq3pEuAiQ`F~t{t0lzioC}fV1W!}^fD!)c=9XE4Y3&%5P$1X z{5vTy+zkBxVCp7ArRqG!*!Xus?Upisek@uqHdo3O+rpnV;R|WeE)X^okc`r-jJvBu z(Bq76z(-cvPLX$!VD);bw%q2 zZp_I1rw(8ONE`nYjMShXyOHtoIDr-RY>=yd&ko()IrM%}B?maEL?@Xe`=QbVN;V*~ zAgS#P`lXAKao7nvBp*k~&23^Q!cjm}Tb}6`L#QHd{N6MKYsHTa8RRFzTA%}K$p3Q? z@>#*)rHZZ(o%{nQ5pWx)g;B+%hN_Iz{08(LAc&>ljA4LY{eg35LQ6kI6kAl_83!|p zvI55hq6A#fX?AG48GQP1L4GHohvOMo51n*yA`W8|!x(8?$b7jy8yn24?oSx$lMj}v zaQ}rEBd~`|tPHI=O%+bZ@k5JLPVjby+AYr;PNX^4)xhkTRLtKBiB{au&Ms)j7}}}Y z1v6dE$uRy0UhFRw)N^d}_5WWF4#I?xEjFthyE;E!I1z(NRe&dnXNW7Jzqg@pfX)

    + zP-u<|WL6|ENf}LqLsQxd*c%{l!5h0}j=ix|P%F<^QcL^#WhPZ zqm=d7LDV(C%$w*na2W`!pkHspdGcUT2g4dN*v7NPp~6e*knPKW@I_Qge{wW`2K{Ch zwk)Vr$X{Uc1(PUL!GT4e9?6=25=D9qqyg+|2?6GDfWr1WxoiEGeE zTe>wb_j{O1lL^#gNTtc51bNTRB&K0;};Cx}UMJCkN7&FUw%;Lv0)FpP7 zas5~5TOWnhY)+Qfadl^??18eoL&A1*qOSwQeE9>9G_@Gv;NkUyG^S`|VHXS!UFTJ# zXIzqS+=bj_vY=Xz>6yLSz|`{rVBa)%8Lz{RrT- z-~eS8=Y_>Pv@A|o3j)OnlnFHC0Vpp7iie@VnN}WY4X6O13UJZ(p$PTM zo!tn%wfW+@G!UnO*Z_h9!bD+26X^Um6UebhvF&rkUQ-&TK{EwRs6yL5^Wq?^w*p}( z$PD}?h%!wb%h>*FLxnu z(mgLL#}Irk;d;a$8Vi7v8=T=_@`78Rf^j-n5oJM#sPzLNpM_uv$i%}vSTKRY35pjO z(a*-GvM0dpOT!HZVz~k!dxCL4FzCl_Q`wDCxIt{i!+hv04>}9vg&w33tE6L74sYc) zBN#7$kq0WOKtsV$u^e)L8z4pgT`YVo?hgY%fG{$=_7Rw?oZ|`lrf5A^{y?PW^We|; zV>}ne^$nTQWsP~yXK|u6`tKI5^EO;RLhHF)rQ~GNvm5d6_aOSJjqKi#H{wV+uUGds zH8MH$Ir_fswh_zWC5c<~$l0?O(ESMQs=IDGl+g!h1jLmx1H+1#Nex2*pco-gM|Sq# zvE5=mkTA;t3Hy;YeG2Nhfms5~KCB>P1$aT;2XgmE)PX10gwg2%K(}J zSPbAK`Y8x|#F!3uH??Cl+){?H5!}+W9H0*X)rUA7JJL$nKoJ0C>Z7Am5Wh`EwgE=l z!I>Suz@Mn@^=MGY*gU_n2Rj2fv7_Z>tBRoJ1*VUXKF z#)IMt?x9^Eb3ueUL~sL>3QXlk6KbUJfhf&Abtcq3);H1b5dX{OY(#$t9H51WJ!!z7 zVzOD1c#6bG16jV1ahB_nw9j0y6$&j}=T%`rsvAS&xchd zzVm7hk6e%4S9hdDKJk%eBo8{>8-`9kU;m4C+(^C?6(HS=9fE|L=5=O*2K@m?TU|EqYUtiq< ziK##l?;*;Y84|f6Q3z4Zksy;m_6K=|3CXd~b2w4VHx8gwAWIBN6{FEEZbGRQC^ZA6 zK7pc1bWn5;pnD<27DA2#XaGqWP}$VX2x=$6TabqtY?T2O z29y=3Nr-F1;$Vg%Yz8@yA3%~mT*PT`>q(}bW<-DeBMbpIfjA0;E~HpN5mOcj5QZY~ zf|@S@V}zKVBiqe$z&gVKqj0UJ9KgcW+v0#_0X7;KF{tRmfS3(7kQG4A1jQWOWzeq+ z^gGB65sbh{03$8CKV&a!#<>1jw74H!Dp|L!w}K)_QEl z^%JzWTptao=nsCOtug~5VjtyjB7JDzEO<1h1@-0R0QP!7z;t309gX&V_!6adrgHqQ)3471fYt5&H$<8u+F#W*vLqrWb$neIzMH1acTt|%I&3GRMlohI_Ks%by zEeXcjJPryUgye!e3^D<_B}2C)ERN&DBh4(vljHiA3jU%Qt{kQr{<*GA&DvLY$*8c4$Q5WotO8YZ!W)B%z=Jp`VFz@|`eae>4O*)!bW z;sKXGxIA|Om5C?`gD*LpAc}$5!2l65AZ~vI9}o0I2a+_jYj+SN{%{l_&j#sOyB081 z%Wj|^Lb^OBNI#&bCz$I9xQxN&$py=PjM@d%9JE)l2bNu!0fw-X1Zo00NTP%6^N=kL zL&*yOdK}Qp%zz34+6^g5a5XKL0ky|aAwX3DJS_Jg`?8`#38&mtmV6F$CbWMDs#(Tme^g0oN;Or; zsg6&ue3#!b=ztFj4p>~?Ji|VzKaTq+Y$Nv6u1m5Zdz-L>Anbq}4kWOHBgg8f7X#_w zAQL-KgoBs#M-w$8i(fGA3rJ(n* zK!`eoh`^x_W)cJF0k%i2#`JLk!~>8nRGwpoBp!h50LrC9d)K3&cZMUrY!^tqAXU>r zKT#M(50n&8GLX6^56f+kxUi~BfXc$*9L&>t4?*!z%5@*)od!i4CQ}Bbj2#Y*fgEN4 zr!yqEV!RNb!ypBS0ELaV0Q0-@gX9hYYvQo1#3&A+2v`;)WT`{e0+uBP)D}qU*eqJm z0XH^)G)NJcgmn-AE)j4o8~|w%F5`|cWb=ZQ3LPvkLckRWxP!fK1x&(R!r&5v%eW&7 zE)I|^G1p#9!ph3Q5|52=9F_^tK_%9K5oG6K+1TKn@VYALVWcU@B4Jq^K-FN&ldyEd zVwE6k3}WvHL2-92wPl^7a+v<9vrfRDG(t3FxWE#qyi8QAQ`A!%@0W$03|?D4Fl}sK-c5} z{|S(cp(u?3tTGT)!va((P_;}zwZf8uwZ;bf-GJ7^VLmIWf@6fzd|i!~KoX z0WJ=Q*357d9N^5yTGQWvh4#DHd+lfza>buaOImRuv~@qwcTOR_0_kzR&FGmyZb<+u? zPE5k)^a581b{T#k$zakRaOq+aR<;RSGC)8kEUmE)c0mWm&|EDGNZ8<-@VVJ$0SONm z9gN+940N!AHIom^TCACVEEX<((-n%>G7F$j8Y6n7o?%&zICN7s-GgRLM^n;e-0{$%pP>MJ~C;ZXpO+we$alTXyV!29Jo?Le#++B>qK)WhuPuyG$m zWQ1`}#>jC&)af!8LIJq(!(44`ImW z1?eSpFu@1`S0KOxd*2F}gt>&lB?gz_Aqp-IkSs9QUQEKu%E1zkjc^>63D7|~)`1aZ z=V95{;GOWg%IRUGDaabZvN(XM!Imds>4wEBLDm?=dI&*rcP;Vdrr&4K>0l0h`}3jAs==5BD?su9UAu>pOZS341%5MTkZyrwMTe|&+OX`y zC_bPPAk|+0mcbygfJ=}8q0R^3a22Z90n`Gely;%KYoeh11(OMZGQhr>qgi%<-!?0YyvP2+j4KwfnwGEO8Hp@ZiAOZ{E z2dN&DFc%J7Pr>EG2@(ayQo}l62k8n(tMm|%2?6N8!iJg?0I36Waf6Eob78N586;^; z!rpTXxHhpX(#Mwjz|aaESYjQVfb4#dh*((%kVshHUXbO6b-)aiHMZOjO9GaKefZol zFV+{~V9JJ#A3XMav8N4CWtN-r=my)wDtFnce96~RMq5VM(%UM8%wKrcPA!zU=Q0_S z8;TyTuyTdf0<3U*fDy--aI`0J7A0xk*$aoqp@2R!3JMT}GTjiI#tu3M?2FH!y&LG2 zT)^Lf5Op{tGl5A1APz8*82}OjNEDz1s7&I3Br$+m!DK~`_G_0w$FqWeKS(*?55@-J zhou!GP-Z|`L+X|tEVn=s#j3JFl^bx_5A&qbBS;-etsR3rKTx7zGIzMK0A4uE4?|=% zGdTO9mC;?mD+5Y^kZm=fu+c_g{xxZk#vouz50<_d#Rrr-EUQ@|%NNVSYHI*R0V#+M zq;Tj!3>&}_Bpys+0*L}H4Xgt>tPEVn)+xxAz$B~#RtUgj0g50wU=mhK6dQ4f;NcNtnw9Tf*&Z8NgBo>j1lh9LTnYPtX=NI37MNL5$E`CtP$5)&W0IA=uIw zmSb3~Gi23bvC2?N`npc>=+@PH@I+&>y%;kx;BCzE`>VzV| z3iLl^Lxj10e)+`tte)y22@hd-h=bbAge)Dluusjqxb5Mgw z1<;-7Vb2HqT+mCPm2PgR%L<37aM;EQ&>`4c!rnU^AV#RP7b^Kf(hQa?0MN-ZXm836 z`ZDkz7{IRtlFU71UH%J}&KSi3lrB(JoKRI0q;S}CLMsYz*x?UV<+wnRDI*}uup)wxWDfiw3xwPO$qE|<8%-UPm_ZW4>SGPN0X2$cxx#V{%i@Nt z0L;J&hG3AmLGs!S9h}1guvu<^^b~W6f=dTns#phGKnX(!Q%oR9fV4pe0R|wwWC4j2 zBnQmJ>M8wb=TGNOEzPuQeJO2cb}3F*6pzdCx#nT8lzCOh8U0Tuppxp2{yG>kJlVD| z%~rEqi^m0Yj3_^M*>++xpmmzOPHc(l+iCTQQz7ph-r@X7GL);X)8qbm;ezMjS$QML z$?k2N25;$uydCWYTW7n9REwDMVp7J$kp4s_KGYcse_j%a8an=d!Z2 z0t?@tDx?c{|9B+zimX1lF_q7!vc z=;nNS-EdL1DVI;?io~l`qh}s z=|Wh&U+ZwXKdq{DH@W$Nbb+a$za@k@^c7m)go#k%|=|NZ7e{P6>kyk>b zvI9~Xg|UHQRVRxzkjoCHrKKik`<<#jrIG`=OvW9MT_&41;-=*H@`Egfm6#lL^Xc4R zdLWfarVE8J9uCFfTqd_}Gxv>~9oRgO$&I>$=@g5&aX8KOhkcusR_11l>AYLq%%8`y zBe}u!P}&`2+Z;)zGP&%!PyIGIXj$42_yJh#K|OV&X!TBSw`#dL8@nsC7#P38UJ zIhagm2i;LOJIG^`E9OzhQj8Sb%m%lROy%8Vs!(u8`ZF#+UCfm!U^tg?gSHokQ^lw* z_g=c%n9gJ*BKqC1S9lO6CA?D6gLe%j9*?Y^(Xsq!uHfEn8U1&aQPMH}sRA4HXf7MG z`+V6Ka^Oe>j^^E@yD43eD)TiHb!{=9$`*W(B}c{zvX3Qn*103B$-#VzG4rbb#87&G z|C`DLUyS5@1f>UxV|h2Z!Oa)=b3s~v-W|yK$V%mzm!tXI;Fv5Rnd;A{2iM6O*gV~A zK^b&jjyzv*X&ZMN)JSSG>pbrcqzXm90sN#ZL6I6vjq+%AH_2fs`|+pHr)6DuR}T(E zBirvfAKHWI4e7zLRK_1RMTWDKDmOIb<_l31zYDOTdrIc=>2>KWca}ulKUB-c;hb`1 zgHbIdw#DCu*p-O&?P^HXAl;ml^CvifKt743z-Y7g7AXc>MZ0?-n&lo&1>W4+F?yMto`EXzo` z$Sugb9Ks9~<9;fY_q8b{59=99=L^MTA(e3>KPrpygO{XYhP!DXGge^xR;4MY8hPT} z#;luHEsnlrKl~m3xsaR_Hb=U*WMQm-eXKj~>7vT}F;qW$!2Zo~M=8g3W^f~ic=_Ds zREA?6cEQK{xOMl-3+DtOCmW(7(VhhMNXcYWEvSq*qbMshH{E?ty(8cgSWNCzV; z*6e}InC~C%Zp2*;kR8>Zi!4<6fuM8buTq&zazq9qETbPI7m^!?U1^`$+{WZ9+1y6y zXV{)nLq#{A9CSCx-``b(FJP?OS%(~)`wr9(Vg}SeO8Fs1(H$7hvK!58PG;Q!w@^ss zH~$19@<-0yGIL8JMg)Y)r~%-|tKrz-iq-hYyPW5^gUR*~5>mVfWo}qRI-4xGzC)g) zTAnV%sv1cRy758F@=12MVQ4#)%VcsJCB);krF;jF+8j5z&{V$D!q^(4hol9?{u&`X zo7wyi`XClo3NcnFhCP{*16&4|{%^;!X<2sOE#$`X18y?S0VKz!d4-E|pW<$C^P5#M zC4;9V58;~sZ)|#?O*RGTg!%dOh>Shh1t=B72A3cCWQzWyp}+WUeJO?H-GTHdC%A>k zVpYSGIR@6l2IqG|T)Kj6L}_{#cEc;KJIb_6vFGwbEt!!^Ve>AxOZtYgZdCpv$Zh@X zAeI})>;Gxko1d9TH4hADIq{Kk@`#%%jO7FIWuB7D$Xu)-!CIc1GM|zosVw_Z#$C3M zE)-?D!sY)2OXj>%#>x?)q|0(klhb=p^zIP1f2^n`Jc22Xy!%hb(s}n@ZX=l*<(09) zteH^QyV}B`65E)|d2%u5Lp-cO*?)OpEyKirI_4H6^nzCWRH)|XK6g33?;Jf*tP+f) zAixPTz;#^Ay_z+UcL&ql_o+fIE7hkI%bsXV%Ite{8EEC*^MuphYt_xs8l&f3OBoQpz8qcUz-O8%NUF^vKvqN!>CP ze%ux7taO$cv^klM#rv!RcVJ8=;88FXkGo6aD?i1EhdJsOk)Kk&L}q#F(bUGFvCKc- z(=n-YV?{YChEp4YnZjIuCcVy2DY*R9X5ScPPQtyw{Wq2=V`W-gD4qBi zO>G{L+D)gU#)iC3XNS1=+_L8RnsQEOH>3(Gff~)HH>3tOM_FJomoLh^H=SKS=J!o`!TrJf zOe!93svN336%GpCm=1iQd}d=lEoZc(JxUKIgVM4&;`kJ^Dsb=~Q}98*#zv0P$1CaV zVA4&?mIc!Ue(WW&6y-2M5m9n2fB&6R_#7|DPR_~s+p)YU0Ip$ za(EyqP5G-d#bK9+$&j+p!l*ltPGwlnGBQtPq)Dt>UwK|fDaPmksbg|yOHFzX)F`=ktl(phB(_Xuf!GG#KE zumFkt91lD{sg!XNt48%h|7Kr_Lq93@3Mb$S;S3!2m_HG=!Hs6Z?utHSmvaU8a0r1* z4W~_IS;b*D9j@^dqIL{MzF6w&yM)TSBdIhqCOI@FqZ}S;a&1G!$+>93st{OQAvrtQ zmYkdHNIog`+vy8c7|U- zVk#T5!j~rIe`SteD4m!HJa^eF@?cs{+Gb-jTE30QT&qybjm8#4<3T~npIt1bN8Im- zqT~SOu5>eM+=rGR1>_32ErWSU;?l7f`JX6XOckTi#*i&URP)n~K;QqE#xFjBp<_V*Md<98+ zi|J>8Jta3nTFiHvxq}HtyoM8+C;9JhMaYKOFUA&V@2V`mtXdZO`F=p98$Tfq3<9eX5&nVD zd!3tg^Qp|eV#vT?!!WWuU@UE4xq9y9qla>NcU?|?+L%kWCFL;84scvNbhk=|OERBu z(s?kLT`OU>Td~YouK0(EXrUzejz#}|vHm1}Z26j_F}6vTKd08V<%EEr z)ZZ(Oikz_A*ppRRlHZxK&&O0a66L0MDMIzH+)c&Q3ZKm7lbPJQ9LshOgNnw&Xg;0G zheIL2(7k>oEluYBGK2`Cf_}BJrZmjF>(|9$E`BU8L#as2u|jH{n_M@R9&|J5tSh~% znxB#iEDZYzXS!HOZgl$#>0((%D`!ws8&c_v?<_gIS2K=kE|WtG?i2p+J@$%iR=X97 zDY<=$W2j;=+L9J2dforqyZ)koO7?#arwYSAg*txX{?E47wmEa2EZ_gxK6mzi+5h?f z#6PR<{9+|g|8s6CKQR1rZV`932~?qGb?y~6Tfm*y1#0?Ig<-dVJG%tx7Z1AwuN1~c z3b^xzz=YuAs#JCzKeAH@Vc+@Z6(}j;&UPV^cYdi76?e7^RG@0LyQzpqftsHg8Y;L& zRLP(HpbDts%4AhFvb6Dcex(8xcXkTg*&#&ponPicc5orpKUYlU`KPFSCOasLaPItS z1yJ|%|MgfpTb$D-MXV9x?@{@8cea6Qeie1WU=*lT;}M3yxY({&OcAIHcD`c10A^!_ z09x_9h<=aNu@Z&^#>IBSVuL_M+SgEJHdn-*w*-*FoxK8;nbbzy*)LEvl+KUf&btDY zsq7%`922M-%&`tn2ERHhP^yk?2l=Ta7>^s*qaN&nMl-+1;)j;)q-%98^r(fO(2QK|5E@}RQRf< ziaLR6IfV$Sm{3x2MN_=uiWY%t8Bu{M77A2kbD)Yofre-T4OFpGpgx=?1XTwT4hz(W`+z|e#{}xb={-=zIf2TdTpm<$S)f)$KA?*0vT`-o z2dcQm=915ifGTSQD$~URs8SQC9OE9WoEY!V%48sECHE}H3ZTkPrLW2!uE^g6399U4 zrMa2hMo{I8N|Tj?CC#Z^FHkK53{d5ku`AO zPJ#Mxo(ELb74%ZoDpgO_8i8sV4uGm&7HEhPWl&W{ja{nRCV$Cg?hLx1YPV8B)jOed z`y=VzjTL-M>WGcF>Xhn~st>ucaECOg>SCnxs?S2DR(%y$MAf&d&D9M84blE-P_?ES zT|G^pA==&ts%{BnukI13@#9rc^)i8qVh&XOVjzC?OQHDHLnQ`LJt|PeQ81{QRk1lY z2C8OY` zp*pI+jH{#icJRBJI)S<%;0D!b0yX|-HUIVfzQi@H;q#p_Rn&A#J5js6{Ws;S*4Fg1 z!n6Ks>&KVu|MuTkF9oc5L!fGNZY=M=D!w7w5$?YZ-hb$Qfhx}1{g<={t*kj4D6Zy` zKtnWBZj;hG_Q6e+jwj!rG|}JvYVA*UnPU&yW7x6^c{a zr;M_8m3r(&wytpBx&QL*YPtK|e@k|KxCh;T|Fs-TwYvox`%io$&=Bon_g|%4#c=jtd0Z`bwEJ%~uHtmU ze@!trtgcO<#^2-azkFEsOx==btkx~_`#IY6?!OhdF~0ZRe^qZ?umj$IiLT#?b^D~N z2zSH#@4Bs))A|0pXk{Df&I>d|`{VtW%2uY>Kn_t zyTe9Lgm(X_y8*UFEJe#n-}umR4SkEFE4Z`DqxFLWhUdl%F_=R>fcmj zx_*yuxN66?{|Zi7kHJL$?7nRY~e;RjB^;aYJRR3k*E9<`vOs1iN!KC)? z`>%nNeb_KLWJJT1I3pTb1kmrlPq9RnhVD?7h8Lot((tk`dqXDr^Qa$ZuzU@#3RH2L z=)Y7@3H4_5>>f258V*Dcz8gMxEPC){U@{G7BlByxAW%8VX3+2{TfaIv;J+*{u%d=r z;m~a$rNwb|zS!d?2#kxJEAZb!SF27J`0t4u7dvC%zwvE+$w>qM&1_ovc?18IX*K?- z1OGK;wd(AF|JJcubppYEcUX17r~J56GO|&p5&SoMRdN(B-^A4zKbhdac&i~gpWwd~ zt1*5`!GDRBRB>Z#;Mp2GA{91vg*0efriNSN>IgrLYl7Zrj10UnD^NL-+U&n+Nm_N| zR(28coP+-YrE#&-4*olld?`52Q70e#cNEq56F&ZnhSa!gyyW*!#rK<^+K#jjBT4 zf4f9ouzAXVwM2~{w)wAx2wF7ZKv>UvWi6U;JZRB{c#A#~sN6W5^WTF|tImh`Z#+;1 z-40un5CU-hw+;kfO^7N_ObmXLm@3^@>?Dc*Mu6Z)eX8AwRblw)CvJ%s1!~m^6aQ}f zK!b_b!oqh(8cYPyZ(?7l!NkG11{3cIV93A!-4DMKXJR6sS7}}1lQMNBt|)aSZpGA< zxLp?3YBjM4PMaLbtIZ4r(mLV-Y2AUwwdVyYhZ&pNi@~_jUX~GcaLUEMy1Ono>*8Ot z?W;k1Gg5=LN2x(O7}a{Xq}KPtC`9ArUM+n1_esmxSi2gQ{8`vjT9658--J?ofz*1H zKwYqFz`ugIR?TPo_a`?*XKDP4kX0(FFA1vBSC$M;{RJhAJ}ljizaz`PBskQvzBSUa z{zlkd{hfGw^@Gx#%X2vXO}We*{jAIjuVd_v3&vVd3mE>ro=WG&iLgz^nV?O^1%YaLI>^60v#OBd zp=w+U>bw~_G~+9Qaj{cF{@s;wa>TTP{xq8e>ce9<{+*GbY-SMYnqmK&%Sv-&a}`&( zG0PR2Ls37Q`LgF;3vyy}x02O-Cz93NUzS6gM+B-esf`8y+CR+kZ}f{g$h;&_EmJlB zM!s6L7sS75Pj-7Ab_%*vc$=1R_(RgL$k2v$7{8gl$gJ@TA;>1!{J}v z7AB8YF`$>V%{OIhr$Bvp_Q}6DtyZ0Y^6xsU8j{Jcbxg7)>)%Bd?;Pu_Kz+Dd#lJ1g z_qx^>VN<^rs0;S5_}6vGCfO5$Cv2-s*Y@PnzOvimO}2ZLI_(!qhPb^pEHFOU?G0*Z z+1tav?+o!|?^bcAy;q>d-=pDQv=jvI_EBG5_6I?=r)3~!JpF~t zqrwgK{_R5k^G@Ui9V1MRoXG-}xvcBo&Lb_;Ve*aSI6igS%i<`fD+;Kc)qyEFFRS{U z;ZkXxY*_oYsP>&o3(i{tjq&aO{>3m-f6n{iFHe@sa3&_hr+%K|d=^OXIfrTCh8B?X zr9h2;atw4|m1^UChCpNd6dCBgiON>)V*^k-<3aaz%GAl<69c-hkHb2DKMd%;HKl)C zA86#hEhUd_FMaGSHG^~Cp~$u0cOcb^x(8LC%_=(MTDSqNqrTB?mW$K=|BvSXpFbkN-w3FM5KojF5VB)Jm zPB3wYim4~QDF?DT^9Gvuc2L)0))zT7hbXiu(tZ)!jeJLnieK|9wN4X5PP5V0_7mJJ9`s@!x+an7X+C2xm#r89dPa z?+et2J99wyUkK{CRsx~>Z-i3aj)~qRlzy8gL_Vg;iZtAm3^d%-64!83d(aV0P4Y_qIJq$npvlc@SWKQ(`c%7IcTxNNK$Ck*eie@P$?HR? zOwJ2b<+AeWEm6cXIh@v+ygQ0ECcmSgHu+E(QcOM)@50H)19eV57pP$JMec+#j`Ajd z5+8+=uld@V{FOizyKKEhSC!)Y7MBmUesd!jfM-ABfWYa$t+i z!vO-CN0}*{@`9Q-1?6uSsPWHsf|_>*p?>q8$P}9QtLVG=NYMP|XkM@RWH?jNd{$0I zOKLY?3@W%>l1(&U3(L3_t0Gv=eXu6%zy}-S9r$1{3HIQ$pnwNk1RA0fq@V}eOBnm$ zvY^l~;(M?^2FC~2#~JkCCgpb2uy}AspvwpM2D*IkV4%wfj|enGr%yo-hEdOh=R@5* z_)%EKm2hA@$Vu-jt~&~PkRu%bj2-B~+X2FpHGa+~*S;w^Ayja(nLU^*doVd&VJ#WO z{K=A-Ke;3rpvl$2v!N!E{l2ZLvs$3!dZn%8*0Oct2< z&&Q2Ed8PD^U&PHf8GzuS2_;WGWR)8IL(}5FdT4&oriYe=nty0jINl#x8yNLNLjvPV zPSt`Q$_ms4XKO(Z?F^Ou&|bNoE6>@29y+8J{|fw6FvIoG*&xAv=)6+jLm!3Wd>V;! zO*y!SZu%U2=qrI5|4bg}p|4d?7o5=rO{oawpE5ZpaLTl3#7vo`>YFk@?EWd;@$R3( z8E?2*6Er3C2~)C>Kb^9H(|G@UFKEg(Whhg2hY#-wTQOz7ih!mZzAFlya#Xc&%6Y$q zQ!bT7qf~bu?;B?f9|D(ClotDL%xU1M|#8Z`Ay zIh!;hZE0#W2{iS1SitG>27DMb;A$w{=Q90N=cPeYzfzJsQWXZ}k2I9D;E}*ZJ`#F_ zM_NiIuODfTOdtQ?UAh|V0z^3INKh1 zxAc!E;*@&id?4-R(xn zro9+;^|Y5mFEnj^%nMB`23}~|w$j;^X*=UyY1+OZ=9>0iDC6-+##4dsna16{&KK%Z z_~hl1j+%Bo)=|^G_J_e>x(Ir-Li+gN=q~6{O&P|cVXr^h!Yq|1$w7~{E9E@e6J_X+ z_5~Vvbajx>Ji1n(K0Id*dUQ}_0grC-cSrbV&Owic6XB2U7O2XmM)-f<3930LP#>N} z2R(XNS<$1X%4Vw`J*$}T=;g4$Yb9!U^z)b+9{pAoG`&usE;yMEnjX%FO;0j#(nT(O z`gB$J^!D)2(cXvYJwaJ3O3PXm4zB5gF|AJzR|ltWQ6!wcU0_`73_NK1PDT9b2Ldyk z{%%=sO+OlQbJNe2?6H~tu|Tal;RTw0BXqpezflFx5J6jJF!Tme49*!7R7+<}^a(km zxy<%wObHu4L+%MHf@bunhx-I-)j=B2j1^(~XRM92f5uR_&^V*WeIn0PfM&cVmsRCX znHjr+R?pZI$`+nxoQYTTsqcN=RK5V3aU&QsGeQ-RFZaxnU_`DzEUG^=GQzN)O`V1;7Q}5U3CDDF8iolLHrj*6T4U z8%iw=GM|<=7l2xl0!V{eT1!f9X%CjnT6+DNlrg!!*|IXAddo|(45(#DnORHd?^|9C zRo$|!bS|r9muhUw{%8rK~iZgYFQBUbCqJib<-RvqmCJw6(Eh{v}I)cCh8 zfF9ow4D!d{7HEhLynr6xrzC%zGrv)W<>M#Ar_XSV5!}cCdi=Z^WB$DipvSMqmGwAB z!9y-TIjhRw7waEY2F;qF9L=mLLE*EeM}v13_kVDM18CMl?$9)7*2+L7vsQ;4I%{nh zVb2;0BJ5eCa-Gn>R;^u*gifrnzk9TBJt?w$ZW zaYBYr>&LinpE$29+8Xf-s|%x z*7wTdo7VS(UTzI1eOo^Yjj#2xnz(KKEJT(p@TQtwYW36(iP=H>XVg^+jB4=gEezTb6(Y^vR=N^1 zdv{6ohXfj;rE1XZ<6*5A$|d`_bi4iRn}L>QlR#Z?LkFm>MoH2(QJ^lktpn5+@UAW7 zU0X+}-?lCRbb#9W%0y~gp%mQKU%EBFZ8&ChZCjMlw(SfHe>0Z)we2Y}gSJD;4BAeV znL*pBzzo_x4k+JtC0^5Kfd_B96+aQs_HBGa!JLLLmz>j7D)*eJO4K=>egHA2D;iyM zdZN)aXJzRbgE?!WGCA+}Pceh$Y*LDy6HbNC*;UqibKX+JWX|FE62+XOel#@abnxf1 z>bS+6^Zqn5D{s!FFrJ)qrNm*+xuN3gIk(x-rF|XFA=Se6#=tGL8>$!En*s}KPlm^3 zsC~LXjem+2)E>@fd+*Y7aI5 zwjc5r1^wGkK+ z!w{&WBIYwY8safc$HdZ@rek_&XB`XKTL(8wfI4~w#+Tgf0_s>3&L4Gb2n?lTYiwk6 z>`=qJhWLft2WLq@z@nl~(;GbL_G~mgXqU7Mop-{*t^Kl`c z+!9YtpWGGd^~t?K;Pm97NT&BBfZQ}cdm?ggTFSmA#rR-dx;!XdMF{tx}a6Y*+BZZX8SZ&Kk#){n8Av*B zP1zo*dHvy!HrnYUj>C-V-M?rxfQG{En?vtgMRN_IER`#5N7xDR07 z7iErg-q)q^_xuLc&+|h9&W{KT{tuFYzx~%&$e)3P*|@r(}gQaBDjUClz0mVRS2@M zWIc4@Xw-=d!@cnfcSRkx@GWJ;3!|O!3rlv!FFYCyw}q#pM?Nfhbz@r`D?I@u`=C zG5FMwKcDK~v;%r7tIYnXZBb!6N($SphTKyJS85 zUM_P5Pu&P|s;A^msUoPGM}oYX1=QUT@Tc41%wCOz?$A$kw+Pe)cU^(HgEf-wUV#av z_g;azSE%-Nht|;TTZ6g@3)CGXOx?S}M`C-2x`Vw#-O=8m?vlMj-C@MgeJ-9)cV85! zRrhCsy06MLcb~W2U#d{FJKP=pwA?b|U*iONxIh^5R=?<<`@eL9#de)@E{=kV!skwBNq z1iCCxtxV_XF9Ur&{jI>b63gkS@Q)w$G^xz9rsZ>PrSoX783{!+3bfRzC@n|GzU>W?d3k!;*ah6H4z* z11&koqzi9P11&kDl(pp3do3?5xf%&gaiO27Q$jxzZV`E=ITZStX#pUgX%(ms?_2{t z(;3SD%!(2Z`OFKEZ9Nn2p?jvN%;1?V(YgL-wkaEW=8eEp4sbPV4&`6 z13hyr4B(#mFj#JV=A+nZ<1?3)AwTnZSnQW_nkIIq8|ayD!X&rX2%KJTbUe7Xm@Vi% z8vgotiJp7IodUg=Lg_y(xmlz4TKr~>-doDMF0BX}uv8reUaCbScd4Z;Vreo|#nNeU zRV-}{YF^qM-IuboPZ{je)zPXSm&XZXbRisQX-0L=(rtdsk_}RWr8`0^UAix5_R@Dl zb6k3~qDQ`>eHDS``hppQJ}t1BJ||L2U$Y9y z`eufIZjELU`#NK@h?imi^ z{2TN@edptIm3^1X<|_Mw6SL2XyC*H5t%*zc?3DOq#=8p({ay6=VPBO zIUoCM_Fnn*v*F&5XZOh+F>^T)`K6|;u9G)$) z_GfPe$=$Mw($%144e@QzgP>*2vAt}|+Ev`VtS1n0S?Lb2WzUyR9xm%wq2;m-5&4#F z2`9Oh?FiP8m&MNkE_){!Ma$k57#F(}5VY*58vV;YEUo%tY&~GvwZP7neHn`SO?-p? zGKwF_ULI@!S>CKp()u?Ef|dspLCeD}q075tn*^5kg~(kV`nKiKVesX{iY&|XB~EVn zrYJUF9xjh8e>=R!XE}TJY_P7h{M~54Ek6;C=H=mD(dFUwFUvnET~l3twZs&b--?^U zir@&;3M&{lE1LYt7(b0&F{QM$6>Y%}dIhR7xpg_viXggQ@j^*eE7q1&m5mSF6`_@{ zc!QgkPs>8zj)Q;2fp~DUBA5kUai+AE4@+vf9Jb@8KZWMsmIzuA9)^9cCRE^a6BGoW zo2bU|bIoPboX<@S$@|>=lA51e60iBW7o;Vy;Lpi;&cA69^xSBv3ZIilfD5U0Tm<{{ z=(8^>ufxog{ZIqQePgZt?F~Q1c`fKHjYJ9BhkG>l&QPawNTum#t z2klz-)x0je*KYpB06=vs zL3OKz_og#H#~-(V>b42*`E1K5K;r~xT#fKHz41T&A%A=UG;Xc%p6IKb$rbGbRUQ=H z=BBoK7EyqzYJ~Uw)4zL#i)sVabqcR(>f&DpsQx&p{sZCNe|??HA5Vko&k65m#&7vk zfEo^h8V(EZ>DC`#Dcp0eT zdEx!&fu-NDDtF0?#=Mz7`ro)6Ri{8zXNC9o`|o4nt3L%*Ulrb!Q~Qc6?Ps9+o5K5( z_kZ(imNo-w$P4e^JvQxM@yA1;h9kmz`=9jwOE#PXpvHHFSF2C`8d!xFg*Wd%A7dp~ z^nxl@2+#QOmNwQ<3#hV9c$fZ3eo+Oeax19vb?)JAP*tz+UjFv*MmDEbP)&#Met2Jz z1+TdUs<|z^|6${!XIQkUp!%7@>wM+6-($^HfyOlmZ}szkHlIIsg2r_T&-muxkANC9 z3h#?Kdv0@W8~hvSsD3Y~{($hBufF4P@i##=UkUHrob-4uU0>Eb_#FZFYM*tHogaHd|!CCw(i{sG-Imp?%z3&I|lnfI3&CigWa{PLIPD* z32)l3H9yAPb_G;*U3mY^?q7e1g}(->xgoq6{inNGgma+!3*zr*|Lb4mI%+_Tjl%oQ z)qnP%DzFy)0#w%xs_PY=@%;(CKn8i?ojz5$j|apDpxQISTlEKjS;+(9b$J(+xAt$` z|HM*V0O2Fy{hPo2K3j7|Kd53@cy&Ko^fZfMfNCZR?+-`2{sn)$3aa@`cn80?T(DG^ zK=q#p?{wzIJN)rBsGfwk?GI10={Ht^8XJW7?yiyF;fhv*uv&O6x$VT=u~S|TO92jKa^KR zd1F7nY%v#g3e#XcgX_)YtzlkJ(M2hS!9*a{Lqj zD~ojjH0~qeRn+bMH5RK0)R+|BYkz(FGaf5NQ01#UR^9>C?-$;m4xa7f+6YuzCA^_u z%YVqV?FH2y5Z<5s&gv?bXZIah!gB5hjXNZ~>dM=H#UHPL#$6ZQ#qYhi z3%K(U?tCh|pUV%OXSy7cH${3E?;K3>$E%>q&xH5mAO4R8mel~&P88mcC;Zmm^2cxF zWpCb>|MAcdScrw7`X1rE^2VA=TuldP+(O~)9G!IlxN{VDjtg)6s<+lufGWNPRSE&B z{T5Uwgm>^}`!8{Q9iYmE!W;jq7LT8*E>P7H;r-j`ySW#u4uPtU2=DiP{f}8y)q|kw z^}_r2tIix_y}kgdSu4D`hyI3HQTrvR_G{t&@b@UaykCFw>0f1= z+YZ7`wz(!yMN)Ww@#HVP$VRpWRIyEXKWl=ksQ3z0!AASNcmAjXRK@?+F}J6Js%8rB zl~2z8Q*OnFpqh)qJG)I>=8xAwHJ=Ob&V+ydb1e2wP~Drt%baZc4Hj&xyb8LQ@Y2uxGd95Oph_03Y6+-nnee>K#_w~r2B>Nxf4@Rrz3I)LW&Sa1jQgWT zc-#NkFiTQ98B{w(cz0%24fDr7Q0+?L{rRLNud^l20@b&(CB6=7*d@HD{>ibm-0Md{ zVwtjc`%BL5+Kax95-E*w4l=pM9S29(wLNn|y5(s5U9QKlF-O2^_9E?*W3H&f6h9ussdFt2=7O~)ye@<&AXtQ_k?%( z@CPaGulGQ8?+fpve|?QLTz>^re_eP>-OcO|#+{JYv3X7Be`y|hfq%iSzy2(!{=D#h?MIhdfeqLqykDN&=K3oxgDS2G@A|H5r&*i>pvrfJ_oJ=<`Nyn=Gx82q@1beGF^6?_lPN8{CyIyO z=HFffReUVG?K5uw@1(tZl#X@x2fRPmb=}u}-3QY=LXw0gNn?^EIZToyBx#Zml7yU+ zq)C#T6VfzElExuv8j>byk{l*UOh^tBlcottnrBGb_WiEC=Z{~j^;_%xz3W}ide)k+ zX1>?=xc6s&_uika=)>I&arYB%?lkeZHutNbAs!$9YMm}av4R9*r1TuGe|r|l#{K^gJNPr91MX$c9nCEoPIvlr{OA5%F%{OFIp+vs|Cg~T$5 zZ_D1NNIte665B`|yih}Xj+H}V=ZK%0w?})92atFY@k=Kfjn!tRQPA-ZQyPr`5_^$2 zNc?r}@n5VodpsmEiMY5&Ydz=we%0s1)9cMu;S(MT36CQFHs=mI z_Prpn{=~Igem+;{-yDM0#Ls+|r4Se`jTM^Hi}uyjOT8ZAZq#q(8+$8HZknlZA+#3~ zI!N4lSME!C@X8>;N#c%|mNn5`9;Jwv`0v37^+-qc9Qee;Z+Pe#om+iKydiO;;WHEf zxf>wv7UHJ{1e0}%dqBKC#CxVe*EBQ>5}HSxI`)j(8sR38aC71xrz}g+<|s&bEOC>} zH|uE6b0P6;;v0t^xCZEtLBs{O-85RKl>tF7;yZ8pLGg^^L!3DA^{M||q_dn<$!{!P zJBZhj_?lzqREP$XAi;Fvt5>d6yCJI&JNzxA|7_`dA(Ti=8$-6Vt%mw%WC_~Qh=mmsILG?O-P2s9pZ1_UQksx zzLs%UbKOtQ=z_WPAnttP)44Od=%*(@yvfAfpE;;V-D60|A+tIWdW1_L;WFZxb8jyeQ;$4aF%9uA&_ojU%+q&rg9FO?rQ!Ny1p#ub6^s3E+gys{k z?J;q!f|srkCqug}fJ8PC9~gX<%9C(iNVoy<%mv>XI%)t3rxM??$rLc z9+ltG!;okxaj2}l>Z@2YWB%mb7ZvXWq>2Nn5)emn8Yxgfqe_x^!D`_JU*hdI7tYfysSgP?Bwn+B^UpefMtWO`TQ9uib8U7vVqt#ui87TR zk(NeA%T*sfqIPezwh^~-qs*J)x?&9=@l@is?ieyqPiAtMy+>Vjk` z+9f_8TdY=?KN;fBBwk;=MWKE%2@YqLKjJcu}uyKFos zA(3+8dv02~Ob2jI&joS+PDdK*Oba3I4&uWZU#LuSPa8!!PpfzS1xGaiB-DW*eou(s zm-w*-e<}J1ErNuW64z_}m}(Y(yOBKe7dcOCb&=SPM3G(&hPaLj$oY`yBH~y3pHf56 zNrgB~iSH{ro} zU29~#d}G`1ZUx$-6Y=)vuXtLA;6o56K3%e4hOScyBveNHLGOlo7yS7~?#;KHX>Q`U z7bNIUTz~2Bridm$;**J&)}A#=y9yvkA`Z=J`Iz45rInQe#3_L|WyH%CHB>?2or8En z{LARMdcQ&wAfd^`9hY6I&FD^@3-Rz%$908bRUxsO#7P+o)JlkFL*fgGZ~ngb5+Jb! ziS5K2_dfNNZbW@ZxFPY5OMX_}6Dia4PJDNRh%Tn9S1KSr^XViNmi{`3zkzuAGq0$0 z47V_{aBhCzOA1!Q2US86PnmPiBRcJNkf0;+Y?`-To3qr^As+qWTu+XJ25bb|y#AZ0qL9dWg)PK~Cf%rdFxtzgp}E`Pv}A3LF)CZA$xQ}_Fm_hM?#q)^BCDOqX%UsB)cA8JL8n?;OF-bJlAtQ*XOyf`}4kE z_xp`$C}`d9c_Bn1@D7rj-6J=qhy^hfIH5>alMG68G1eB?eydB4Vn1kYOKnZqIpwQHzT;qm;b2w zFR4xJTio7bP_tV^Qn&GKf)h9t=2pogtPFv2>&!bIcphGBHWtt({u1phxNcR za_5t3lVO?CoXHrywCaag*?oTpQ(aqiOqSMs?NIsUI>kL$nk^t&tI`3e^FtXQRQC_Q zf`T=85oUN{6Yg7(z1ZyZkTwW|@&(y6tLAQ>7EG%mw;s9BRsXk^`SX`fm>X6<4se2o zPkZF`%y3YeK?Y=Qekg18GmDALk^1NsCDwl()BPskPtyppt$G825LR5lPlI9JKMRvi%#2ox42g=~Mk4i*S>ZFMos`aFj~UqhDu&Y8E6 zVmVQN_!qOno+qTZN&zDR2K1f}(^ke}j(xE77rFQr$qp*$qE@b;$AoEeGYbxuw_k+> z?*QGwGJ;--BT>LA1NZ#vqT8$r6j&ZhbWko$2_l!$oQ5VD0zjG5%|_vtYfuq>uh!MqwyQ4c zO|jUm3{SIA&fC=3WFD@EYY(P$>sD|UVuVNG!1nCaW0Y)8>&EuT(dHxzGKwQF`(jDJ zYcKSH(mVc5h@H7_{Nnm^dSS;;$+_-K?Ke!}YWm=uSX=l9>3?uF67+iIW|4y>33ins z+`_#HWN6_4^Le`h&IjZ(ziFUQQZqRPGoQb?qSi&w3%XhB;%DyF0IA9&!%bInQrLJ8 zK(o|_eD=tf7SWax@RN2Lk4d_RScQ}=T2I7ovwy5$S_!=ck zgW>k*k}~aXAO&~%!&aL_S1o4V{g5Iw>?*x)Uq6Tsh{o6nUP=~rcWCh`5ccThH^k3M zAeUrlku47!o1CX+KO<{vG7sLplp3=*5A@oCW}KwGK>f!-C{8s9e>Cuu4C-#6=(EBt z#FWc}X-Ew~Z%yG3+J)hZZlEV~)$|fQEDVai)bcZBh)ab3E(W~AKMZ}_cR@*{$OivQ z_jdiqgeZsYv>mr8wMpaQ<00UW_IBq@W4x1Qvg}H_y=k2-C;p%}N4!goVne@qQ)E7H zCw0kHk{e`((9^9|e1z0EFy9R@NR>arw++>Z*zoRHNujaa#I}{`prlOosK-jkuUhxV z3SK{pi@bs2yO0hU^WxMDrDTU@ab4(1<>y>S-)0yy?Cv1Py@>&Q!P29p%9kzXWQ0S8 z?s;FGxL)8T&PY2J+X)N1ikJ|1==TGISA`iA5Kfre^7FG_0V<*!#pbheBxnOhg^?OF z3C49nQZVRoB}O>Aj503Zd}SqTQ#>_7)&V2qdAq7tDbqgbQt|RcNL(Igp^g&Kf95EF zs=AF%Zjjj_ho+~!c|hEskTc(MQKT#aba{61F&g}2j9BrHaN>J|mNaHI@N-1gb7nBjugDJN((WKutg&qUx(6S}0(ZhYs=rI+KmBtS^6wcbQ^Wv0666ECb1kHG zJX1bG9Pv2sC(Jp=z-qTCYujhojtf}5I}o>({lip5Z{Bw>J0s}+^hS?B{`so3O_fUx zv*nX;8=^N1&5C^pzP^>3Z7f+=ubd$(1vBsj-NTjcS+%d+p32YeKmx8${uO z;ho{sn-Ll}80k}$x(sp#s!%uR?{xo(*?Fmts-hzRhoSwhhN199Rj`yiIc~#@^AV-) zaG*l)=COe~u>r^%ViB9nyM2=?71p(>w4Vn z@t;1wwQKxt^4qW_NWSC!Y-c4;uPFR?vs;qD*30-g_1-*8LB)BSP7I0y^`3@Mnxky2 z%ijfoj=_~>ODfns2@AO6FmS6S?j~l^Oc!%}N@}`0bs+eGS|emdvcRt5s1ner-5Wo= zRe^@)yhBADr_r3O%$o8F%k+G9>Ue0<(#1<=gB^R^Vx^Dy=7n-mA!Np7ZL!3aA$uDz z@y_Ng)3#%qulI5sxQrb{y6B)ws`JiX;}c@QjD;nWo;|7Q$_70nysiQm``WkUvStb| z%m&WAybG`K`?5lX_-xH~akC?8bO;MI4$H^(R)5G4vigeuxx7=oFH1``3cY;&we4$K zC+>?H=t@wAc%W_uV zpZpJyRd5-UC0&N(X~1-ce=%+1Tp+>j@GBFRqSkyXv{d)Mhghx1^_6-_V&-189>wfV zRncTvGYjmTn4>dE35I4rq+dN}f*SDKN)y9u8s70E%GHV1JswT5(J8oh!sw@~>lJN9 z+b}{3px~A5wfn^tQpQI3Ae9$BUNI6IFat2=7`9vHlt6aL2uEf7cBR2nRL~tQMdyc^ zHXjbnAo1NF8?0hqPTd3>5na!qnd^mLCKupd0Rsc^%oCb$(^pvCc9lXWra_qOgZYi< zjG&(RjD%6lT9=$v7Ta0A$r(5sluDBe@ zKE37AuNtKXDx~WFd{+!d0RlBPN@E?j)ZqkUpoi9H%*}@n(UpBX*FY~MSOJZHRH4Jc z#d=|+(ntk*@rLoduqa8HHYs5tM0Lt^l@8h5hT;tsYm5yOp+CL(9F7|-_8KlcNijJ( ziW}{B+}@GT!Fmk91TDz}TOa{e7Ya};3Ys0H8bEw`7bhHB>@C8{0B-g9&2*v4=BRD6 z2rRGjJ-^fO5L9DC>mYG~q7nZX9ptLCO>ye}2f@m<57l{pE9`MiLQ zQiCmRI?j2<;&|bKvx1KVUQe!=06svMJ0P?=A4`t=W)E`BTlm8}pio==c_sY6Y5Am= zI2UcO;Q!^l=r%+}r>l5<6mIRxlV;;wmuP`nKt<;kz#IK2p3^f4`EjMu92 zDxvUHLbRnF7k}~ecKauY2ueC$UT=|q_0y{}2x#Bh&tcKF=ZE@GVX1}XgbZ!~zsk6U0~d<-hs`vz6hB=-%$VHj7v z$4+40vAB5$guao1oIH7;Jg$wz6@Px$x>ij1Ogh$0!$C!g~jA-|$`oXj1Avzl^O*SP7Y_tMyN^fDZ)DNH7Wu)Fr93>!43B6saa&;LfWi+okMipAUS4Mot$-1-cd|P!vIdTvNJ*lMrIXEy~ zhqNRmnNiWEMF=8n zZ5ygPeQRQYX~u%kwyC4pd2hd-P0ciSKM=2 zkYaNIoi1Irr72*`zEwgYJUgGTNcckJQ2JIEf~tyW9ktPwjC7oYDPIh@?_Q6-8+E)H zCLCA3U``UbxV2}GvZ!?nph0*jSUr7q-0DC5a&`VO$(41IIo)E%d~D43{l$%4!$(BC z_U{fPDVV*bt7J2%kNYAG<}cCKmKA@E1k507Dk^%KVsz?8I*OjXVbQ$LAZrNpbH|VN zB}Sxp`@w0k`Km20H)^UTSeV#2ynEErXPKw5Oi%dL8K=^TO&_ zum?cFRL???U_GqoI%xhET=Tu+GJh-7q%WOP!_Z7v2cRYYZ9x0QCJZH!6tE3!>6bS} zXiQ*Jlh4a*E>>ivY%sOKRc-7;btOV;nDAltpwvV_9`VQ1@8z>jGpd)WQ44_Z;*YI7 zS*(T}A)dG3UYV+$FwO8nj{bztW-vrWC-+camj>W`$Ina9^yl;Z_Sv?$e!diNMuI-r z>4Lyx;kzkL@x9TyT#FQ{5nO96Y6fLNNlN2jNn>VCvu1X3*TQxW2`fd z*JvBMCssW$Ihu-6mj>#sO0aQdfH`$+dIG9Mg3ag8AKyt$+^0^C1SGKoolQMFlJRO1 z*~h;*PlA29nyO(U+&cl-So%k*y=&OZi_H@*`P2;Dlcv#Pc7$zWz^8giOI4oR*jPY& zC)`9-v(d;@WB&Jv_W`oKD)72YJUe8PpDR7MGLaNLYnoZY;tT=UuB3@sDyi1o7n*#4fLDmYcy91@FrR<4BlM4f z62sRv5`Z>K^sahyj;YAXXWgay)BaQlE3J-;aekt3u%1-8KYV-P0AnHyoNr+C;r zZA(5|M2eY{;dM1Y_sf&ggqQV{pa`FGqkzQv5((%B_u88>r|vl~ zXC|y@bA6deWO;9$VKy3a#y&-I^$Ae!;4{O8y_*u$4%nQf4B|bUpoTpKAw3Ha%^a!g z{Y13L$ug~t8Bpzg3*54k)6w*Vs74_`LvQy|A*C0r&^vrwFQ-e~_O!{N|1h=5#6S&! z405a}EPu9Am07(CDSn6WcBk5KOXy_;&<^45Q(E=6ba@$0%}A2HKqmSsi1iV9pSqcosxufGmTe1*^}8ch`(uqurS9jGg_)h*d9ha@19Xf za{Dz%v8` zf^X%&gBXamO8A#9dGllz&^rFHg!W$}lp_pz6F z{`H#K6o*2X7*jv?vX$C#P@wrCDyL8UG7Jekov(qwVruV+OL=NAiO=g{aW~J$L@x!Sb&!e4|9U+*BhrL%K=p z=9;2sISYMJ~^>pUXkr<>!flxjDl4z~4NllQ%6jZf-vHy35Bt`_ zn}DXw);zy`~PBx;+!9ZXsRkR6{T>}x1J{~H=+m@|e^|6g<$1BdV^;p#_)pkyDo=kQ9#b|Uw);@cPVmaxVI{26{q~1rjZ%fSL4NY z3tzE$!Yf@`T2SK~#Y|C*z&~lTH_v29pl|g;tjG9PP4nrn!&mCxp2QII312S!0vY0$EgkY;nr{1uPrJjrYullclmZccoC^MZB0=b*?<5=uDRw0T}-Pw zRy)gf!l2vw9TGGPFaca5a zK&^*ZsC9Ug6v9QjBW&?1XI)a;w2YzU>)f+3{~`GA#~^o@mj?=e4LYYm9N(j7zW?;f zqXitxx^G*OgJuEm5}wxIM|5ZITtk1mOvXRPEK!0o#&2b*!PF^k79^1aG@2d>8u*-& zpbDxxJ6G(alAE36)LNhF(43o5lOb4F8(^A0{96<^wQ;Nx0yEBA>ns53^td% z;(-Lr*yUc}^)IU8pw{o%PG6%$*u_Io>}$F&o5O8*9TI$1C~))1dnBlj^}pXmG=-(b z0@uM%6aG8*)!4~KF!3Sh`!}}*sX%jHTYrKOIfv;hm}w=0v#o;G!1BKNC*6>d?Xmsw zRa=Y?uJ!1+RAmF{86ThtWH}9p-nVb2W(V{9S1OVF=IX={P@HQm;UIZ;-elg*>6n-;$kJu+FYTsjw>DnDddw@# z^hk~hG~=baT(mXyl{JTR5wgwln3J`)kVUi@_Yz~JcYL$ZTFwGI><6BlunElypWl)^ z@3Tkk3ldsxYlRec(#5tGV}g}lOfSPREtH_BX`clh;n7@-mO66TcXv*mXi)i5UbV;u zW6ngBn>XI3q}u2{3x!==vzP1iY#fz(phiK+czZcsyy_L*s(`g?#mhb@%fCrA1>*`K zMh#Xzmqv8pj;GQn`mYt6hCJRO29e?{A~m=O1no-CAIdqZxvRi}n|G*8Qv8F}od7MbJxzNUxl3L`(8b#==f7cT@qkrS zt0>&d8a&YUHal5%x+B52_;-Jy)UCOK$Rl>?5wSV*O9COtw>W<(Q;Hy?0Vuqi59y`v$6WqrBmx4w(!SANxWE08os((n(OgH2jU@7D(wb8O} zFN5)L+b%B;QPfpSjU%P1{>E*fiY%joR+J`~w6qB!+{BBmVdL9C!BXH^K~U8e@>|65 zdi)8?XdeC5tmDLIEUdi!5ea&f(Yqq7iSyF~h_^iYXcX_W-*G=C<~vi-Gh{C#*4w26 zX^!Z)2DR1uBO3Y+mHZ2VGSWwnsiGuaAE&d`ZJzGxPN|wL%RZoW1Pcom#_h7&IiO0~ zik>45nTc;Hu+@BBrvp;4stJRI&fLt2rdE__A57L#zOSwJG!^K_n;+nkr1{<><~SCJ zj$X$58ExR62ZONRKPH?~q7*@K|FFV$QePs-Ix$=N)|CO`^0%NP{1-wc53tdd-(nND zqk#nMQAp#7iJ|#}r}aD9aA2merIZHPtG#t^DncmXBpi^ z_`a4!UzQwge6Na)nP%LbCAMcfs`;2c0tnwGEa8Y!-lwkD7T_sSmd^6PcsSt&e33mb z42rNyKHh}q2uA=�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 FPh% z-1YsZds?^+VPZJjvW?+!ZgpY&98jAOG( zKU>Ly%UyLsLi(D7M3$rXVG!?anO{|IS&pa-cAz?YZSwgyX12C7Ke!qxNNZ^4t06V& zK`BDZLk`~O>H4>5m%P2p3dm^^|3#%FT1rp(&WD8$-*SUpcvHbrskss45=(_J0^~<%M+% z)u=$gW-qbHxQZd_<75v{w9VK5?#F1HL6Xkaq+q}B0GooBDaJazbqQC)V3T2s3kU~o7VM;WpUWZgb%_tB zXw8pNRoeE&;4~ED-1x}{Ug*Jju7l~eqJMfT!|wF6K~pKFr9I_$?q^8Z>(&u&1=HI> zXpDKr@o$BPJKt9;F0^DJ3=lTna z#t`#vmalOdEYPDYq-G-PwQ+soUIlNPEbGl4O0dtrWNT(WYZJh3A)N8g7}u*p!RM=* z*1eGX5inDRrAL%b8-vqvvYEb(YT~gM&*`AhycJtaxRJjoDRf)&N}?4RTJoYtD==IV zA0~#i8OTA#9@jg-3kj$3obWL7^Unr27a6cog{Aqs-(W;c5JT zNZ9}z<6>qRQWsv-_5Qb~iV6@U9CCX%Cne|O-if1M_VhY>ip7x)VVBO%A85*biFy)g zP{shbB6C%y8c^q%@}?jQy_BlFa{DPM)m_HT*|0lp>Xtd1onHL}Od!RIFYwz~R3S*W7}AB#%;z`B|-B z6BOTSK6y{~GCav?g3>POQ?LESh`>|&-=%g*&>5scl1(qzqSyBJp`^5Q@OV?JP{ZQL ztQ0$}*t;>8%)zVim(I}Z23hq)*So!P4p|L=pKe^!d+lXUX+EvOn#rITp!M->-wTFi)dA#{W z7lA_IzKo{Zf01z?l305&nfku0O@CDk2#wawM3+UyQ67~a#gM}dtze0v8GqYnpo#zj9K5_d356ae?Z$|tRXYTeSM;}Npx`>VzLqza-aCw zgk{Nd7{{XG`KKsz$?l7o9kbm1M6uZ`KE2kwi0T@eJ}Omk#PJ zEVQ}}xz`+S-qq3#{v*MqlgRr4CGl_25(-|{J_~QUtXLCmT158fXkL)r4IO zOyi2cPvPJd%CPOf`pkIKCnrlqEBsou4+4^u1&)h4gXm8(^ zj42T@qrRu`CR{*Y?iq)!lfdnEwK^=J6e z-8eSg*A4p=yeZAEd1(YBl^+jbrJ+}A^X*%S{zIqC9AjwYN9Odio3xo5ZcJ}jblsk+Wj^Sc_lXbAlHSR{h!Us$Z7VvULum zW0`jvo5~b+vwe*3v#(w2r+#wtcieum_Qld!u#4gIsCj>nYh#T?c?GE`FE;B!piq!RTC?4*(^pLEo zNIBUAPq$DSJB;45cXzlUwR25a(f+YUe}TPsn-=KMqmvOF@+Z=$Z6<@I-NV*LvS4^$ zdM9MLNfPYF75L=cT9t^+x!`;|uwdzgp zNUc=~ak+YX9{zQ7ylocrnUX^W6T6qTYwF0)FQ$8=uxc9Y>$o#F*G zL3wpZQ~RQyw(=#2B*=Z4zV22ie?ZmMAHw23{T6}$lhaF<9S@7<;`dZx9 zf#(<6ROjx)|6jdza*XOvbc|WbRDCS>&Y00;u|-zG0VQz*u8VG(_egwnzg}r5Ps%Q8 z#l~X4Z7Qg)@rG`E702<@S;_Bz3zL(kpb;WvlY)1KGaM`MnMwA;l`M`=W5beazx)hK zw%NZ7d3&)Y!J7T?U^XQ@XJCO+O62}zr5o|=kFq6toWrMY&i%C0ePFv#Y$WvI#7-vc~Hz52L2K@13#~CpS+j@p4Pj=@+LoIWi6@N%=?d zwX*rApfB0|T~?;qW8IJHRk{A%nZajhrdayL4U!UnB;)6@#eY6DOG+LO;MtCx67)}Q z?|qRmZ2gq5mwDha3}p#&>XT2>nr-Dy9-Oj&m~2`4_w{_>S;kz_<i6iQ?iyK} zt*W$H{eNk@?vVxj*1rQwk%W~w3vycF)2II>^*ecK^l9Orc&xIlx+;PhtyAwTH-Abd zC!r>Z_L*WX9DQ2M&XGKs3QxXfv9LdVk2aGZc1vJg$s!NQmR$T2k(4_h)tbaN`91rR zjUTd(S3DE7{`GEUihXS$&35yEtu7U+=|myQAyYD&74bG(OkZmpftw zt)DdB)x5n_c31Rvb=L`UnADys!%V!mKI{KVYco`yWylR3rZxIRDm+Q2_&ZU;RL;l1 zOCb2Bw6vZBz9E8cXhWtpbxNR4kEK{!qJDaiqB3zuXZA_Ri%i$O-IiAIr7NdXF^QqQ zMssbNoJPIpG{mM~R(MxubW^6+Q=pAy*ZWSCmbuZ%bD3wON~soa<(IFJIeB3v(>qN3 zxw3m72vTpWb)m$6g#z!g2Xw>LWW#;=feag&-$zo2A>Cibf0LmG*^Y-m{}p zYtrei&g%8%Ju3W`a|y4A?59m@iwYiLPN%wZvws7aj_TRyK5xqt9)~-3>2aNQw}Wd5 zJ62!bDE(vSEaanRORr4(#aa<(Y+&if{x6KZ_#NL6>uwEsy){p1pCD^qJHra`HkbaX z*C5=%{fy$i#ePrRYUZA2;j_#I*L>w-{pOITy0;{ww0Rlt9lc=|xf}LkS+B92y0}1U z6wp!JFQ8>Mw=ADg5%cI&2+w|srtA;)e`@%a)hCPG)cRiZvnmUV9KyaOd3E^KNw?LK z?2o_2)%G#uGS-kH0=;Fm;t=15hknHXP0b|b4^ZO_|9J?k-@AjiM+SWEp-bQ z7&ap3;doPP-~LJvA=(2EE(0R0=RTf>X3`l`g|2#5S$Oqbt0Ge^VF)`psr9$4_bHJx zamUWh^css^`*e)Cv`gB$^5!58gT2C{|G#$w~hcEf}7_9Os zxpy{|P7fwDo$Q>9&aCAw&8&quUxw)wl=%A|Bnx+aM%0TameIctJG`}cLAUkro;!mu zOC5?kaU9)DW~b=nhx+k#dH$Kx^nYZYqY>jYhwJZwT zDFKT>HTpwm`B7p;+=3lmx5OeOm1^UyWA0Z=%ZKs1JCfakTLlt5*BX@eXp?G}d_HQA zb`Kt@TD**2DVI&o`1t8qhpCK20xn}Wx28~3Wztu%Q`Pv@-fH+>A3}qe@wOZEk>_9X zx2IMgMIWECeo$(BepfPJeQyOjZ6>~I_+hwp?5n)UYA*+!UdhO7a*@8T|8dH{zU;wY z{-(Le>C*$!;kUgTMO`2Jc%{!4oV)BFa2kmI zXFZcUT9o(2`HAyXQuniQ>I;{C^Ivi*DSUdCp6L&Ow}x*5v2XEj<*MJ3=Ee%VaetLQ z_ck__?UgKktFpBxFKhnttM}cnLR)IjYiPE1>#p29Xja>Gb6fl621L$#6;<`N2kvm@ zhW5yiyivyPM%}$j=^XG}r%ZXRynX8;dYEcJ=G~j>+>dArhD;DNDQnuhn1|k?@#3A zdB0!Cnr!c}{TK(^K>V>wG_6g&zN?w}uBd9*e1i)%3}M1VL|q`{PS_aJMITx#&=nV2W8HH%Y3dw2$d3 zhr>S-lUmaQiS-YCgEw7wH0x-avM}SW#+W+ez5fs~c z^Cc-YJN3xd=J4F^yTLf!79o&O9TldHkSB2Pq!E!|$F2zL3u`Xj;gfZu8T!{Lz!suyp9$a41 z>q5hI<|`GM3K{Sam$ILJb(%5}A%&5ulT{U2Q^aVx%``>7uDSP1Dr3KA_@``Ae`pll z%`kV=D0=sFLpgMRZ(Mofn7x5)NN<^jCsL|SNZkNzyfEt+^NjX6J4+Ge)ON7=1h1Aj!frTF7DxJo3-^QPw5Wh^5&>Xsp+U6i=%w#SKjY zPNyj?Kdfd8k@$lQ%viNMlSYLtCepTHMQ+Tf7U(|Yx9k@x_eDfxw0yH_zKaNm->k2D zI2C7?4ew4N`#@@Cncx)ptw0K!iB0*Nki-Zat^riv=?^#xq?<@7QjXa+is%Jf5zR;2 z%o_`9DoqCcK3{|Vd?e!>Ri>H@hqzL*&>9jhVt1{)ByyKuVk9EgWTCF5k_RDtA=Na} zyTt|gkJeYXs6U8jQFJ~UOkAup&Zr^0w7SOW6{lL0758Qb=ZK|=>MN&^iWw@GR0k}p zFXTR3l96DW-GE0v`%b1I1U69FjnG>RxnM%eO7X|d%Ct1o9+&1dQzm%ZD=Tb=1ZIA- zKKZa-S#1sf(zOruK`Jq3Wu)J0$`gGGwdL=L;S4(F$IToxexnu`s;y*ua^(W5o$<;) zks$$1+On)$t8pV_d9Ngb6c7LMo*9IdNoo!Z6@&YHZAC*Dx(Yl#Ow?1+OAk<_f^U|f z#bHlr-J-u~0Dlo}CC9!GM4v1kGC)HNl-51{8m`pN4xEjYaoGMXuS@tN;*Q59Oy+-y35f5w;9shuAPcuCyCmI z;z!ZKPqT&T4dIiCN$WyCgs3idZ`i!`+$0wm@vV7J4dNT>D-MAz3nXZJftUTQRI1>P zbplSU-Igpg$9Eh)URIvi7{i$fXo2K5uOCQ0uMk${U=r9Y2NfG2rM&MYnxNO*6A-S9 zRx7HkLv|}lBLamZnx<4c?1j%uK-*a@6zb@=8-==P@%0W3P24~z(Novp1^ZF^KgyjC zUCjlG|IBKwrScOn#HE7KZ)}DJV`ohW3%e9GLQnlyggBZWyxcj28yWJvdfScQ0>tR$ zYrREVv{d>w}NzN&`G-D~p_4Vhum$&m@H~QDiiTK-_3M70TCEfGxRHN0y5q z7!OA;^C{G55?-q$q%$Nhm8x9QIljav7PmU>eDIGg+}GIc_VC${@0hZ^1u-3=In{7O zVP*bCn8Qe31{pU8*$A0!xe7PN<~Rf&HU)3Y6W~-UrWmmzKgLd>JUZkuq7i@@BxCSP z5__Y`3!>6rNny%B1dz3=B!lFY4T1qB+0*L2pLAAq$a4&cSAuNh7a?pp#WU)r?bn-I zM|Ky;Z|JW$G)|}ysDG_;SFXbUN!_Lvg$=m<`f~-F!8i%1KWsONJ zGc|}}E->VERq7u`)iH>j%f1~Lsa+o^S}Y`*5s3N~u2NJ$e0Cv#EMnC6Z-C%m%6BoV z?T?1tKY9s89OX=aZT8^|sFH$EWkEP_TJ+v707olj6&Pr8_l9s?qV}2vROIXGIBi}9 zxIt&a3*LcGNu$2Rz1fPG`XT3(7!Zi$35s4a)-8&Zk{q#SLZO!oso)|&chcyZ<%m`Z zr!a~6%l*a^n@|uf8$!e(d1j8ACn2Vd=o2X8j{kD1Y|HdaV+4bU`kM0WJatOU3V;r7 z@5JCTiQc1Y!UOF;(8B_?0X!hkpnP4plYrhBkN`pv8ai!D5>EOIqShILX`AwI2Iztx7AUGey{7AAZgp)ufXt=Xl+b@#twdZgT``;pPx+cNlS_jjxf*@6A7A)M%1@QyfkHr zv6*D=4O2dobto2y1Y$~*s0VOrDlG=c{v7o+04y=2eGJL_OM-9Vy+duFjj77=ONXpH zWb@#?@}9btRbWeHlM~Gz9JP~KHkCx--+b(ex%EoZs5l8^A?Ma`eh3#Ue9A z03~yoia?f3{vwsI7to#PuK{TaXb}_MMCia z0E2uCbem<^sLDRQzjhZ;$9ScXDOvSX&Ie=Wq~i(|#qX64kV=a~(t+*;Y{N+Y14@p0 zsa9BymZcD&$-aVX`)}VTSpj~oO(Cp`?sMpFrV&xINUK;xasX>_v3?+magp;GQ$L_# zgUu>0BrNp2sH88XJ~qZH%%tJZ&UP>WP5jK< z{zS4z5@y@GO9Id0t$VNGi`q-r=OJ#=4YwH`iw1B6oKx0ox!iMcuo`=Kp$CcPh zYBooEYyUYTBiB?q43t@fQ-h05GTZrZMVksv8Xy=O=^Go&FJx)Jg58f`un{NfxJiZz z?v09vrHN=DgI=g07~h#RfI;zz1yY>ruhxiB*u8%V7s4j@d$jmN>?lY@{wwjP0g^$5 zcPX1y*iv`~G^j6#v3q3iqAv0Ex1kkazBE4s3M%efl9}uNp#_Eq_ZB5;NIMiLwQH5$ z0!HBr5<^CO6eE}jlvkWo{@8CQeweuzSUp&%16MVs;%nf*e$h2(P%X6wpeJQmMENAl ztccNn-3%r6-LwR9QSDHWr0hSqfGrB!bbu*?lIsYIsnw-G=Lz4F3jfXdTO2npyL^5;-_Xdo7c-xHAa!6$D9JRg|2{zi@Zeg(RyQO2a(EIiwAx#R_y zjsSG4_r^;kUmbyI3P3DzN2to4>iEuQO3v21%AYNVx(VENJI3!hhkBo>Qdgs+0y> zSbpA{Q`fQTq1}0?pE2D&PJPBEmweYAK~PE612_dF2vrl1{EduQ^hN~(;yp{|T0ZYW z$+1bGpFyUGqB>E0<-$Tjie(}BFOT96RUI)&@R98QrC!P~ z=~k0i8V!>}urCqI0mGX1ofi?I>D&hx`9LE4&>%;%-g{#L4Db^XDFy{Bv#Em|Cf1Tn+m^1M^nYbNr_Gj)T{AvX+refo60mc9M-8S{X4 zU`e!O@l_OVS^}zfS|@9LvK>wx)gyou@q1Dp@){9P&Qn{MUJCiXz*!szn1PM|6KLl4 z-pKI&R*Y4e3iCfvkDUUE4ya-rtq6IljJ;QRziA5O^QOr(oqIEAA-)Nl0h18`5i5Zn z_#b6NRBg3CFjN7jo-jZq??+H>WbyCnelvi46GfV^?fHOLg+dp0&zK7SuN<+W6c3=I zd=6CuANb%CaoK`06HXOS;sYng1k?uzUR?NINO>dKO^sg|?KIH&noR)|^1v!%C-7OL zKKnhE*&SK8k|J2R3so?opKjVB5&J6HG{vQ1d92Ck41~f%PC4AQ;PU1iM-n`Cjbtg|X!30X>y*PWj5G|Li-c0CXR~ zI7J2wH$ZC4Is8kDZ3O%t{(qJwgEA6d(ou2zFV8MyP_~57phTiUHgDO02)ArN`A2MV z08w)Rp6M&N|pphyre1-Zyc~xNO0fKheOhWhQ65UXnAHn7P&Vie|0K0R? zd_Mq&&QcGT_L|XrmokEpm4&Br`U_5R2+5Kg4-UiyIV6q(jj}}{^?|5AgQ3V>ifG{& zu&JO*abXdz_XeUWsdI`6%ejXA*Wwd7i6G?`tZ*o0 zLRelQuq1&3)b3$!@lOGN^A%%(qyP?cE?|)lif?--B_!35es6|)0i1#T&M9Ld0SX*% z-l=$CmIA(ig^@izcg26lmpTRm@jF8RIB3zJGNK@PpFVsA=p^(u{toJQ2DCO1erWMK zORX#hp{G4}Xa^|4-vS^(xp9DfK%yu&q1QoX4#3zKx{v3A0y31sf6q@B3dEudAzOVv zj|xO6Y#G0onD3?Pd&v%z(Wsm68V9Lb!_hF`vkGkjLqBtccLt1V-%FlgU%CFzvNc#x zR!D^Z38Vx=AKfTv_!y9{A=)Zx2rL4CF3R@-PB{Q*Y&|OUCj(JTYPZb+tQw=Qov1Cm z|2g-J_|6eTpEl|%1q4t60c3!RP{28sTu~g^Z9XwBU>XMMNQo&~jsWV~jZ7?m%%8#l zzz3hW8=xwCTLlypTZ4ki^WWY`P=G>f#|I{_6zo0V@Mwxt@ z+@CdI_LjPjUx5N>q;A1M@R0A~h=6KJ?)Tl+;2>@X5NqF8hD+c$2Ynv_eJ8a5W=gV6 z1$$T!8XVobig-`NcT8vyrT{W10k%96Jzx}qT5`VYbqK4CC~*iI;7jd)uc4a|<5>x=En0Wk) z8X?T}&JaYdG3zM`S||c}=Sx$T$&&o|FQrQa5Kk;PaApQ1KVa&Iol_n_6afdt984;> zGOEH_;HYmYR64lw^BHqD1~9kf19RICzv34u6X2`D4+v0%Ds0MkYe)LLFYo)x2$+ED z8oze|K|ij4qXHX}XDN0Ns1hrfGH}$w0@E}#&;bH;*Nb_qLZF~%;zkNq{jxsgGL%x( zDLjB93>X;#z}zEtAAblXi~3sd?HvJl3>bhw6a9|R=fo&`lXn30BM5;27#Bd4t#G(X z5>?FqLm@$-Frc8I8trZVKOiy85x05EL}hJ2p8%D^fR^l898hRXB`~`J+R4J++ez`m zfTW*50L^f4#bXiC?LAo_L3y-+gH-HBf>dPB5`dgI)Oh!U5rjka`9F5|cPB)>{&HoWIx}jP>H?1 zvl5CR)00TQ*Weg9{HKcWz2mBh{=>)Losn>d=-lA98JKAoAFo11G) z&JDbE6&4n5y)}*NH7i;!3AmiLKUC1HUe1)r?!1Z1?8ts?c#dCJU2J~Key-};HyU1K zrd-v-Az;{+IgoA99=(lBstfew(ZjI~TdYxPWnYc!+!}fnwkuE^J&#{h;?bijWdH2Ewz_1ma5&ljWJmJB2Ud9L+{2&i&!)w0z@UBxLlGC zJ~4MKNSR)$Gzl}GSt{J^)yj8fFz|4Ddzn-5+8e)&%E+`XSbA40;?FhEZ@1K=skSjH z4<}VVU)b)VdV9HB6seDHk7yJpFz}?a_W!i>c4+0>hJt6S`ZQTFp7iLDdd$R5K9f9V zJhD9FANbv!h&5wzv_C=I!I;LRS{+B;bfoawY$(=Qc~J|YU!ueta$KL|46)!TL)(D% z?>AkxG(y|3XUj!u<@%5hMNw0N927{*P8eNw-$9rtqEux z(&76ZbNRM0W>RBgwHpOzk{z0Vu|Aw=yLop|O9`qIOf!r}Bn89wA0z8!dSz-;J?e@= zAO1izeyS}Z80RQn$fl2N+c$nTqSj$>Jwwk%LEM66nd@*U@`6vOA= z{ON>{<*eYx4fZ9Z20`mVOKb9&UaF%Hak9f>%f}YCKKY=NXIN|zUAbtkeoF(Q46-c+&AN93rYKJPCz&GNyZO3)~O(jr$2rGwb;eo4sG0 znT`-A^*_s-+6O-GUe8&&QDsW)Nd!M-kTi(6Fg$e2Yu`$iX3$4prZ8}mrsic;K^|Co zcjMY!Lb7Qb^s^zAV)cW$|Xj|9TQB4XM{S$VioDc`Q`XpgCB z)wSRoif5B{2-mTUaV1?4w12mrf+D|*h`^G7C*K>6opKSw{TX#Il3*4Pjd9r-s#wl+ zF8due3L~?=j$Z3~viXFojuwN-C3&Rf!R8d&9drJqiD?QSx8f+u>G7ljqRG?2o2mUA zllEjv!Ux>aG8bey+C`_7jI$C=9>P4hqc3^iAky)}Pf{WM7Ww_XDM%~2EpG3c1K(AH>ac4J09m(~w)BRnQO1f06czr19 zq8*2trw~o((YEn-d?KlT_jeVB1s-0(+yO{=b26C?+8g$ULSd~L|5VG^c?)IXPQ~4? zgC)nbLH`O_!0*61n)eYSZk2U{>WqH~P31OMbRFx(vWlI^RkpO7GHKBz&J@ayUkJl7 zh!0ws`$vs8zOI;s4m44ZjS?|(P+5-gip^~4|2}8Wm7?ZXNb5szYh_z%>Yx9trV~N? z^mRo6Y^rIM5RJ)i2c}U;TZ_(eVw-9-U!PR(&(kNiEewea79;^)3s~`R@Rakv%kwOF zG54*j*a1dYKe=jDb}vejYa>osB98@BXtAdk+mV)D%p^vvi6{8kw5fO!;mR`|Z&;{< zZin%!Z(8#L1RgsAo>mmYN)Y<5|0ojcP1+8h68=dNw@*ExupYCbguToog((}u9OyhT z=|wjxfUQlhGY%^LX(_}g;cW7dwwjIimv%ETvN;d@Bimnmu}s_So}ek=5dgxy@If!tYHyUihd`-+o)VFB}jKZ!ZdB30y+*g-MJA zZaJ?nziD`NdnqltFQsx99`Ewqd6}^G3##1Y%GOBYHCb66t?O>0`3bBq7)vtos)E~C zTl*M{QcGi)aD+G$sOY01B~59EY7<$sXmCC?7Vk00MkhVakb|BEus7c1{h9vZ&f*1e z`nWV3cPz@-ezw4Jt$RCZ>U$$wj{ks?d$)6}4f%6x=Y4htcw+4I+wkV-nRDY5)$+0D zi~egiZsS{h<&URg7tFTKstx)$iv7kTNJuq`WSNgwmC^2BvlSD5CqMMgU6Vfaev2|H z9RcoD2_AUEd3z1xG>f>d;G2Ei93_oftJwc+*rhST>Mp3}{21)|W3RFJO1?{(#yD)* zrZ&c%pJ*iz|83;6WmsnChs5GQ#XRGrE4PVuclKX+z1upx@PRm zIOPK0ayb%wJ5Qp7$;!q|-idoUt;>!dT&yT5tFtV~p7`89ap<<~11tE>o^3|<_u1=K zqtR=}y-qHv)JH#FRNub*!uHv9SbpmPOQRV{#Gy|#Ml^C=xy1E-Juwp)F~-hRGN@lT zE2yiVm_RnU;kcjyuVLNAXiEPMLF=QRHN>1z*+_T$3zjEX8qs2 ze+9x((WBD%nBTIf(P|5WSs(Zq;o7+w5+77Thu=Psx;acu1yc|-+O?lRO`uevLFl@n z35EPF9GP;ARa~a?OjIxxXX&>Wql=+W&REIejw%lbsj?Aso+Q-O)?gMeo`w|eios`q zqZIkm-nTfAg-31jIAXH?)iG#@RcVoSN%)EHlOb|N%#L4>kDEX4TVgRp``}CJ*W%wf zrdk)uLJeDqs893b_%;s>4u8aGwi~OS&dIpAb}VA(C?0!77z`$1&(7!Mpj!OiI{O8Dfd#;f=$isY+TU5yr@%RE}UxW(S&KQ zsCWfam}`z`UqFF$)QkO0=ezf}GeFV}uPiJtFN{4+dnIKkQy& zlrw^&m8O))uLWQtw+rOlq{Uxzqss>I&&nUejFSvu+`t?)v%K?^^iQrma%N zyq-(kss;~ynN-dDk%^_Leg2jD>!TWEyIShB7YE8V0i%vp<@sQKjV!|mp^wCseAen85zS=v z7L0_MLgKn&In(<5(r?=0jg|~-DZ)?+PEQ};VXiB0DeoGQz+fc8A1z`DVlYu7SA{*E z#0k1|4V}aul$##9tvJCbh}dZLHnvB2fg8D|k+*Vpt`r36!)D$SJti$>8h(}c7xM= zUBWvG<^D8uV#-V2jdsk!$Hnu+R7JTAe6AL`G2D~qkH~0Veifz6BU3R6{%b2XlX&WC zq+58?QEbm)mx^dS32P1|R{!V5t5ICoKxGpfeGamhz|GC6c5Z>2xhZ)4S_Ad$_#^}# zXS%EAe^i+s6|&4B_cNn#XF05APm-Hm#1t-f1wK_Ne2Gn)jm1sK#>0gWA|0LJiP!;m zXHS)`sc#$-VhN_5rMBeWN`FiZOTJ)w3uh>FEgo-Yey4D0u$@9y55=K1?9+duS#@aG zajjN#N0UM^Bp=XqbHZW!+clh420`fSIV?^iT=_As3p-LO-h%AQ_* zMR!XGPX#ySa%iYAcuZy-M)@%mANZX|Z9zMyRmb>?n}b0?N?}JqDm+$D;f0?ud?*bHrsv-86rQ@N zm45QF-u-fxD{H=AY&&>Qx%OslWtGa?)o}UhqP6_|=JodE)Bonq;PdMfQRmta##8Vd zukkl^cp)Q^O;k#~@1cc%%4E&|P}kHddG^Q5yh)lnHvfi~X}F!plJ#cgzf4g2v9*)^ z%kRsFWI{C>uZQKoicSx9(5fg)8>j7ZG*DT(u0W!Z@o~wT4 zgf(Plsg!MAx|GCFmd2bv+80R*1|GIuLTdjm7hL#5mvOS_bYf-=?z@NW_DNEm;DB~Gf=zM-(=(I$DpU)leW zmS^?|&ib3C^csFm*R7(8GrJ^7tXW=DUffkZeVQC>-^``tVvr6aKl)7tW8?Q6_+=GhPMG@94wt40d! z13_uchr%$Q9U8c+7cctbEe|`@ino4Mb7+GogL)%r)y^DMMYOdKDRTm$BcFZfRg`oH z?CYx~tKC(NtEeG05B%F=$$7Toz;kxEw(O;g#MsJU5ya@-QQ2#PlA1zfOnj3{;ZS!i zbV3g;TMEQT%Opx+e<{DdmRHuXR}^Ix{?+)!UforOJsEy&WlGJfglRcM3@r$0zMW+@ZM)&oBgA18q4OOWn&xZhhe{~B z(CW$!lxQ=gsXLMz*dt^@3Z_RULI>vT`D?l3(=yhQ!EuEP!zCYjw+ zx5dqNZI7-d2e)P?DZM2E7#7?OtIaoKDt@@u+R1QL5N+-#nVDGJ%A|;Qx`2PHlRw%G zlSYF`v?%)fk<&|60+ z#f2uVRlRGQzbq@iCAbfGsfv*;#=D0^6({sy#KU3Hs331mTNyDv6EDSU=9<)W) zYDnjJDYv~-Bq{CA14A|~Nyo8G{6p@6nVbdb@}Et&F(fVYKhRSJ)!IFOgrIXz0;lqJ z4dq-HiPIcZ1+OdpP^^mB(aWriw+o!Kw=?_#Ts-qrr41^J(B&R4CD}5mjM)B~_VPCT zmCY_f$Pb6Z&O{VkH_tk|eRcC!p?s{yZJHv!QqK}k-p(-#8euZcGou-9-p)6RlOkxG z>VsE$UBMWG9y$&0%qJQ(hfa(6dMA8cpq5#8J2XK@Brd3iQl~+e1|FLoQiws;9)UHc z2HZw7*Y0O9Zar21lQ`^x9h-=qu2EV@bg#`UNbodu!GFy$%4+)M*tpy5up>e6qxjWk zza>Y6N!d&$jz^*z`cTAFfW3i${Ct2-E0QSys*O9g@+~u$7~5f}i^E1dzBI7bEluoV zs1^6(0CSZfyXTZ1rMjPYROHw1mJ6p(`NL_{bO*eZQ;z!2&yB;vF7D9B25P(ZX^b+1 z7{H}@`N8P@pBDC?*d{QsA1VRA{AC=zE|Lf``!jg=boupnoqwwq{lickdg1(si~^l0 zm5*So;kQ?c8D@lJ{Sz}ujW+nT`E=eF9kdRmWYO_-nowq)yWb3F+1Zo1aDN$vWJpd{ z*&L!vXNk`iNliG&bngf}MF>0xJI7)gsDKN#VhuBT!DSxzeF!_vueCwT?j8Uhoy3lW;e zM<#cI%6_NME+o-h@KwOg&!5oYtI7;;_O+dG#HRL1iZf}FbZwDNb0gWAP@k4XJ>Hs? zZmTVvb7PRTLSd7%Vws_D==aY~_{~Rc<6BIirpTYMSb2cxs#1JAjn>1hkXA@^FycA= z8?qG(<1B@aN)8HTs+fr>--gy*3>vs=srz4py~pp(qO_OUDfeOUIeZq@%7O56i>L=v?J+y* zf27p^*nXyUXdEQ zYl-`)zHH7@dP{8cW$qRl)~EvNOG6t*#IHYh5anLOvkkXmZs*2T@>S`~-(;8xO#Q*< zfT3){so>i{X+&Pe{x9d2M4^yb2NHsR91{{>!sBx~GYnVdw?|j)rR(G7R7`%}N-=C6 zYh$sAC@s<({HCGX;b!_q+<#%OS)ZHw>K#^;r|bS@UECcv?_qoq+-SyzoSJJUCGd>$ zP=@zW5bAvzMc_IOIEb*@Gmo+@!`f&LXF(Xq^$U50f#DJ`LHHyn^@+K&Cp>?Ua|qH( zmy5l#kL~+2jrs3;1|QM^8x=4}-+#dvzmH#NyDzxIOt?*8@!v}3^Df$U_@8Jr*tTJ~ zV9zqGG-p5QEE2gTaG&=cvxoi~uLJ8kCO+Xmk0`Zi3~Y|;$@!@X+IP*nV5=63!S;Xl zaa4EilItbvv3opPtd-l&vkykLRdVk*%(I))a?s6dU-$2#ljwR;x=IkBPrt&;rI_;z z7{vM=5^JJR(lv!}86ce{S_Tnr1Q0+or|-dX39T?01n&iXXp6w3;5+HKS?t!j8!6AS z6&0HGG>6DIZ(Y2Rb(N65g~Gz zqTRVRgH(IjQqRfCL*HJvZB}J>i2#}E`mb@f#Y=jRMU$&;r*nG!-O}L8Nyn`@@+=oD z>iN>kCx=S@US2WaKguoizM>?Ti*{SA@3L~VCnfdv$R_Cm7sG0+`1h(H`+riHqMmEc zcmL3lE>q_0(aUTtHDNAEFC;Qy{RDobDd&I0;|5NMQwUvb9qGs9gA882Z04S0?b}gN z;_I8|DDP)cB^U|%9{Y+sld$y03OF(?*rwd9frXRT~o zB~mcfh?rk~x_TYr?^%*hir*OGc6-{cz3APRz$|!Pg!GLk{iI6O~U z`zi0eKDQwPKeJtivaubFDe5>!w_)Ug$hD{%b7?vWp{r*+Fnq%XHrnsreN=o^q`uAp z(cYQZ%i}czcm6oOA!uDq(BjQ>t}bxDkiTnr`e@o-`106sdjoro()`M#=5NCZuB#Ba zTdv)G6iqGp;NSpWmHhYIo$RTX7)GT zf;pGvQnjw0&gR$L4UPiuZnhHd^d_WIpV{T0SwV8aRa&RYb(?Qlk9|u>HxkdwF^_6A zO~f~B5VgKH0e78_qTTLG*ita0`Y+@kbBf6@MCI0)HWH-#EUq^aaKTg3rz?r_lo`Q` z%WD#|YMJ2_lsTy3@DNj#IQr~{4Ly@4)}S&j1N=%_Vi38E8Sng(3qEiG_x@%pQ_T{N zn1B`I&wHm_dK*6#dG14IQSX_fPKIXqDtFGto~bMySi@wbU`WZ>ai{D2ix}pau-H+d=FJ}IV^Oz0z5dWIhwQ24%v0nWametjhWwm-9 zb@G6>HV@QE1fa1&g4u8#*vxbLWUrqGQs)x?=2cBkMOuj2sDkm_or#${wN5EqU7gG$ z8pU1qMY3AiTL!}`+2rxSt2R)bltSuoHe-`ioL4OHzB$LJHV8%n|8#rb8O;H_&sew5 z;Ak>Ptw08ks(KUe(HF_)X`*G&e|5&Zd6mWq|bZmzwz?1@)LOCjo1>=f7axZr6W<@IN3z5qvql^X}Hn z!;acl9J?L=aGFft%!MBmZD>n2LA7zOnzwP&|8jL#M)@}*QnYxGNib0C;)au*q~Lk5 zR}eh|i#SlMH%-d-66WHU;7#ue6tqm_GlUOCMvuzWFG{x3MN{c#BdWa%^{CnL#ry}^DKoc+)SM?VS@tu!DvIcT>R>aR z$@ZVk&ouWGc&(PXPBK1zE7m#hJ7jCB6_`Be!l^G;`f&!Xqd6y?EwY?PbRNkvU7=%6DeH z?uM86jL#gXuIo8qdr{`iENUz2vxZ;PTTLiQk>`|SnmH(g z%dvBs>Fc!aG!N%r=rm8~7HB_UCI}8v13NFGJU+_G*W9)D`2Ag%>s?aX#2OT!*t`uV}8Ngh)8`l|6?c*mO)I3&-(4b^fJQw?Z|Xt>?8v9tv-{vNP)_PZ!@wA zR{=L9yM}Wi2X~^b*eqwvb@3xBYt$#bao73Vg@}*M41JGS1Bf2^20=Nm5eR^fFyLJO zrZMT;$a7JjlEjyPHL)#fc*w|z;oHf0=x002B>RxMjudSOM%}x(HXXCXUnKGO6yIJ{ zP{ij!xQ>MX)EgQc(Q0QM)e=xbq1k>oU|oyaNxHf`CO_la!(#igm=MF7UUc!wU>OJ9 zWnSbYq)hA7OH~0S7yj(fHMC-Ewv;td9ltrR!Ka)cb z{Pu67mh6lvEo8RZyviJ2tR7xft_!ZNyI2R^sid#S87+NDl<7~Y6I7C}h=jhZchE6j zyQo?vo@;4tE3i?l>vx8eqsk^VtTW$tP3MxEsE8@Li|J=*dv>m#a*ErW&PvXP0QkpWGDpJU^KtJrA-zO*1 z@NsJD(q8<%=cYFVou7iw=n$al!g_$m7Q?=j7ejQ#L3U_J{JyHwjeih0lH)Bg?f3;8 zQ0@?1MHaar6SRSIo!$B|bR$n4ZvpT- z2Eo}T^KauA37#ZP8O-)p+~J1_w+fi)N9h++K?-Prow1ZfkK?CXnY#&r{j=9aj0c%Z z)D24wbQ7QbisCv2nvhSgnB9weqhC_JqzIe~F~^9}UD)S!?MCOdO*I!*%C#0&zS*tJ zubg7e8XFeuMk+n+%GpD=x`lt%88dzrAH*&69pkmCNOMt3I@P(h^o~vA>}^s_(@RwT zX@dJScIH0eEy)^F=MI*xkUGbi&H2^kO6&to8sme1822zAj|7w5I9Zj;E?df4Dy#Am zXL0{JuLD+W*M2Cwv-8-&?Pt@`#gbJn)9SojhepYrJwA%2#9h8ZqC8?q+wr>TeQwy} zjqEEWqS|y)nf{D_5ONO326q|Jg{s95KOzVc9mc1o_rE59jX(Ga&Ldr^|BxE;&^ojN zr;o%$GD{^#GKkuqF_vtEpnv#+pm~h8NJ1+-!uoR=oc_tP2~ty+ z&A(~K$ve@Y2i($4B>TF_)6BhKhx8~dNf~ot?otXYMYHqLaCt>#gK$s&CVzItM0vSI z#!tZsrpC(JW<$;X$nX^TVo4i#-(p1ul*64XuV2e$rqAiG=^lsZp(WP4W}B1Lr=L0= zyIIKAetTJ0Rbmdn%M8B#I)2rplpNC}C$qFtL0KA2{Ek%?G2`t&CFoFdggU+#&5zA^F2$4YvX+W_4Fp<<^>#^sv#(Fuih8^wpWTg@8Z~VEYgKc8%I*U@z zZMWkr{p?EM@~+Rh+bvrKqPxB7m~oln-te~Z@yqBMM;(cY%@I?g#Ze8;WaHPlwMMz0 zrE861LfR>jV9UoEuFI@2CCj&Q>w!mb)hBKmUzT-aX$a8w>N_fcg=IDP@)~xG`9BZj zemebSZjIH5ZG|*b#=ighjRdr{vuax9)e(|K_f))_+fKuX|EuJ(R|iv7w|PQsCr|>T+p{}x5V+(a*$S(D zZz49?bskZFpwusKK>S!F@1b)Z^UI5;eZ7zdm(IMt^wT2i+^>m8zhVL&k+QqO?P8QJ2g3t-3+GNk`CqQf_$VtYZdyxlJ& zH-c4Fi&Nb!WAASVTr&uv7zNW_F{K>dLUnV+JE3Dog4x&_yoabH?*r3*j*$9}E`ZqG zpV4IDItzx~Wh}n3h5kqzyQ>1J?Kz!2)q3yuCcph%MRBaV`nTUgoK&rr4%&TGhf$DQ z4_fH=WXFa4X!6Y4y`ybH4=BH6z7itQaTskD{qLRRJ((+UwH;Dz+aI8F1%e5^(^%O? z2aCv`j(NA2U*kwI!rddRePwj(KDu43#G!kCq~opt!-8OF>CO%h&_T2v+WXXWdL7kv zR({jAF+SwH*{lro-{Kape0$xhPloH>@P^%1W(UjoX@S|d`!cXII@*#COatI+4+WMd zXMAR`!#;bO0y3_c6 zP^UCh$aqiNaxLcb+;UxV?{v_Hm7kP#e&`s6&$MlHu;}n}SzSgDSn;^_fh7k*{5-97 z#}ykfQROjNg!q?YtOGtsx194)lqG;`1P1Avj#k-1qBLp5Ii*_eDiw^uL))2VBUJ6u>kJ>rSRIo%Wvb|GuI`2}$saL{-ICu@EZw4oMs~cMMHvV$ zkqMKdUGp6tN2z~?*v1f@oi<>jjYE&ZS0(-o7p@J_M~@=C}`d5ytX7f zsBV~?!Fy;q1QwiEm8{HGr4X#_R+w?Vv+fXavEVj zc?ds_c8nLl#J68TFB&-y-&S0e$WDwmnIZ2UyZjr!GYWCZYD}4Ypk9}vi%aHZTrz2f z|DJS)a$ZS;yXx}yA=6IH->zF{?Doc`!L4Rj=kw2CH7TpJB{Q|gwnro5Hj8lja?79t z9bSm9V>;2PhpP>P0Yok*SJBsPw;q|U$=c_wMUXr1)!XMWt2LirOM(Rc$eA#HbZjQN)V%cwXLj_kPZ~ zpL6ax_k4eQ5z=Z*D7iH zI;u7j-b3doXw8>{X*?iDeXr86@%xNAsM=1=a=K0LX8r6R^ccT?6*q_4NLs@ybFPLA z;qmya*Pf9fIliC5n>Svy*rf&IWp*k(iAJI;SoNTr)JXUMds%h4s&lpqUm9|{_XLOi zrqpsSJ?bh*xp-Nlav!hd9I=I0V0#pCQuc)bpZz%7lGFl{b-?i5(%d-JYUJCM!}Ooj z5lgtMYqc$>c&VK%pi((6=GAmep;cuQ&YW>%in8Uahu*Il2s zKKZjf<5FqI`WxFvc5$cNk^)&wkpJTl*S&i0^(|x7*nyW=nj#nmZD?jNv-p#mr zo2cYyGM4{ig}q;CBzD{@n-Uc?qBd6IrOcUW_uhr0VPM`X<(Mtqh_~o~pS)&nZGp2! zm9c%Var624PV)x!G*|kF%Sdtxb)glbvab8c){K*vnU1`i|BPNBqG$egeV)B~;rt$> zcva)j`uq%Z)oa0+z1l%+)OKNQ57EdFb_FR=xPqXJbwG#7WHqpn(5n@wAUx* z$MrLs5hLO)?wzso0?U#snv_(l@j-0=_#zx%jrCir81^X{+pULlkn6;bx5 zVE;sU5ZBf`pxuV|Zrp3PmM_?IaN!E^O^?mhPYT%6(^g)V;=jOSZz|rVQ>1&?=WaH6 zdRX+>t^ReiEXRk|6U(zBOibqhocqi@G?ZC3YzzK(~ zkwyG~n`>Z14X$WA1RPQT3kWF-`L~1~rCr3QvL57Cc=`V(Mk*r})~8E z{u`n{e1_BXm@oj{<~(4?F8V#)G^}~B-?>GyJf`%aBSy3R-jX4>ql5tzp2l*^Cflj2 zD8#sYEr+P#;cbZH5lx* zJsu7nscYP_zs~(&SH0}*>sqzEqwZ?oxjc$El9hNw57}_Oo4j)A#TxIQFhbFF?j0C? z9rxNA+bQwyDBK-I1m5L!Mvi-7(NT3x)KEGV2fa$I4U($1+sytsz<0BWQ+$M;}v8{58tL@<}6x zbl8uzOdOn^85SZuunNqgQ?BVWivE#v%AK9_%GD@k-~ zN!~bG$E<2T*4&P|UZ%S#RgT+qjh79=Ug5Bw7N>DBjRl$Ks5~~Wa#02{Jr#+NNo!dj zimqRwUB84kuBctLZgt&Emwu1hh;r{|I3a`j%h#8+ULTKNABtF370&r%dr^rMPsYjb zaA%<|60fUiNGiohtW2%&CE81{%&SF82->ODDCz@ISt;yx{rlF*+t({x#6z7g0s-1J z*Jx`}q#G~PINiV4KH$8w^xZOLJj>uEdOFC-g4C*!+)7%IL4yi%)Zl-Of6%W^!f{3` zchKtZvRuSkN%j`~g_ELgQ?M&vYvXF=a_^Fc zcvOx&?|g5W>&%)Y$gFk|*Xk74L zjaDo@OqT5WPLsbw|DK)neNHB7b<6ZhgH4+(m&@EIu)n2QC`OKj(Uw&}gW}GZS;0h% zr2;E7P#a1Rxa{wb!Aw%iZIb<{%q+Ot<%b#aX;MNi^ZaX_G%m=F4y|LHOBcnoB7y~{ zw_LhTJvU4WLtWX!2)Cdk1{)p98>lB-6qYfK_cpEu#zuz8?XNgwimmW82i|nM8 zPFDDT@lJ*M(iO9duOam;NVgn+-~WT^UJOudJ`X6n*!R8Q&AR{ktbegLny|A+X1_$4 zb0XDD@iAS^O%>{z<_G9sxRqO!Zyu>f!Oz<;pp?dlww_$hNVW z$LBIZ3)V4ArHidJolGTdilln^S?8(gQUA84WBxGINa{tIx$tVNo0nwG`JKOSIdLeAB!{7UBPTOr^FS<`BX(%7 z>v_1}Eu~rsQ2>dkT6qd=SREI2US!@l^%)+MVKnF+y*`^}*1t|_{(K|YYWYCm^5(5^ z)T&*7{0kV9g*0i6K#5g<@dU5QAfnoX=bOtjEr*Z(M4gPi2r)ZXBVF^#j5wtn+MK9& zBDIp(NQ?NR5>Q@i&AN40(@5V_F6s{Cvd;f{ z{cx>#JOY(l{7W-xMg9$mP&&C@5kmFblYshXFGZnmMJ*2KHmw4EWwo!gj=E4Wbqty3 z%Z_tT^n1n0An%|0V4cvXGM8c0$jvH$nroPm?4>Fj(yh(Epp$=VV1gR5P_x*J^_}pb zcOapKl`O8G%%pwVpT5Q6V});=HonfcXL9-GaMC?McR0YN8MB`{czNgq^H5iEr-BBR z`SV{)=6KjVDirQ|g2#>?U+e4@(nBUcFUq=US%RZw$f|6a7&Oq`X;aTUO z60`;6s>5RQ7_qCX{n}ra)ai30OyCY1EbqirsANW**+t~znzu+@*t5>vOMI`S z&o)`17ghe+u)W%pTZ$+WmIh?08`&4zOuOZl-|7}_EYNQ@t*sF?*B=IX5P~&}eeRAI zwIp62?OM4V&#YC<`Wr~z0K`vDhZ(NQx#S29ZipY_oL%FpFD{h3{(Ty}#3VZb|D$9e z6Lqw}FWN-ccTyvXLS28%6<1%73Iz=oauyo)sx-04H5s*Ocjwa*Ku3bD$52k$* zG|`YNabo(4;7-ac=h({cyA*V1<$$y$64IJ6YaY$;>I`UqWzow1ZY*Kj_?)zobN1VdjzunLgUG{Yl|Yb(Xn6 z45)2?RGUkc`|^p;EKR^wjN=p2U32^e{|GVV>xl+*gmfb<%4LK)A7C8QI5H^x_hL!X zHVZ{s89sp3jVWpOXYXrnd{X=PHm#C)vnRgep%S!#Lar?)`@LoUYZfnhk{KQ=e8p0l z%eoTLLfUB|Gwn3rfiKzb5XfJpSz2eB(7{t=Bb2Tgn!sdS&E#1$Rj1 z^Ll@29#a~5wun+u-yOW1n`|v5Y)N*x=JU_d1SmcdfdC$q_&0|Goyrh zwECM~`M7a9KO~2KzKP!UDi6C&e&2`w*4*&vS?e_F}C3nxLyA=v80LVaZu zu_P&Sz2NckNJ_bfJ34`E&`cioO3JsTS?U)BULHl?&13ed2GLVmIbWV#*mRu+3|=+{ zUU!ytyd-(7BpEWaw}?)@io7E?UAEY298IXVNE{`8r5y#g>KI`=tHB za72}iBiypp*R(z1P2^_=Ir-0v(P!gAo#5yha=nQ`^ku~m>m86RnVzh~iT3sH8f%X( z&9M(-_>^sTkI}wFaf_!ur`Fe^bg$0LTOxH@$M~;PSuB!$9^fqThn)xP4dwBJF(tf% z=(P}r2?Vp=rea9vzWeo`M2m6yu2Yvm-lCk6+f#oKwDivgeJwUNho)Y^OX*KX-8sdm2(!R897F zmh{Jtr$;l{kA<5^)n9}z=Cd0mOyc7P{(XvnZF+fpD;8gae^yaj!Lm_iuqCGe;qYc+-?%&MMA6c6zh14wzFz+ zznQO`np}^M{H=zL%b$2R%YNMLdFt`pd{T}A@cT`1=z;OZ(g~iylh>$0-mU@VJ3zNf zlEWW2WGWsK;iufkRI(jYBD%p>c(Le&DVCL+?zB59Xbt}gLT|40{}b%`XEcc2N2?!W zx;R|hUY)R?T#)S={l3=~TJ6uSFwXRV`lojqo2nh1{+xse2XRqKUps#$hPY zyx(m4rB~NmC-}* zKexjgEFN<4eKewC{Zq`eujEOISM3=ER~$U|>HH;{pB}^UxQOg=ktJSsbMpx+t3PS4 z71YVCmQBL4!zLXSUUSeVTT)&C0TQl2pQVwJcGsXux6tyQUTO9dc7Y8OCH-iq004A@lH;NNI*Z%vZ%dNS=rx}4d zyp|8z^;5RSMrbbYwMLFLhGhsFzY`WTV9pmdc=y@h;UnhBcwyuB!ULMd58nw3drq|H zzY(S~c&}*SZ}d*|hG1r3TYY%0v7p_l{U@MF)!k{vF`{~FE~oL=uCCm5F3!>@+xUx~ zTla5!l8Q)n1{?O{&}=KIxlwS|jIuQQ%&oSMDBN1QY74%ENIoHT^ z3io)1>5$ra34#&gzLIc>+ksw*V*U;^-fsz|0Cwm$j0D9Q2$`=zD+~~HX~HdkFE53) z5bDJ(gL6gG$e(JMzua7}#Jm03tsJ+|xkm~cQ=EVuQCqL0ZbtA@Y`p++Yz6xnl+1(# zy~eF-JBHJ6fLJcB@>OdCZhbPS2iI~coB(WlujG9H!~N&xJ{f-obG~|jx$xDBqWsCD zyBY7%x2kq>;7jftRfJ2jt1XE(z{iW6)E%Zexu9+2VlV$Z=ce@IR)Enc0;)`-u>znum_L-I9mz*CNJxLU~o$P~pY5IX~H%8G(p>s(e;*gblprI{S*DyGyK|3F5L&qO>-!kG zL#$1E?`QMH!OK`-3zFAFCTP^ae`xS>?atCpCU6zV$w)bL$zNm}Y0JEdN z{QK7;6yjR$@xC^|lPZ=Pg488s{OT||o3%ndO7qyWdr|$R&;tYl%X};NdAE%O;Bc2X zZz%#&?`DOV3C}Ao`Ld0(N`n{{2`fN>dXBv3h)SzbhrzT|#2`GaU|-a@R)uFXm3h~J zCf!V64hex_q7y&p`t|j9{aypRcaYw_IMsn%B;nR?_v1#HgP_Ub2=lV&2l)xDazMrr z)2l$C)E!BAeUok;viq2cZw6CK9?}p+yQvqZ4g)?XA2EPS;<~2AAVC}w-hi8Ti>rl- z6hKR)t3C?>7r_MQRzHQ;8yJN!IbbdojmN8JV$S#2=&aW#M!)}Q31sA|`TI&eXE+k# zUxzEWDQ}UL8~<%zoAYa!@}Sq1!YGI6mw&!e%+=Oy82Y)&8s@=!E}+(W$^SXJ0*0K@ z!9=MvwY}A~p@I9bqF1X>cd~xdIs5tNc-K>TUU14qr{Ty?97d)Yw>Ra#;-taaPhofK zoioA^=F9DHdlh*GfQ`7?-|t=tTEjaehic{-=UoriU#{Y1o}|+IqE0C3J*a^;!fL=B z`T6~IJ;*nE0#181a&-71K1sNGh`66NB1LyMaHQB zdy;L45$O*L-)SMw(maOj>K#esxici#HOt1%=t11WG|5r_ewyVI|2=@*Hw_#}_57Cp z$QbfD4JUR|%S{^iz#UUlmOgPQM5g;R@R1)U?;}?>;5RG6(zmR%-?xyVwuG6$vIHKZ z5J-8M$B9_*E^dY;+ZfLGHm@wrsDIpXsI$SK(7P1>@D2O4?#tF3%hfYHQhucx0h^TYM zI#Wq{qvy$+!7>BRBTTtBtX$1i8MVeoFv#Z&+?8{!nKWY3;Vvid+$OjFvz+eVA_y_Z zy<^^l>^BKsNj_3n=tN4R+1I5zR6s08wvP;P@Edo^X(c@})Ey(}%lf1#`MpeWyiAyv zRnW0J8&iWvZ!=BjE&0j+@&vx%pmw>UmVE5)x~u&-&>=z%%UoB=AgYQ zjmOT3mC#jlgsDvs5%Gu(=x<2)-W%Xy_^uV{DvSAAcgRQn?TJ%E`DJ-TOfXK|*3Wux z?0)Msv|OZ(kT0h7EEVOvdWm>nu-KD!&bGdb$N?^F|L0L>BwcELeF1+U#$n1W9_}1x zUHe&ZAfYrO`RZ%~d-lNXrV$O^u}{;x*f3+J$7Jo-Zc{wp?#Oeu6$pY zHYMXd!wdGh$7ydrq{(DL!)ss{Rs5%WH)vMfpBTnXB{pM~Rv)H*JBnTj%#{-wot&le z23!EAH{$&qairJPPKFt;3ZA0U=HEmD++2H^3iDxeu^0FBxC7}(i z`((uUncc*G7@T>Z&L{W9!;E?}dH~(7bHONB~*kD5_a9hB+ z#>pT*qC6%k_F?wa&ynj~m^ZE3C#&Piy$@>m0|N(g0w+h=2lg(k|Y|MmD0&zPLRh*$O zd4Wv^fs4&!_!8!ZH+>7q$0z}8%Hci!Y+E4FzOj-0*3YVkZ?qPi?AZyf14 z9CcNz|EUIXW!Z|p4^&H8%e_UD)bo26x}S!SWplXZp;V)~eTB-QJNyGrD-W;N!Yn?{ z-Fnn3ncHj?_-`;vOaB7{O)4Xr@I#Df#z*HIWsd1Jy%?ZiAAUn|2&#_?jWLpJWysbL zpI;w5OqQRBb0A~6JUn2&34}KW%)(V^cL?&PvUmT!$xSN4OYXHNdH5BCt)DoaC~z`L zXHwG8v5!>Q?<*g9@D48s6L(fk^XpsYp36`DDdBbb6-WvBR*(BtJiplgo98zj2H)$L zr~jH>w1w@maVQ5w;mEdHi8Bpa3OFAMaxS@zQ1$Jw(^1cePpC}pIGP$7P2cKb<^`#s z=RQz#%Z8j~5K3GU2khT{avtzQILeKc%x4NX}>Gg(i<8v=@)QE)2aK znif{O+9_-}!_o9Uzzpfj|`x1(j=;v1|8#rFKrPLj)9=7#~3g zI5RuC-jyQPUtrYrXeHf6${d)%uz!aYLy&=2Ig4gH?w$Vs-R_W5Rf*2(ZDsX-e*~O+ z{EZ{qUXXZNpknznVzvjFA-{O57l3i6o=~KXBF_naYWEnOji2(pby$#WrFkSw*SSo! z8N%jpxdjuR*oT2I2zY*NwZZeB31MguoEtfN1qjf+D(8a%Eq;_T_`u*hFgn6K%##kv1%%7=LlF%<3TJTEs06(An<;4Ltx zRFI1Xk4`}cm#Gh_NQ+in9WH?4fg88HIyjp12}lBT-h&_qFSoo(?{}Js>GOi8Erh2@ z)x`CwB-5)hjtBjq?D`!CmOVd8WCGTNRGq)>`pu^WKYDx2LFy*F zM9~6HI(QljTD}slZXglU_ajNBMJZ8Ff@ACH&~{Q>j+$r~BY=kqy*aCyH5KcP{BDBj z8^iml&tkXPz`h;C2<7!YXUA+DohExmZl7r{^If&`nzVZHTw$Ptal?_OooY;=!h{y) zz|zaWvuD?N)dT^P8AHbw(sm>Ko^*QYn^-F@hybS2z?J-N#k23HQkDW7_WEOHJjB{s zMjKYAwuMVlJw~~i!XId}Z=Ntu>sof!W!wjT5D6bzIa6HPZ$+MxVZ3Yi%Jv=wCW!ch z4^1^;xp8(YPmMhfiqfPjGAD6do@zT7rIe`qrB{s>X<^9mhnO4vR2PCGkP(A!zIxPX zR23s)mF(7MSOF1_aej+y9i!Li!}G?SiGjwP{!PfT1}j0#j1)sK>j4npYl`QH?Zwtw zGiZDP6J{>iP4$671FJ*iXNtN{+I=F1(`!SuCFG_YK+fNi+*C$@6nZJUM~LFDG)78{ z*jfe+GS?ZwdIQ2nY_jb8QPa4D`)%VL3I;mT8iN}vQzgqPqL+_=A)GUW^zM7ju&LhX zhZ8;l*0NjWV&iX;eNtqEt5WC^`}C6e_yBE1{*J5fFx!LD(N-jR*q?vdH6x=_=?LV~ zP)DznAdTUIyYqwzAlKD@tyQ1YD!*!mS%y*q+pP&Bah>nt!3*68Uhwje%qx8x1?S{e z#2l)U|C6c>wUG?FcPIW+dW;E9lFMr$Jd>gKC6`;%49BL@Rw72u_XDn3+RD!lN=4g; zzz?74!fzBGm;+XFcN zr^7Gpwrm{O2;|Gv^3FsA%bATAo&O^Fk)yp&&NmO&xh4B@=r=Vn4CCv<7V^Z%5E_-| zk;Og^IM;X93WJH$(>zbMLhVt`=Y0K7T5ArL8aJ z>K!sMef>#V|GI>k(d+gQ|JS$@8uppHM#TZ5Cv{<7P`)P_Av~Lftgy%@#$01&DhHkz zK_?BZyjV3oE$Q$Z5tx%#W`dBrGPak{pl_|;)gZA=4S(8&UV8BWRSgLE<>twbvi$C+ z(qI~{qn9JkA|PMiMYQ(|+Ep{xO0F zpf16cNANWTz@c?>h#c+EW8ErMDkDo4&9hMMV@WJuHeQ$rI;K$X+zfZUR5Y8FiKl^UQ@+!q`HucgtEG@QAAKrwWLH~CMp?UKX^Q%V> z&r`phx48J`DTfu@B=Xx<+{Ju&j;x$$3@n0?7z)G`#bT2_P*- z(9n|=BksH8RJo(-9UKkOr(+vAPp5jFlb;||{-Uzq7rnS+q{Qa^?`$hh)mR^jSwlF{ zFaA_w_9B5R0eFL7_A6R7W;}RII6mv+JzXHlHiWxnP05hneUxHib4-~vRcop!Kpem~ zXCiEAX{24;QC&UQGiPd`y|C@>z?N*8hm>>4Cxi}q)2Dca75E%cp}G)U2O-b?Djj~^ z`P>pX8eh7ZvLsKqRp<9v8aUxdND$lW$Y)t%0T9@{l~*FvTA@H-uE4yD@rPcz;GS3* zBDx{;$UZ%{5N{2wYm;&LI_jt(3n#DMoJjdY>iLiT=-w@(A7J&%UvGmK7@$$M4fxBW zH~xWZ&Lt8S@w~`!#~aa`p$}Ed-gJK0MAM!g$$TfYvw3g}Ex<;|cHZTs1RefiZa>vy z#RL)CB>Wy=r|X5-I1O^XkU&-yd>%*17kOUg&z0;4Lw8T+C>k;y8FFX!38|gdA4Hpg zYVU~?@1&pumWYJ++?Hep#G}3N!ogRMQ=u7dh={NA8FPA~-FGL!*Jl^oe0)GhNp#)ug}h*T4-nS zFj1x!uzL@}hrNLTsQiEsJ+jkr#}yz@4-qp^xJ-$J*y7~ z5T_~+f*q&{B2JQHu0e)(6)5uV>TD{~KHPQs;q$NeN@4qG#Iz^!&*RT`03{FgO?nX* z{(*~mz!`vaB3Q};EY|hgJ}P-1c`GO6n*?y~Vc?j`YxR=y+wdu7@9UNAW(dzM_&U7f z{Mh7M+D8^3PO{D8ud91de~U${TFH|p?#ah$78q;&B~$4~vvH-|DSWw2(Y$>D%$t?C zQzKJ9n|Q94sbjUBveZsL$(5LTyna>yEUxyei5S4^yNqCw7rjSlA@SD zL;*Nrx)GoH?H`iG)yX|(y!_;@b@pYJX7h+0w%2=LO;xMh6WP`eA~E_5R$)sMm^5YU zeJTYytZlyw>ZXkqIRu}4_zAVRJHWC&H$#%gn)$(4BdZY*k>H|A4p5 z0y2B)-k03y!rSmMMws~iKS@bfSqP#N_w_tlNa-H!=GM1;OJYyR*qd{vPWt+zvCon)B^K zt@;g0bFsL^U2CRd5wWg!lOP6JbXC&bs#g-RK|>6)L9YAvfk96LeV1mQKiCwI*9ZA_ ztXVTNcS;ZY$`UQxFxpcmFyv1i%z>Bk2vniSD7khly)(V=QIS7q)=}peUpc9(i9mpD z;KV@wcLWD@?;{MjF62(hz1wg05o^tREd~B?l>HiU`rw7)*6KF$-($?R@gkAlZkGfe z2pG}CIMdikzXlJ1&I?}{uQ2bB!GqXSd6d4=H1#6w0)0K`w}uu-506u3IQ$PJ(%fZ+ zodohDOi_svSYx^34soVLk#8WCk+4E?Bg?qb7s*%OelK4?9Z>6;Z~*B?X{8=(G*AHM zMZf@6x$SEA}xrc=QvJr zldkyad-6`MHXoN*QRY84W*`^chKp!4;z`Ecp$TFt#H#mgu-cKq+2DMTS1}JH^WsG+ zlYLZov3|+>9Ean zi)IUcrLMUXS(BHIltXblGSK(}yfJ^DHE&pv(ELbhx-Xw)ruYTfA~wHr>Tc0#MtZ}) zZ3#;>@bBS;u8tx!eIz&xNtI&2TyXqyXBrtbu{Tx+kuG~Nfo+#5!90V56>SmkwOcnu zmGAKb?0{{jqjnyN%)8FXCWwe14vmR@sYp<($GH+E`&=xhQdk;K)|xN(yWRHEcxJ;BFH@PMG9b??A)O7-2`k zv@d{ZD#4`Z;rAXXpR)usA3gjnGu_{rEs4qw#VdsvQS1~hHU=H zLa%*_qh0Q-W5cNW621I-jht)_wZWGVtA`xqRn-1M{XfpcVPTACJ%f6@;=5mF ziecDB(8|@b+|I`hN9f9@e=h-$&;nc#cgHJtlo+&R5>e6@Vt#rru1^;+JB)?=c#gge zaFT5ccv=?ydibep_D#URVl6CR#yOXH6f;~-{yn%M0)YaDYs(e^-`#RMvxJO#n5RJj zD46b#Li;;LGHkH5Uzh3iDlE`dPeev@@Un>liU%NF`n50?Y)n`b1B`x7sd`PZ19nf3 zjxAT^EKy~IZU1$judfE?I=weUkpXN(+I+*g^OJs(Le~HDFbUOMpuXK3TY{KHxBK1e z*HdOn3Vs}^Ebw+%)YaoIdQz@fZq(PH^8ulW5fJkED|PmLk*{p(;o$$~5^3k+oM<-P z>xUGe7}blVV8}C-LVF6JfpW11wUJfwjOXP&O%~hUaTCHnU)e7M^MI~)k4rs0RY;X z2QHVXWGMdt^gbH`&HC~^J~A{B^I?U#dJRPi)br+H5e~AP8ikT`OmAc|Z(YNanocgF z=eA$VttaE#h@A3lwXu%+gd%cNCPNBOh?y>7$av3B`iGJ8{xo7a$n()bhPc2>w!(2Y zIwi4b_G~V3qRw+?R*#q;h=t&#V6mHzlEXWCV15!Bx-W>6_=NKm{Li_O=RDNi5B)Gw ze=PnJNT}@YX#|zQ4##q;TC{;mj|g9zU$nyQz({Bl4*%Q>|Gex^&u==AwiGOtPpFPO z`1O3dzgI0Tk0XfB~BwWkAA zi-eDak>l=8@#NFt9 zC$y!=aJM^Cu|MPjRO{>xiE*)Le!JEPKYn!wkOrU8@EQ{UaCh~jf~BM+Tf*b;$d6?n zL#Ra7SMe}tGAY741)!>I!?HQeRIOBbvIne52;&BO{kV;?bMKRjPQckkj|$jRPbVgD zC7XOhq*i_9$u4KSG<796NL=RMWMGC7%{s*w9B$JXf|`l-13J$pp>>>y?kw65j(=&s~1fa0v^Uoaz_t zel`iNt$p&hYKja&j-ibD39k!yP5>c#XDvd0hEoA;fNfXjA50!tuczR{pYNSyhFNpK z8Kx_@n!a)OJ{tk6FGXQ=D;XDkU75G!4)dY)_`n=!W76vel28@|hWiFaJq`JIh) zwZmk={rR8yaRP1bhU4kRXxIPK@0y@MNG@`BDUg0;o?*uFnU%&MFRXsd_BTA4OL_dse@ zga;5cQ^FOK5mirY2NL=bw;QMB{r$HO1nETBncY%@+{?u4HMB23Pe;}cIcGe@Y=$^2 zr}QyHK0j{Q=0BW&)z~25%8vCPSoTq96< zjeQp4;?D`Rx=XbGT8ye~005T7+agcDaEqs=%GKHv4otNAcdWZF%n>e>XjE3x7kZ$5 zx|^p2u-%@JR-w=#4C11Izx_ffnF#)Q_NmbMiS4(K^uTg4+ZEa15$}Y)W_ZmucAd`T z7b)b=f4E})G@&s1=Xw;R>M5aZ&=kuuhDA&fz;<0lJN|i4-WJpAV@Fx4pV2<`CVhum*?ZHYA7~^C`OUoE%OAO%_3T71<3+82^nI zIZp22N3B}cEXp8VLonIf1z71?H)N<0hN+RkM>u`m;cO_g!glA~+56P~Wr%A(^jo)@Spw{T>iwPuTl~Pn-+Yg?y{< zXg+sG#bqcV*TXS0zTXEDp9(@GWw_1)6|tP(RKbXi>OUvX_ZeyXLDxr7L9^urJr@9k z%hAgTSf|+Vo>ch4zqkwUubDq|2-{xKzmXF#=r$>WC0t2ZoQ5|(;ZeP;#wD#-tqgEJ zrfq>4(7;xWwUD`GE2tm1Q>a-+n#cR|FI(Z&AGfcb-$Y9~aY+?m!4xDraLFmqxA#^_ zFe0|YN-eqeUDP-L(7=8PdnorV`lipNo^;H2=)enT?nbDCb)5i~2SW>< zKS0e|=gdK3zbI+wQ#!84Bl>^6E|mMrf<8%OvJlbc-kfEZ=5^4>HFTn);b-%{+{(qX zS{OkxIqv0>(rhN-qncD5^xLF?SVuxx?{oUEbP1*UDfCx^op9#+WJWyf=x1Z(IrS~v z*}r7lwg&~jZw+`N*J&}_FPxfFlFBuQcR-*i8o(dZ$w@i@UQli2M9GIelnU1DaY7Sa zdU9vJH`WBfPd8TL`iuzzl;9#nZ#?4%e0SCXs4ubsT8u;!kkBgJMZyMqy@Fk`6ZkrJ zez35JH&MhirKlskE1^L&&UwiUV|7xrtGt!=K0Z0hvFMN{)kQ(x^Y|5AUIPwhH;8ll zL`|cCF}*>mOKdf85AW$%{PiP(wIkZOHW&+O;4+MbXoOa3=nEiFNrx~QMb48X9$(s@ zvgGLa0L96ZKw*i%y2%s_`9vT=0^#`Alz8_#RUpPtu@)2jTfkR%Sc(;U(VvL_ZAb?x zZ^FG9buq|!nCe`ziAbft*3AXvF~EU<)Xw1iPOv5i7Psk~D*9^LG{%|B8pfMzZQ3wj z?4&t(Rh39l03P$5iK~&xV=zi%+-(q*FVHO@g9DPSoazn$QL5|!0yFygSjqH93;a}dz`lFt*723;j)`l=u2pg z?o@$%i_CNT2D~X~wqE(G^z>{X){Htk6R(#50H3kwWm+0vHdu9~-`k8CWw*PY z#(`&@E=eP@(z+%LFh)+qwR<0>=i%Xp+8-(Z>B?!#v^6b1VvqgE*;?w+a0UDR=<0s1 zwgDkAi<^OD1Y>BixBV}cL6?cPzTyQP)X~LcLjWS_6nGJ~S;N^7m!Ou?VXxZDYZ`$R zU__T~l%?8ekkXjIg9{1t1*z4seKRSWrcreO{UwSwc*M2${ZF^`wMYSZqVu?Uhg&*z zK1c`yMsO}2ziBp!w4lSJYrnjo;)+^`c!qL-GC)4(ENpn~5v{&_$`Kj6yqL$Cun(s2ofAQi1BJG+}tB!(6S zBR3sW0KDUR$P|>HJknr^VCnxGXu4$gUOp*`XcFt85y=XuVWz{sV!uKNIb$ATT;5YM zKk0vu3{}FEu$(l}yx-`0IP$BP4B|?w{qi-qiRpXLYcQ(*=q1F=De!kGY9K523-V)v z$MSrwWA|^KV^cz>w$CS6dRF%{FHjr}QJZQ?M-$_Ex}WFlDk`w{vY$EEdYB=?d5N^? zJ{=%WM_UDuohv{zI5!O@$&>#1lBMu+cDF}|dN@6)<|-^VVt9!huqR|Y^)ui@2V*vm zl&z{4b&hYj0&vPWaKnguF=&-O{xd@5ifGL#=E)6gG$(x6k=3Sd0NG(S)|NWnrPNWp z@kSI|t(#Y)8HeF;WgDxNm45=c#j;#>$YsrG966kU(Yxh0N9vPjGTqr8soZHb zB9zHRS66Ga2XE62hPDA)J4DdPxJZH??HajJz@qyqggT zagE1KC`D_6P8|3F-ru*q>1gJYk2x@KBD2LrcTz|84YA~bEP>sD81Aa_-;Dyj0cM&1 zpr_I-(|JV^lBG77YaWe|l+_h%h0 zUs4!tCdn*(D4`cU>3K&9S`Pg?C>B$~A<_1EwaW6r7qUEFL`0%YYkH7RLf8VhQd7Bu zU+fbHpo$SooREt5Dq%vJy~gqO?X~Pq`yqb`V|IQxGZow?G2&yUJ1<)-vDJrsh2wZj zm8>VjOcR|&XB}XWz75SglDJA{s7i(-&xLL^-+$OCVtYy~6>i=y zMV|U&5KZU2vz&K~){}o27N5-ZKS54;U=l`Nu#@IJb+v+bsQkTWwO$)@Rtf6NOZqc4 z6bT88$5~@%Q7Sm;79eP7`33_x!bb&XOL06{OEG=``}W;nhh26h@F+eE20l?<@Q*cm ztEbMW*%4iXs%)eOV1>i&;&Q#}DTnuj!uu7t=j$T+p%p9$Q)#J^5yR%VQuUN8zP5K= zz%a6G4e9XSzq}1H6-17j z^|QDf^a3V$C-4Ads8>|(EjY^hRu=N~Nn0)z@)p+Y-8J~M(+w&7I>Mh7sA@|%{&Sij z^q1sN9X@*MnHZ;k1kHGZh*nwzck&P2G=-CvxG%y#upnc=J{1R$^S2|4~1<(p>GAX?)o#XwAbfPPrkz=8Vn@> zYtYHgO#-I_I2^(lLdINyKxG}mL7%KY*Er*wFNjO-t>CiYqWCaL;`r}-suwqMCNCRs zL)MYVVitQcqwHj#T-IROpOn1_U2urjy&r{F-ft-EzPLoXpMj@m;XL&C6eV` z{k(x#NAQM|EAE+4&7ID-Pjp)?O^VC$2>gOBVj z)RTe`H5uanSi0(fD84Q#-QB%(mq$O7jD0>6UK3 z+2!+(!!qyQbI-l+&3kXgWkx$m(P>L>!YcZq_3cdq^*F+pASEyVQyJ}PJ9e&Sqq9W6 z&x_~H_&X(bb*z&#A>yL*Dv?ei2-cenHQ=-Uuf7ep1IhF?k+D+Ae2ES}km7vsX?#P{ zezP=mwmRnoNwxPa&QWU=vd!Dr@$Z1E1-a(b&q7(==oog}tXJfy;3+QV56eRT;~!7c zPgp(cRqR>&D<^fcA?+MXT%)1ZY!**ONO-9!lx(X9>6uG_ApJ8X$w{DN@0 z30#LARsW!F>XM-cz6C{e$c^@;Ol^`rNC;kZ83E@8XED#hAgnw<;6?RPsJ_gxjO zb0SRs&)uO56xG+Se=gBRDyjPuwkE+i5+XA*g*OKe|+jn4W7Wd||tc=$S6wiDuv zo1AZLp;`HMb8m!OCd2Zf7EN}wqjz(XT-Dz+Gpm6c-7}j}7f?Iaadu&Scg?ET5T}Uk zHZsbWrmwNMs?=(at3TGJnz4@5nUPQ&YU@WC%>o%IrP8y{SXh^Y&a>|Y zBq9uuPi^CD4b#_fT%#g<;sUQ#?ojk4Nl7z7H*(+8unbL5;wnkL-vWR7uG8vk;+r!| zomh5e8L0L}V>W34Q^jQ~iV?c*K0fK0`U3doH#-!c+sBTVt5=YY76Xec_H9Rg`1eFq zsGZSSb<{7G^w`4)dAw-aJ&FQzwfZP#u^8OFOW*}ZyPX4MEMCs%NUO`LJYF*|QAufe z{kBu)ttYF-9UYf0rS7UQ@1@U=KXTn6d=-^wZ;a3?mzDZ%cYUCbYvVmuWg!t+Cj zy5v_E6qT)LGs$BVp0fW~AWm`HHG7qyp@cU*Yv=L}zeHF6HMm2btDHTMCF6k>Tw-LWorXhDY=VnKdShG5b1x-$(61uK-~n!>&;1|`qSTcuLa0Ew(Hh!bf~s7=oA3X?O2q`BCUx8vN-3*P;FK{Nyhq-I+`&hR4S6=H=t4(`(+S~`&0 zh;2sUhLzX2mr)UDF=jI^{|(=H@IvxB><;7~xRZ;z2RGsH^ zs4uK0lqYt%$#_k7zI}ld=<{V+=WODuRLf~63SUsnHc4hA4TdN(79G*S=^kHmrZoqCZ>EAT=NeA9p{-zW@Ei(=dyXHPoc44t_Y1%)#yM~$(n zKD4g2>zw?C3^gArDa1HBvir4a)Jo_k7o@sio6&0NXefj(R6Zf*$LVmL+=m?=sF%;U z>(cUZHeQb?xsVo)L(g3^A{J#&=w^<=l2KIAC~FRuoCaOx<~SQ!mrM=&g$(3f*>=&e zQFxOXXOpeG&rY)jBtv6>jk5j9$ttbH9V6}{)ANxR9E43+{_awvaD~z0BjR|^IToen zlA=Y`-<&W?}1RU`^UWDi0#tQAV zb(#tpVfJ`vbr?v=c(@9W{NYXat3YkCWKxghod0|MSZ`Cs!!-duJE}JrU+69DT$9hK zOc1 zDg;VC`>uqOD~0pMHs1OZkq~(*ImbNkU{2v9J?J|7c-$fN8P#nSajFr|P1og{4lHZz z$fWIjn@5Q>oEiE-LbVE-mHiWHh;_T&5evb5Bt!sGmcL!@gX_)9*V4JRD6iGx^@8&U zlFBIAeeOkztGnpip1b>;LL#iI4U1=7@3RQhX)$bt>w7#_{>KIo}2-MPlV$dEP*S@ z{u?rlzl3U{jkhoUYK!X2g=(hQ_4SkG&u!X6OytrUduQ1vUA@P#S19<=5Nvfcd#9) z^Ex%ePO`jL1oH0FHl12KZ$Vyt_v=6Mbd6j4u6AffJrf6Vtm^C@Rn?B2gmva8I{2+T8hEKiPd-P?tJgHtue%zq`cXb4}mw z{O7!&ufRTH&YiB@u9WeG8cRhTU*g|uYvB}c+7V+mKlN*!+9vu05PraB-H}OA5+S5j zwbpB&gy(qpIV4BU>mpKdQ0MRcPP`nRssDw}-QEkx>&R_S#SD%@+-_|}x!DD`b9D6} z4f2~Cu# zP>|%a$gZ5shZ9kWs1DT4N;2p60)k>!FF~$m^)t8A-(p%bRu((|a)Mg_+~6j-Gji$j zG`nXsemi%EaH)7belL7=qvL5=jFMAY((yjFgL~yjemEeJbIcX@)Hvox&`n$=TZoRF zQH~zx`zDFt1gJIHc$>Md4=FEJY6-jO3J%!ND`~+>-S>&OZpsl84*bj0%2B9U z`#vsas%cvqau&Uf@{Pws*u|k++gmQqjAr~>5I0DKB%pG%IDLPK9Wo+kymS}V5;`~- z9gUkV)i0mMog+)Sn7Zitn(*tZ0aY?S>;m35_4WRPnp#ZhxeU+xgxdV7VHSv$I9#Bi;1=lYCtJ z$48_PH*5a#nC;yfW9Kyyqwd$v^1r)=kwg+kp;!9jWNEA~GmArr8|`Yk4T|-PC!w{` zlG9uE4>OrKr&}>*{4V)o^S`oHNbjL<&pgy!jjQ#?8-jwo1jZ++$rt`E7VuS~NPP(L z{VOlUmadwQ8y^uNGBG9uv14_Xq2&@cIhzmY*{dmi35B4Ip9VS{QqSo_99p&w8wyOp zb42@@+aDjxS5Lap$uZ?_+iP|^!kG^&%oO#3?^Ny%54Ci6H{~2j%!wq*a7IKJ#PKv= z3+y6V1~KvY-Q@qyM14M-#7=NvBfH0B6o9yI6MI&fo<~+`PNn6-;w%2`!nuGP|pxB;Ci>$I0hZiJM=IdsjYeuFa?QXp`g&r)e;HsPtRz zaE(_s|1Rl&P0D4ebGCH)-e0BID>%ExZgOJJWWD2{_hSwo@Twbm0%tTVAL7xen{Uko zj69ffN4ums2pSDpF=XX;C-L)OI&6H?(|I}8Ju|+uw4jb+CWY9<@IEYgSIF%Y$rN-B zx-LGBrGDtGb>i4F;VqwtE28#yzcTdNQU0jXU7Nit1Paoq{nK8+)4U=HS)mlUp?xRvvkSAS zY>cUqeGbt-*bLu=%I>L;@c2C@eSYD0Y=JIk4m^b$IZgi!`8IBA2w$S6fM_VHO6_jF zL-L0p#LZ07d+ScU;o=1qi;u6zxihJ~$-en@g+AoanflkfA8AJ>C605UUN{oCutw(6 z2i^QR52IEu!BLqeY5NyfW9zvv<;L}{^4K&Aj~`|&n1*?}zgpnN^QoDUH}`Bn zGEI_5?LSii-gQ>dQsEo zc7?~UTujP)zb_))Cv9f&z4B%A914erfz0)Xl@vzFO(Bf}hbKvX4&X-K{@{T^1{HD05m!iF2b;-16`zAbiNfyPJeJ-}& z?wRs&MQxh=XGf;<3x&&FxYP8V>?L{6hKE~=$023-A0`jhKA!4?_q8h(^v$Z&Jjc;k z@qd?W8Yd~pz)KQmo~%VYZuPQ%dDybCWcF~+7Qe||Cj6G5x()Jh9|9+Ao=7&H?<|C4 zWp0OFH18SmJdgiV=DzA+dM9y-yJYI%y-HzH=G)M=SK;2=#_H2BV%qFoh<3O=WFzd} zlF0KR3~PNe)UlZe<+9?}Mv}X$lF1mWkIjT>n)morM%UX!kp&#RH*yQkbUxRMeONw* zZ%oPr8v=I0#We#w&IwpF8)o2_{yR$njqmmjXChn9R(@$L8Y_}zO~1795#8<1X$FfN zc2{6w?$b&)qt)@?B*s-ARg5=*ZQuO-)>>$#;v;&*Bhkd>&4XGUx3dO{#EQFlO!iyz zrV8>po|7nb%Qjtj+y9k)ukCM5=7K42gXdPx%7c{qr?93d2v0>_oYIC+h)FQE℘4 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